Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Framework] ODFAEG

Pour la création de jeux vidéos

    28 mai 2021 à 21:20:30

    Bonjour je posterai au fur et à mesure des tutoriels vidéos sur l'utilisation du framework ODFAEG!

    https://www.youtube.com/watch?v=olvJm9cY9Ds&t=3s

    ODFAEG n'est pas vraiment un moteur de jeux comme Unity 3D, parce que ODFAEG ne génère pas de code automatiquement avec une interface graphique tout simplement parce que je n'ai pas besoin de faire ça et je n'aime pas ce système.

    Néanmoins à l'avenir ODFAEG permettra de créer autre chose que des jeux mais utilisera toujours une api graphique tel qu'opengl pour l'affichage. (J'utilise d'ailleurs ODFAEG pour créer l'éditeur de niveaux)

    Un éditeur de cartes et d'objets faisant partie du gameplay sera quand même fourni.

    I suffira juste de mettre les classes c++ dans le dossier Script du projets de l'éditeur pour créer les objets avec l'éditeur, ainsi que les images.

    Et ensuite de copier les fichiers générés par ODFAEGCreator dans votre projet c++ pour les charger dans le jeux.

    Je vous montrerez ça dans une vidéo quand j'aurai terminé de finalisé l'éditeur parce que il y a encore quelques petites choses à mettre au point.

    -
    Edité par OmbreNoire 28 mai 2021 à 21:26:31

    • Partager sur Facebook
    • Partager sur Twitter
      31 mai 2021 à 0:03:02

      Salut!

      Je suis entrain de remplacer tout les pointeurs nus par des pointeurs intelligents car je me suis rendu compte que à un endroit dans l'éditeur de niveau j'ai besoin de connaître le nombre de fois ou le pointeur est utilisé avant de le détruire (donc je dois tout changer en std::shared_ptr) sinon j'ai un crash et les pointeurs nus ne sont pas évident à gérer.

      • Partager sur Facebook
      • Partager sur Twitter
        2 juin 2021 à 12:04:27

        J'ai tenté une autre implémentation de la classe FastDelegate qu'on m'a proposé dans le but d'éliminer la macro (pour les placeholders) et l'héritage malheureusement ça ne fonctionne pas dans 100% des cas, parfois ça plante et je ne sais pas pourquoi hors que en faisant des tests simple dans la fonction main ça ne plante pas!!!

        Enfin bref.

        En attendant voici la suite des tutoriel vidéos. (tutoriel n°2)

        https://www.youtube.com/watch?v=6aK6glnAp-U

        • Partager sur Facebook
        • Partager sur Twitter
          4 juin 2021 à 0:28:19

          Bon je poste sur git mon essai de la nouvelle implémentation même si elle plante peut être que quelqu'un trouvera la solution parce que là je sèche! Les données ne sont pas bien stockées dans le tuple en tout cas je me retrouve avec des pointeurs null dans le tuple! (Et ça ne me le fait pas tout le temps)

          Je trouvais la nouvelle implémentation meilleure et j'espérais qu'elle soit plus rapide. Mais pas moyen je ne comprend pas pourquoi j'ai des valeurs null (notamment lorsque je passe le pointeur this) lorsque je passe les pointeurs dans le tuple.

          • Partager sur Facebook
          • Partager sur Twitter
            9 juin 2021 à 20:26:20

            Salut!

            Je sais bien qu'il y a plusieurs choses à faire, au niveau du code, comme :

            -Remplacer les dynamic_cast, par des méthodes virtuelles.

            -Mieux séparer le code. (remplacer les fonctions trop grandes par de plus petites fonctions, séparer certains fichiers .h en .h, .cpp et .inl)

            -Rajouter des commentaires là ou il n'y en a pas.

            -Fonctions constantes.

            -Nettoyer le dépôt git.

            Mais je n'ai pas vraiment le temps de faire ces choses là, j'avoue j'ai codé un peu à l'arrache dans le but de faire des tests avant de mettre ça au propre pour modifier moins de choses et je n'ai pas vraiment le temps de mettre ça au propre maintenant je dois terminer de coder le jeux, tant que ça fonctionne ça va et que je m'y retrouve, ça va, maintenant mon but c'est de essayer de me faire connaître pour trouver un travail ou mieux, parvenir à générer mes propres revenus et à être indépendant, histoire que, le temps passé sur ce projet, ne soit pas du temps perdu, et je ne pense pas que un framework me permettra d'être connu et de trouver du travail et tout le reste qui s'en suit parce que, il en existe des gratuits, plus complets sûrement et sans doute mieux conçu. 

            • Partager sur Facebook
            • Partager sur Twitter
              10 juin 2021 à 14:46:33

              Salut,

              "je ne pense pas que un framework me permettra d'être connu et de trouver du travail"

              Je ne suis pas totalement d'accord avec ça. Si tu te pointe en entretient qu'on te demande de montrer des projets, c'est typiquement le genre de chose à montrer ! C'est pas parcequ'il en existe déjà que tu va te faire juger, bien au contraire, ils peuvent regarder ce que tu as réalisé et identifier ton niveau.

              Pour la propreté, le mieux est que tu prennes le temps. Coder un jeu sur une base pas stable et pas propre, c'est avoir des problèmes au milieu du développement, des emmerdes et c'est ne pas respecter les potentiels joueurs.

              Ce n'est que mon points de vu, mais si tu fais ça uniquement pour être connus et pour gagner de l'argent, c'est dommage.

              bon courage 

              • Partager sur Facebook
              • Partager sur Twitter
                10 juin 2021 à 17:19:30

                Ok, je vais voir ce que je peux faire pour améliorer la propreté du code pour partir sur une bonne base.
                • Partager sur Facebook
                • Partager sur Twitter
                  11 juin 2021 à 8:10:13

                  Encore une fois tu te disperses de trop, si on prend tes derniers messages (depuis même pas 2 semaines):

                  - remplacement new par smart pointers: crash

                  - fast delegate: parfois plante, tu sais pas pourquoi

                  - nouvelle implémentation (de?): tu comprend pas pourquoi tu as des null

                  - pas le temps de nettoyer le code: tu vas voir pour améliorer le code

                  Clairement, tu n'as aucune ligne directrice, tu es dépassé par le projet et tu stagnes, pour commencer tu devrais:

                  1) Définir ton but: apprentissage?, être connu?, trouver un job?, vivre de tes développement? si plusieurs, les mettre par ordre d'importance.

                  2) Définir ce qui est nécessaire pour atteindre ce but.

                  3) Définir les étapes précisément, est-ce que tu peux les faire seul, est-ce que tu as besoin d'aide.

                  4) Te focaliser uniquement sur le point en cours et ne pas te disperser tant qu'il n'est pas complété.

                  • Partager sur Facebook
                  • Partager sur Twitter
                    17 juin 2021 à 23:09:57

                    P.X.L a écrit:

                    Encore une fois tu te disperses de trop, si on prend tes derniers messages (depuis même pas 2 semaines):

                    - remplacement new par smart pointers: crash

                    - fast delegate: parfois plante, tu sais pas pourquoi

                    - nouvelle implémentation (de?): tu comprend pas pourquoi tu as des null

                    - pas le temps de nettoyer le code: tu vas voir pour améliorer le code

                    Clairement, tu n'as aucune ligne directrice, tu es dépassé par le projet et tu stagnes, pour commencer tu devrais:

                    1) Définir ton but: apprentissage?, être connu?, trouver un job?, vivre de tes développement? si plusieurs, les mettre par ordre d'importance.

                    2) Définir ce qui est nécessaire pour atteindre ce but.

                    3) Définir les étapes précisément, est-ce que tu peux les faire seul, est-ce que tu as besoin d'aide.

                    4) Te focaliser uniquement sur le point en cours et ne pas te disperser tant qu'il n'est pas complété.


                    Pour améliorer la propreté du code et optimiser le code je ne pourrai pas le faire seul car ce n'est pas mon point fort.

                    Tout ce qui est remplacement std::unique_ptr par std::shared_ptr j'ai laissé tombé à cause de shared_from_this et l'héritage qui lance une exception (bad weak ptr) et je n'ai pas besoin de std::shared_ptr je n'ai qu'un seul propriétaire et je n'aime pas utiliser des std::shared_ptr donc je les évite le plus possible.

                    Et la nouvelle implémentation de fast delegate qui est un soucis du compilateur parce que l'implémentation est bonne, je la trouve même meilleure que l'ancienne implémentation, mais comme l'ancienne implémentation ne plante pas je n'ai pas besoin de la nouvelle donc ces deux points là ce ne sont clairement pas des priorités.

                    Maintenant il faut que je me fixe un objectif (finir le jeux ? Améliorer/Optimiser le code du moteur ?) j'avoue que je n'ai pas su me fixer un objectif précis et que je travaille sur un point, puis un autre, juste dans le but de faire des tests, de me dire tient si j'essaie ça est ce que ça va fonctionner ou pas ? 

                    Et quand je ne trouve pas la solution tout de suite, je passe à autre chose ou je reviens à l'ancienne solution et je continue même si l'objectif n'est pas complété parce que sinon, je perd du temps et en travaillant seul, on ne s'en sort pas sinon.

                    Parce que si je me fixe sur un seul objectif, déjà, c'est risqué et si j'échoue, ce n'est pas bon.

                    • Partager sur Facebook
                    • Partager sur Twitter
                      18 juin 2021 à 10:30:28

                      C'est un (presque) monologue entre un développeur et sa conscience, il faudrait en faire un livre "Les chroniques d'un dev". 

                      Plus sérieusement, 1 an que je te lis, j'ai jamais eu l'impression de te voir avancer ou pire, que tu prennes du plaisir à réaliser ton projet... c'est très brouillon. 

                      Je salue cependant ton courage à continuer

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Ancien étudiant OpenClassroom, diplômé en développeur d'application Python

                        18 juin 2021 à 14:15:04

                        Mcflan_7 a écrit:

                        C'est un (presque) monologue entre un développeur et sa conscience, il faudrait en faire un livre "Les chroniques d'un dev". 

                        Plus sérieusement, 1 an que je te lis, j'ai jamais eu l'impression de te voir avancer ou pire, que tu prennes du plaisir à réaliser ton projet... c'est très brouillon. 

                        Je salue cependant ton courage à continuer


                        Tout à fait d'accord. Il y a clairement quelque chose qui m'échappe : ce n'est clairement pas un projet pro. C'est donc un side-project, alors pourquoi se faire du mal dessus ? Aucune obligation de réussite qui plus est donc pourquoi se mettre autant de pression ?
                        • Partager sur Facebook
                        • Partager sur Twitter
                          18 juin 2021 à 15:00:20

                          Par objectif, je parlais d'objectif de vie, pas de projet, un projet est sensé servir un objectif de vie, pas l'inverse, ce projet tu le fais pour quoi au final?

                          pour trouver un job? être connu/reconnu? en vivre? apprendre?

                          Suivant la réponse, les priorités ne sont pas les mêmes, ces objectifs peuvent changer au fil de l'évolution dans la vie, et donc de même pour les priorités, mais il doit toujours en avoir un objectif principale et d'autres secondaires/facultatifs.

                          Une fois que tu a ça, pour commencer tu vois si ton projet colle avec ton objectif (est-ce qu'il te permettra de trouver un job? être connu, gagner de l'argent, apprendre de nouvelles choses?) si ce n'est pas le cas, mieux vaut trouver autre chose à faire.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            18 juin 2021 à 20:18:31

                            P.X.L a écrit:

                            Par objectif, je parlais d'objectif de vie, pas de projet, un projet est sensé servir un objectif de vie, pas l'inverse, ce projet tu le fais pour quoi au final?

                            pour trouver un job? être connu/reconnu? en vivre? apprendre?

                            Suivant la réponse, les priorités ne sont pas les mêmes, ces objectifs peuvent changer au fil de l'évolution dans la vie, et donc de même pour les priorités, mais il doit toujours en avoir un objectif principale et d'autres secondaires/facultatifs.

                            Une fois que tu a ça, pour commencer tu vois si ton projet colle avec ton objectif (est-ce qu'il te permettra de trouver un job? être connu, gagner de l'argent, apprendre de nouvelles choses?) si ce n'est pas le cas, mieux vaut trouver autre chose à faire.

                            Aucun de ses objectifs là ne sera atteint je pense à part pour apprendre, je connais très bien la communauté dans laquelle on vit, les gens préfèrent critiquer un projet plutôt que d'y apporter un réel soutiens, juste parce que c'est plus facile. Par contre pour gratter ça tout les jours on est emmerdé!!!

                            Mais je n'ai pas envie d'abandonné un projet, juste à cause de la communauté et je suis sûr que si je change de projet, le résultat sera le même!!!



                            • Partager sur Facebook
                            • Partager sur Twitter
                              19 juin 2021 à 10:26:03

                              Admettons un instant que toute critique soit toxique et ne puisse pas servir à corriger ses erreurs, et que donc toute personne n'encensant pas tes choix soit malveillante, quel rapport avec le fait de réaliser tes projets? En quoi la communauté devrait t'aider à apprendre, trouver un job? à gagner de l'argent avec ton app, comment font les autres? Et surtout, si la communauté est si mauvaise, pourquoi tu perds ton temps avec?

                              Et si donc ton seul objectif atteignable est d'apprendre, et pas d'arriver à un résultat, tu ne crois pas qu'il y a des tas d'autres technos bien plus intéressantes? et que faire des proofs of concept ne te permettrait pas de toucher bien d'autres aspects plutôt que stagner sur les mêmes problématiques encore et encore?

                              Bref, les choix que tu fais semblent particulièrement paradoxaux par rapport à ton discours.

                              • Partager sur Facebook
                              • Partager sur Twitter
                                19 juin 2021 à 11:12:02

                                Pour le coup j'ai encensé le sujet de P.X.L car son projet est bien ficelé et on a quelqu'un qui est droit dans ses bottes.

                                Je pense que le discours victimaire n'est pas un avantage pour toi ni pour ceux qui te lisent, ça nous donne une mauvaise image de la personne derrière le projet.

                                Il faut aussi préciser que sur 9 pages tu es presque le seul à poster... pour une fois qu'on apporte un peu de critique ! 

                                • Partager sur Facebook
                                • Partager sur Twitter

                                Ancien étudiant OpenClassroom, diplômé en développeur d'application Python

                                  19 juin 2021 à 15:07:42

                                  Bon, afin que le code paraisse plus pro, je me suis dis que ça serait bien que je le rende plus propre, parce que c'est dommage que mon framework permette de faire des choses complexes (réflexibilité, fonctions de callback avec placeholders, gestion de la transparence peu importe l'ordre de rendu, rendu de lumières/ombres/réfraction, sérialisation d'objets avec archives template comme boost que j'ai du réécrire parce que avec boost j'avais eu un soucis, etc...) et que l'on dise que ce n'est pas un projet pro.

                                  J'ai commencé par retiré les macros dans la classe fastDelegate je ne dois plus donc définir un nombre maximum de placeholders et utiliser cette récursion de macros qui n'était vraiment pas propre.

                                  Je suis entrain de réfléchir donc à comment nettoyer le code, je vais aussi voir comment nettoyer les fichiers cmake.

                                  -
                                  Edité par OmbreNoire 19 juin 2021 à 15:10:35

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    19 juin 2021 à 16:33:51

                                    Tu mélanges plein de choses, un projet pro, c'est un projet fait dans le cadre d'une activité professionnelle, ça n'a rien à voir avec sa complexité.

                                    Un projet fait sur son temps libre est un projet amateur, ce n'est pas péjoratif.

                                    Et encore un fois, ton discours est l'exact opposé de tes agissements, tu te plains du comportement de la communauté, et lorsqu'on a une intention bienveillante, tu ignores les questions et les pistes d'amélioration qu'on te propose pour repartir sur ton monologue.

                                    Autant te faire un blog ou un journal intime, je pense qu'on a tous mieux à faire que d'écrire dans le vide.

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      20 juin 2021 à 14:59:11

                                      En parlant d'amélioration j'ai décidé d'améliorer le code source interne du framework et nettoyer le dépôt git et les fichiers cmake : il n'y aura pas d'ajout de nouvelles fonctionnalités pendant un long moment parce qu'il me faut une base solide.

                                      Je vais commencer par réduire la classe Entity, elle ne possèdera que trois variables et le reste seront des méthodes virtuelles : (Un id, un type et un state pour ajouter/modifier/supprimer des états pour enregistrer des états pour les menus par exemple pour ne pas devoir recharger le fichier de configuration et réaffecter le texte aux entités graphique à chaque fois que l'on ouvre le menu d'un jeux) et se sera la classe de base de toutes les entités dessinable et transformable. Auparavant seul les entités "ODFAEG" pouvaient être ajoutées à la scène, les entités SFML like n'héritant pas de la classe Entity elles ne pouvaient pas être ajouté à la scène, et ne pouvaient pas non plus posséder d'états, hors les menus sont dessinés avec des entités SFML like, j'ai donc besoin de revoir le design de ce côté là.

                                      Je vais également récupérer toutes les entités dessinable du monde, tester si elles sont sélectionnées et si oui utiliser le stencil buffer pour afficher un contour, je le fait déjà dans ODFAEGCreator pour le moment mais je ne pense pas que ça soit une bonne idée d'utiliser opengl en dehors du framework. (Sauf si le développeur veut créer ses propres types de composants de rendus  par exemple alors là il n'y a pas vraiment le choix)

                                      Je vais aussi supprimer les interfaces du style AnimatedEntity et n'utiliser qu'une seule interface (Entity) pour toutes les sorts d'entités (Animées, Lumières, ...) parce que le problème c'est que je ne peux stocké qu'un seul type d'entité dans la grille et si je veux accéder au méthodes d'une sous interface je dois faire un dynamic_cast ou un static_cast dans la grille ce qui n'est pas propre. A la place je vais faire des méthodes virtuelles dans la classe Entity, qui ne feront rien ou renverrons des valeurs invalides dans le cas ou on essaye de récupérer la frame courante d'une entité qui n'est pas une animation.

                                      Il y aura aussi une nouvelle classe, la classe GameObject! Celle-ci permettra de dessiner des entités dessinable complexes avec par exemple, une entité parent et des entités enfants. Chaque GameObject pourra posséder un volume de collision bref c'est la même classe que la classe Entity actuelle.

                                      Dans la classe animation au lieu de créer une entité de type E_MESH qui de plus n'est pas de type E_MESH, pour ensuite effectuer l'interpolation des sommets, je vais cloner la frame courante et ensuite modifier les sommets. (Le problème c'est que E_MESH est une entité contenant un maillage quelconque c'est pas une forme bien précise c'est par exemple un modèle 3D, mais je peux vouloir récupérer certaines informations sur l'entité de la frame courante qui ne sont pas dans la classe Mesh) bref après réflexion cette manière de faire n'est pas propre.

                                      Certaines méthodes comme la méthode draw vont passer const, parce que, ce n'est pas propre de modifier des objets avant de les dessiner, mieux veut le faire dans les méthodes onMove, onScale (bref lorsque je modifie la géométrie d'une entité par exemple) certains paramètres de fonctions vont aussi passer const comme dans les méthodes onMove(math::Vec3f& t) la paramètre t va passer const.

                                      Dans la démo, plutôt que d'utiliser un std::string pour passer les types des entités à charger sur les composants de rendu, je vais utiliser une énum que je vais "stringifier" avec une macro, comme ça, si je me trompe dans le nom du type, ça plantera en compilation et plus à l'exécution.

                                      Lorsque j'utilise une autre lib, le problème si j'utilise un #define et que je recompile, sachant que la compilation du framework ODFAEG commence à devenir assez longue, ça prend du temps, c'est alors que quelqu'un avait rejoins le projet (pas sur ce site mais sur un autre site (développez.com)) dans le but de "fusionner" nos deux moteurs et il avait eu l'idée de charger des fichiers .dll (ou .so sur linux) des librairies et d'appeler des pointeurs de fonctions sur les fonctions de ses libs pour ne pas devoir recompiler. (Nekara)

                                      Mais pour pouvoir faire cela avec le module fenêtre de ODFAEG il faut que je puisse compiler ODFAEG en shared, chose que je ne suis pas parvenu à faire jusqu'à présent à cause d'erreurs lors de l'éditions de liens et il faut aussi savoir ou seront installée les fichiers.dll ou .so des différentes libs.

                                      Pour le reste, je pense que c'est ok à part un renommage de certaines classes, faire une classe Vec3 et Vec4 au lieu de juste une classe Vec3f, et faire certaines interfaces (si je fais une classe template avec un typedef et des #define pour chaque implémentation, le problème est qu'il faut recompiler si je veux changer d'implémentation) donc je pense qu'utiliser des interfaces et charger des .dll ou .so est mieux.

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        23 juin 2021 à 19:00:19

                                        Petite mise à jour, j'ai commencé à retirer les dynamic_cast dans la classe Map (la scène), je vais faire de même pour les composants dans RenderComponentManager.

                                        Une fois ça de fais j'apporterai la const correctness, je séparerai mieux le code et j'enlèverai le code que je n'utilise plus et enfin je rajouterai des commentaires.

                                        Le driver graphique doit supporter la dernière version d'opengl car j'utilise les "bindless textures" pour que la démo (ODFAEGDemo) fonctionne.

                                        -
                                        Edité par OmbreNoire 23 juin 2021 à 19:00:55

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          26 juin 2021 à 13:01:35

                                          Bonjour, j'ai retirer encore des dynamic_cast je n'en ai laissé que pour le "type erasure" (genre dans la structure any ou encore dans le delgate) ou là je n'ai pas trop le choix parce que les fonctions sont template donc je ne peux pas utiliser de fonctions virtuelles.

                                          Mais partout ailleurs j'ai retiré les dynamic_cast c'est à dire pour les GameObject et les Composants.

                                          Je n'avance pas de trop parce que je ne suis plus célib depuis un an mais je compte utiliser le maximum de mon temps libre pour finir de nettoyer le code ainsi que le dépôt git et les fichiers cmake.

                                          Je dois encore :

                                          -Enlever les fonctions que je n'utilise plus ainsi que les derniers std::cout que j'avais utiliser pour débuguer, mieux séparer le code, la "const correctness" et commenter ainsi que nettoyer le dépôt git et les fichiers cmake. (je vais faire une seule branche pour windows et linux)

                                          Et enfin sortira la première version du framework!

                                          Dans les versions suivantes se sera juste de l'ajout de fonctionnalités et amélioration des rendus (bump mapping, composante spéculaire, etc...), j'avais déjà commencé à implémenté cela mais je n'ai pas terminé.

                                          Il faut dire j'avais codé un peu à l'arrache le temps de tester les algorithmes et attendre d'avoir quelque chose qui fonctionne avant de mettre tout ça au propre pour ne pas avoir à modifier à chaque fois le code mit au propre, les commentaires, les consts, etc..., bref pour moi l'optimisation ça se fait, à la fin.

                                          -
                                          Edité par OmbreNoire 26 juin 2021 à 13:02:08

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            30 juin 2021 à 22:14:10

                                            J'ai remit à jour le dépôt git parce que il ne s'était pas remit à jour à cause des fichiers build de cmake je les ai donc retiré du répo, il faut faire attention lors de la sérialisation des "entité" de toujours sérialiser à partir de l'entité racine sinon ça va planter. (Il n'y a pas moyen de faire autrement)

                                            J'en ai un petit peu marre de nettoyer le code alors je ne vais pas faire ça tout d'un coup, je vais essayer de créer une map avec ODFAEGCreator en parallèle et des objets du gameplay pour les charger dans le jeux, pour voir si ça fonctionne. Il ne me reste plus que ça à tester niveau programmation. 

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              2 juillet 2021 à 13:07:41

                                              Salut! J'ai décidé d'utiliser CRTP pour supprimer toutes les fonctions virtuelles car je pense que c'est ça qui fait que j'ai un FPS assez bas même avec une carte graphique récente mais je ne sais pas si ça va accélérer le FPS au rendu on verra parce que ce sont surtout les appels à glDraw qui consomment et pourtant j'en fais le moins possible (je n'en fait que un, par type de primitive bien que je m'arrange pour que ça soit toujours le même, et par texture de rendu, ce qui me fait à un près, une dizaines d'appels à glDraw pour les rendus complexes avec réflexion/réfraction, lumières, ombres et order independant transparency.

                                              J'ai enfin trouvé un moyen d'utiliser un type de base pour stocker toutes les entités dans mes std::vector sans dupliquer les données et le code pour les comportements identiques avec du polymorphisme statique tout en ne connaissant pas les types que le développeur du framework va utiliser.

                                              Le seul bémol c'est que les instances de mes std::vector sont dupliquée à cause de la récursion avec les templates variadique je dois donc faire attention à ne pas appeler les fonctions plusieurs fois :

                                              J'ai donc rajouté le nombre de types en paramètre template parce que il me semble que le même objet est ajouté plusieurs fois et comme les std::vector sont dupliqués la méthode contains ne fonctionne pas, ça sera plus performant mais ça prendra un peu plus de mémoire je pense enfin bref, souvent pour augmenter la performance il faut augmenter un peu la mémoire.

                                              struct animal {
                                                  unsigned int id;
                                                  static unsigned int nbInstances;
                                                  animal() {
                                                      id = nbInstances;
                                                      nbInstances = id;
                                                      nbInstances++;
                                                  }
                                                  template <typename D>
                                                  void applySpecificBehaviour() {
                                                    std::cout<<"base beheviour of instance : "<<id<<std::endl;
                                                    onSepecifiedBehaviourCalled<D>();
                                                  }
                                                  template <typename D>
                                                  void onSepecifiedBehaviourCalled() {
                                                      static_cast<D&>(*this).onSepecifiedBehaviourCalled();
                                                  }
                                              };
                                              unsigned int animal::nbInstances = 0;
                                              struct cat : animal
                                              {
                                                void applySpecificBehaviour() {
                                                    animal::applySpecificBehaviour<cat>();
                                                    std::cout<<"cat beheviour of instance : "<<id<<std::endl;
                                                }
                                                void onSepecifiedBehaviourCalled() {
                                                    std::cout<<"cat on specified behaviour : "<<id<<std::endl;
                                                }
                                              };
                                              
                                              struct dog : animal
                                              {
                                                void applySpecificBehaviour() {
                                                    animal::applySpecificBehaviour<dog>();
                                                    std::cout<<"dog beheviour of instance : "<<id<<std::endl;
                                                }
                                                void onSepecifiedBehaviourCalled() {
                                                    std::cout<<"dog on specified behaviour : "<<id<<std::endl;
                                                }
                                              };
                                              
                                              struct bird : animal
                                              {
                                                void applySpecificBehaviour() {
                                                    animal::applySpecificBehaviour<bird>();
                                                    std::cout<<"bird beheviour of instance : "<<id<<std::endl;
                                                }
                                                void onSepecifiedBehaviourCalled() {
                                                    std::cout<<"bird on specified behaviour : "<<id<<std::endl;
                                                }
                                              };
                                              template <size_t N, class B, size_t I, class... Derived>
                                              struct Holder {
                                              
                                              };
                                              template <size_t N, class B, size_t I, class Head, class... Tail>
                                              struct Holder<N, B, I, Head, Tail...> : Holder<N, B, I+1, Tail...> {
                                                  std::vector<B*> entities;
                                                  void add(B* entity) {
                                                      entities.push_back(entity);
                                                      Holder<N, B, I+1, Tail...>::add(entity);
                                                  }
                                                  void applyBehaviours() {
                                                      for (unsigned int i = 0; i < entities.size() / N; i++) {
                                                          static_cast<Head*>(entities[i*N+I])->applySpecificBehaviour();
                                                      }
                                                      Holder<N, B, I+1, Tail...>::applyBehaviours();
                                                  }
                                              };
                                              template <size_t N, class B, size_t I, class Head>
                                              struct Holder<N, B, I, Head> {
                                                  std::vector<B*> entities;
                                                  void add(B* entity) {
                                                      entities.push_back(entity);
                                                  }
                                                  void applyBehaviours() {
                                                      for (unsigned int i = 0; i < entities.size() / N; i++) {
                                                          static_cast<Head*>(entities[i*N+I])->applySpecificBehaviour();
                                                      }
                                                  }
                                              };
                                              int main(int argc, char* argv[])
                                              {
                                                  /*using tuple_t = sort<LessPlaceceholder, unique<copy_if<is_placeholder,std::tuple<placeholder<3, int>, int, placeholder<2, int>, float, placeholder<1, int>, char, placeholder<0, int>>>::f/*>::f>::f;*/
                                                  /*using late_params_t = lift<tuple_t, LateParameters,std::make_index_sequence<std::tuple_size<tuple_t>()-0>>::f;
                                                  tuple_t tp = std::make_tuple(placeholder<3, int>(),placeholder<2, int>(),placeholder<1, int>(),placeholder<0, int>());
                                                  return 0;*/
                                                  cat c1;
                                                  dog d1;
                                                  bird b1;
                                                  cat c2;
                                                  dog d2;
                                                  bird b2;
                                                  Holder<3, animal, 0, cat, dog, bird> holder;
                                                  holder.add(&c1);
                                                  holder.add(&d1);
                                                  holder.add(&b1);
                                                  holder.add(&c2);
                                                  holder.add(&d2);
                                                  holder.add(&b2);
                                                  holder.applyBehaviours();
                                                  return 0;
                                              }


                                              Mais je commence vraiment à aimer CRTP.

                                              -
                                              Edité par OmbreNoire 2 juillet 2021 à 13:13:56

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                2 juillet 2021 à 15:51:11

                                                Qui va piano va sano.
                                                • Partager sur Facebook
                                                • Partager sur Twitter

                                                Ancien étudiant OpenClassroom, diplômé en développeur d'application Python

                                                  3 juillet 2021 à 17:17:38

                                                  Ok j'ai utilisé un benchmark pour comparer les performances, CRTP est 3.9 fois plus rapide :

                                                  https://quick-bench.com/q/c5l442vA4CoGi_PBCFP2ibm0wts 

                                                  cependant je ne peux pas l'utiliser partout, entre autre pour les delegates génériques (que j'utilise pour la gestion des signaux et des slots), car le nombre de types possibles pour les pointeurs sur fonctions est illimité ça me prendrait trop de temps de tous les définir en compilation, même chose pour les types des placeholders qui sont illimités.

                                                  J'avais tenté une approche avec des void* pour ce cas là, mais, ça plante dans certains cas j'ai une undefined behaviour je pense que c'est parce que memcpy produit des undefineds behaviours lors de la copie de bytes d'objets non "trivially copiable". Bref c'est le seul cas ou je ne pourrai pas utiliser CRTP.

                                                  J'ai essayé un code pour fournir des types par défaut au conteneur (la scène) pour les entités du framework et fournir des types supplémentaires pour les entités du jeux et ça à l'air de plutôt bien fonctionner!!!

                                                  La manière de créer les objets du framework va donc changer, si l'utilisateur utilise des types d'entités supplémentaires, il devra les fournir en paramètre template à la scène, ça règle pas mal de problème également par exemple si un noeud ne peux contenir qu'une liste de type d'entités enfants, ou si la scène ne peut contenir qu'un certains type d'entités, je crois que ça va régler pas mal de problèmes en compilation.

                                                  Il faudra par contre fournir la position du template dans le parameter pack de la scène lors de l'appel du constructeur de la classe Entité dans la classe Dérivée, ça c'est obligé, et oui, on ne peut pas convertir un type chat en type chien ni ajouter des types chiens dans un std::vector de type chat c'est pour cela que j'ai dû utiliser l'héritage mais ce n'est pas grave ici car je n'ai pas le surcoût de l'appels de fonctions virtuelles à l'exécution tout en ne dupliquant pas les données et comportements identiques grâce à l'héritage, bref, je trouve que cette solution est vraiment bien, je vais de ce pas, l'implémenter dans le framework.

                                                  //Cas ganéral, je déclare juste la structure.
                                                  template <class B, size_t I, class... Derived>
                                                  struct Holder {
                                                  
                                                  };
                                                  //Spécialisation pour les cas intermédiaires. 
                                                  template <class B, size_t I, class Head, class... Tail>
                                                  struct Holder<B, I, Head, Tail...> : Holder<B, I+1, Tail...> {
                                                      std::vector<std::reference_wrapper<B>> entities;
                                                      void add(std::reference_wrapper<B> entity) {
                                                          //On recherche le type dérivé de l'entité suivant sa position dans le parameter pack et on l'ajoute dans le vecteur de la bonne structure.
                                                          if (entity.get().typeId == I)
                                                              entities.push_back(entity);
                                                          Holder<B, I+1, Tail...>::add(entity);
                                                      }
                                                      void applyBehaviours() {
                                                          //On parcourt le vecteur pour appeler la fonction pour toutes les entités de type Head.
                                                          for (unsigned int i = 0; i < entities.size(); i++) {
                                                              static_cast<Head&>(entities[i].get()).applySpecificBehaviour(0);
                                                          }
                                                          Holder<B, I+1, Tail...>::applyBehaviours();
                                                      }
                                                  };
                                                  //Spécialisation pour le cas final.
                                                  template <class B, size_t I, class Head>
                                                  struct Holder<B, I, Head> {    
                                                      std::vector<std::reference_wrapper<B>> entities;
                                                      //Ajout si le type de l'entité c'est le dernier type et arrêt de la récursion.
                                                      void add(std::reference_wrapper<B> entity) {
                                                          if (entity.get().typeId == I)
                                                              entities.push_back(entity);
                                                      }
                                                      //Arrête de larécursion, on parcourt le vecteur pour appeler la fonction pour toutes les entités de type Head.
                                                      void applyBehaviours() {
                                                          for (unsigned int i = 0; i < entities.size(); i++) {
                                                              static_cast<Head&>(entities[i].get()).applySpecificBehaviour(0);
                                                          }
                                                      }
                                                  };
                                                  //Holder avec les types que le framework utilise par défaut + les types inconnus.
                                                  template<typename... Types>
                                                  struct DefaultHolder {
                                                      void add(std::reference_wrapper<animal> a) {
                                                          holder.add(a);
                                                      }
                                                      void applyBehaviours() {
                                                          holder.applyBehaviours();
                                                      }
                                                      //Types d'entités connus du framework + types inconnus. 
                                                      Holder<animal, 0, cat, dog, bird, Types...> holder;
                                                  };

                                                  D'autant plus que j'utilise quand même beaucoup de fonctions virtuelles.

                                                  L'avantage aussi est que si le développeur souhaite développer sa propre classe Entity et l'ajouté à la scène il pourra le faire puisque tout sera "template".

                                                  Bref ça va régler beaucoup de problèmes de conception.

                                                  EDIT, je pense que je vais également faire une factory pour la création des entités, elle comptera le nombre d'entités créées, plutôt qu'utiliser une variable statique qui pourrait poser problème lors de l'utilisation de plugins.

                                                  -
                                                  Edité par OmbreNoire 3 juillet 2021 à 17:20:18

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    10 juillet 2021 à 21:47:55

                                                    Bonjour! Alors j'ai réussi à retirer le fait de devoir spécifier tout les template à une classe attendant un template variadique, grâce à la déduction de types des fonctions template, j'ai également fait un delegate plus "générique" pour ne pas devoir spécifier le type de retour, en paramètre template dès sa création et qui retourne un void* (donc il faudra faire un static_cast sur le bon type) afin d'attacher des pointeurs sur fonctions à une instance (que ça soit des lambdas ou n'importe quel type de fonctions pour une genre de classe anonyme qui n'existe pas en c++)

                                                    J'ai pensé que ça serait bien, le langage de programmation Linotte permet de faire cela avec le verbe attacher,.

                                                    Je vais utiliser  des fonctions templates pour instancier les classes template, cela permettra au développeur de ne pas devoir spécifier tout les types pour les template variadique.

                                                    Je vais également retirer toutes les méthodes virtuelles qui ont un coût à l'exécution et les remplacer par des static_cast, comme les types des conteneurs peuvent changer à l'exécution, il faudra les passer à une fonction (ou un foncteur générique), à chaque fois qu'on les modifie, en effet, le c++ ne permet pas de changer un type pour une variable membre, sinon, il faudrait redimentionner la taille de toutes les instances de la classe et le c++ ne fait pas cela, j'ai donc utilisé un delegate générique, que je peux donc réaffecter avec le nouveau type du conteneur. (le Delegate générique utilise des void* plutôt que l'héritage)

                                                    Je dois maintenant tester cela pour savoir si ça va fonctionner pour les démos et ODFAEGCreator, si je vois que ça pose problème pour un projet ou que ce n'est pas performant j'annulerai le changement. (Mais en ayant fait de benchmark dans le main je remarque que c'est plus performant d'utiliser des templates plutôt que des fonctions virtuelles)

                                                    J'ai préféré utilisé l'héritage plutôt que de devoir rechercher le bon composant pour la bonne entité avec des ids, si les entités sont des nodes, c'est assez galère, et si il faut remettre à jour d'autres composants si un composant est mit à jour. 

                                                    Si j'ai le temps, je créerai un sujet de discutions sur le forum de ODFAEG s'appelant "la coda", j'ai choisi ce nom à cause d'un membre du site (qui m'a fait rire d'ailleurs ou il a dit "on se croirai à la coda" mais en fait c'est exactement ça!!!) et j'expliquerai toutes les techniques moderne que j'utilise pour le bien du framework.

                                                    Chacun pourra donner son avis mais je ne changerai que si je trouve que la technique proposée est meilleure ou réponds mieux à mes besoins.

                                                    Il faut savoir que ODFAEG est à la fois un moteur de jeux (ODFAEGDemo) et un framework (ODFAEGCreator) ou j'utilise des techniques modernes et générique comme la plupart des frameworks le font, il y a donc beaucoup de composants de types différents, et donc, un système ECS classique pourrait être vraiment lourd lorsqu'il y a beaucoup de composants et qu'on ne peux pas hériter de données ou de fonctions, d'une classe de base.

                                                    Bien sûr l'héritage n'est pas utilisé n'importe comment et est utilisé seulement pour les classes qui ne peuvent pas changer de rôle et qui n'ont pas des tonnes de comportements différents surtout lorsqu'elles interagissent entre elles, dans ce cas le pattern visitor conviendrait mieux. (Ces classes sont en génal les classes de dessins, d'animations, de collision, etc...) Pour les autres classes qui ne font pas partie du framework elles hériteront seulement des interfaces adéquates et il n'y en a pas des tonnes (dessin/transformation, animation, bone animation, nouveau type de composant de rendu ou de composant gui ou d'un nouveau timer/worker) et le développeur pourra si il le veut utiliser un système de composant ou l'héritage tout pour les classes faisant partie du jeux ou de l'application. (Mais pour les classes de rendu l'héritage est obligatoire car il y a trop de types de composants différents à gérer)

                                                    C++ nous offre une panoplie d'outils permettant de résoudre un problème et encore plus depuis l'apparition des templates variadiques qui malheureusement n'existaient pas à la création du framework ou alors il fallait utiliser une librairie externe comme boost, mais je ne savais même pas que c'était possible de faire cela, de ce fait,  j'ai utilisé des fonctions virtuelles, cependant, je me suis rendu compte que il y avait peut être moyen de faire mieux niveau optimisation, va donc s'en suivre une batteries de tests pour tenter d'éliminer tout ce qui nuit à la performance. (Je vais commencer par le CPU et ensuite je verrai pour le GPU si je ne peut pas tenter d'utiliser plusieurs threads pour le rendu hors écran pour dessiner simultanément les ombres, les lumières, la réfraction, et les "per pixel linked list" et mettre la fenêtre à jour quand tout les rendus hors écrans seront terminés, ça bouffe quand même pas mal les performances au niveau du GPU les différents appels à glDraw sur les textures de rendus, surtout qu'il y en a quand même pas mal.

                                                    Il faudra également utiliser une factory pour créer les entités et les composants, bref, toutes classe qui possède actuellement des variables statiques globales afin de les éléminet pour de bon, plutôt que de les stocker dans la classe application, je vais les stocker dans une factory.

                                                    Voilà donc beaucoup trop de choses à faire/tester.

                                                    Je n'ai pas le temps d'implémenté Vulkan pour le moment, ni de rendre le code portable pour toutes les plateformes, je suis tout seul sur ce gros projet, je peux pas faire de miracles.

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      16 juillet 2021 à 22:14:58

                                                      J'ai fait plusieurs tests de rapidité :

                                                      -J'ai remarqué qu'un système de type ECS, est moins performant que les fonctions virtuelles à cause du mapping probablement pour les scenes nodes.

                                                      -J'ai aussi remarqué que le polymorphisme d'inclusion est moins rapide que les fonctions virtuelles dans le cas des scenes nodes.

                                                      Donc finalement, je ne pense pas que le design du framework doive changer, par contre je vais voir si je ne peux pas optimiser le temps de rendu, utiliser plusieurs threads, pour éliminer les goulots d'étranglement.

                                                      -
                                                      Edité par OmbreNoire 16 juillet 2021 à 22:15:17

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        21 juillet 2021 à 23:32:05

                                                        Une personne m'a rejoins pour m'aider pour l'implémentation de l'api Vulkan, on verra ce que ça donne.

                                                        Sinon, un nouveau type de conteneur va faire son apparition, se sera, un tuple dynamique de vecteurs!

                                                        J'ai pensé que ça serait bien pour ajouter des composants aux entités. (un composant soigneur par exemple si le personnage peut soigner)

                                                        Il y aura donc un tuple dynamique pour chaque type de composants pour une entité et un autre tuple dynamique qui pourra par exemple, contenir tout les composants pour toutes les entités.

                                                        J'ai remarqué que c'est plus rapide que le mapping d'ids, d'après les tests que j'ai fait. ;)

                                                        Les tuples dynamiques peuvent changer de type, je ne les stockerai donc dans aucune classe, il faudra juste les repasser à la boucle principale de l'application avec une méthode updateComponents à chaque modification du tuple, et grâce aux fonctions redéfinie de la classe Application, le tuple dynamique sera accessible partout.

                                                        Je l'ai déjà dit, mais je vais également rajouter une factory, pour créer les entités, il faudra donc utiliser la factory, les constructeurs seront mit en protected.

                                                        -
                                                        Edité par OmbreNoire 21 juillet 2021 à 23:35:58

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          23 juillet 2021 à 21:39:13

                                                          Finalement j'ai trouvé ce qui cloche au niveau des perfs, ce sont les fonctions virtuelles, avec l'aide de la communauté j'ai enfin trouvé un moyen de faire un système ECS de manière performante avec des noeuds. (Par contre lorsque le système met à jour les données je dois m'arranger pour que, les enfants n'ont qu'un seul parent) On peut voir la différence de performance ici, par rapport aux fonctions virtuelles : https://quick-bench.com/q/jEGRWG62s39A0OzY22E-iNtuvWo.

                                                          Mais je dois refaire tout le design du framework parce que il y a un tas de chose que je ne savais pas auparavant.

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            26 juillet 2021 à 9:21:51

                                                            Bon, j'ai commencé à implémenter le nouveau design et à tester cela à l'aide de la méta-programmation, (fichier main de ODFAEGDemo) et ça à l'air de plutôt bien fonctionner, ça va me simplifier la tâche pour les rendus plus complexe et pour l'implémentation VULKAN également.

                                                            Si le développeur souhaite implémenter son propre render, il ne devra plus hériter d'une classe, mais récupérer l'id du rendererSystem et l'ajouter comme enfant au renderer system. (le renderSystem appellera tout les render) 

                                                            Même chose pour le système qui se chargera de partitionner les données. (BSP-Tree, etc...)

                                                            Le problème que j'avais avec le design actuel c'est si je voulais faire des rendus plus complexe, avec des "sub render" utilisant des buffers d'un autre render ou encore un système de partitionnement qui en utilise un autre, ça aurait été plus compliqué avec de l'héritage et des virtuelles. Le design actuel ne permettait donc plus d'ajouter des nouvelles fonctionnalités au framework. (J'étais en quelque sorte bloqué)

                                                            Je ne trouvais pas comment implémenter toutes les fonctionnalités avec le nouveaux design et puis petit à petit j'ai eu des idées au niveau de la méta-prog je me suis amélioré et voilà, auparavant, je n'avais pas de connaissances suffisantes apparemment sur le design d'un moteur de jeux, j'avais déjà entendu parler de système ECS, mais, j'avais mal compris comment implémenter cela.

                                                            -
                                                            Edité par OmbreNoire 26 juillet 2021 à 9:24:22

                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              28 juillet 2021 à 16:01:30

                                                              J'ai commencé à implémenter le nouveau design, je n'ai pas encore pu tout tester (je n'ai pas testé le clônage d'entités) mais ça devrait être bon.

                                                              Pour la factory j'ai hésité à remettre à jour les ids avec des pointeurs sur des types size_t pour réutiliser les ids des entités supprimées, obligé pour remettre à jour la valeur des ids partout dans le programme (ça nécessite une allocation dynamique donc probablement plus lent lors de la générations d'ids), ou l'autre implémentation utilisant bêtement un size_t  en laissant des trous, ça occupe plus de place en mémoire mais plus rapide.

                                                              Et normalement il y a moyen de parcourir les noeuds de haut en bas ou de bas en haut comme avant, je n'ai plus utilisé la récursivité pour le faire car c'est plus lent qu'une boucle, j'ai donc utiliser des boucles et des std::vector.

                                                              -
                                                              Edité par OmbreNoire 28 juillet 2021 à 16:03:09

                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              [Framework] ODFAEG

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              • Editeur
                                                              • Markdown