IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

conception et programmation ECS


Sujet :

C++

  1. #21
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Ok, merci, je crois que je vois un peu mieu l'idée.

    Si on reprend l'exemple d'un système pour gérer les mouvements : il utilise les composants "Position" et "Velocity". "Position" est utilisé par 2 systèmes ("SystemGraphic" et "SystemMove"), ça doit donc pas être contenu par un système.
    mais là je ne te suis pas...utilisé par 2 systèmes...mais...pas être contenu par un système ?

    Si un composant est utilisé par un système, n'est-il pas aussi contenu par celui ci, associé à l'Entity ( le numéro ) ?

  2. #22
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Euh Koala, je confonds p-e, ou alors c'est toi, mais ce dont tu parles c'est juste être "Data-driven". Non on ne va pas recompiler pour créer une épée de feu après avoir créé l'épée de glace. On a un objet épée, une datasheet correspondante et des attributs de part et d'autre.
    L'ECS est effectivement une approche orientée data driven. Mais je crois que vous êtes d'accord sur ce qu'est l'ECS.

    Une remarque quand même sur Unity, l'approche qui est utilisée est inspirée de l'ECS, mais cela reste une interface publique que Unity fournit aux utilisateurs. En particulier, il me semble que c'est plus un EC+ (néologisme de ma part, ne cherchez pas sur internet ), c'est à dire que l'on n'a pas vraiment accès aux systèmes et que l'on va paramétrer des comportements attachés aux composants via des scripts. (ce qui n'est pas incompatible avec un ECS, vu qu'un script, c'est aussi une data). De plus, je crois que Unity affiche les composants comme des éléments des entités, sous forme d'arborescence. C'est probablement pas implémenté comme ça en interne (un arbre d'objet polymorphique est très joli pour qu'un utilisateur voit les dépendances, mais c'est pas optimal lorsque le jeu tourne - et c'est finalement ce qui compte).

    (mais bien sur, cela dépend de comment on conçoit l'ECS et il n'y a pas une façon de faire, chaque moteur et jeu qui utilisent un ECS ont probablement une implémentation propre)

    Citation Envoyé par mazertys17 Voir le message
    Toujours en partant sur ce concept Entity Composant System, svp dites moi si j'ai bien suivit :

    On a une Entity, dans mon cas, plus haut, le Data ( qui devrait donc s'appeler Entity ), qui représente un objet du jeu ( une biche, un arbre, etc...).

    On a des Components, qui sont des modules utilisés/ouPas pour une Entity ( ex un Graphics, qui l'affiche, un Physics, qui le positionne ).

    On a des System, qui sont des sortes de containers intelligents de components, qui les font tourner.
    Dans l'implémentation que je préfère, on a :
    - entité = un id unique (comme l'a dit koala et Iradrille)
    - composant = données
    - système = algos

    On ne pas faire plus simple et modulable Chaque système manipule une liste de composants de même type (de préférence). Il faut pouvoir ajouter et supprimer des entités facilement, c'est plus facile si on manipule des tableaux contenant des type identique (donc si un système utilise 2 types de composant, c'est plus efficace d'avoir 2 vector plutôt qu'un vector d'objet polymorphique - et plus cache friendly).

    A chaque update, les systèmes parcourent les composants et les mettent à jour (il n'est pas obligé d'avoir tous les systèmes qui mettent à jour en même temps, mais si ce n'est pas le cas, il faut faire attention aux conflits).

    La difficulté est les interactions entre les composants, c'est probablement le plus couteux. Dans le meilleur des cas, quand on met à jour un composant, on n'a besoin d'aucune info externe, c'est rapide. Un peu plus lent, c'est un composant qui a besoin d'un info provenant d'un composant du même système (ie pas de risque de conflit). On peut optimiser les accès (par exemple avec un space partitionning si on veut les composants "position" à proximité d'un composant "position" donné). Le cas le pire, c'est un composant qui nécessite des infos d'autres composants. Donc besoin de faire des recherches inter-systèmes (par exemple, à partir d'un composant "position" du joueur, on recherche les composants "position" qui sont plus proches, puis on doit retrouver les entités correspondantes, puis trouver les composants "ia" correspondants à ces entités - si elles ont en un - et passer en mode "j'attaque le joueur").

    Citation Envoyé par mazertys17 Voir le message
    ...au shared_ptr...
    Là, je crois que la question va vite être réglée et qu'il n'y aura pas trop d'avis divergeant : un pointeur, c'est un cache miss. Quelque soit le type de pointeur (et c'est encore plus vrai avec shared_ptr, puisqu'il peut avoir un control bloc dans un troisième espace mémoire).
    Si tu veux des performances, pas de tableau de pointeurs.
    (d'où ma remarque précédente sur le fait de ne pas avoir de tableau d'objets polymorphiques. C'est pas une bonne idée du tout)

    (Remarque : modulo les performances. Si tu as un jeu avec 10 entités en même temps - rien du tout donc - la perte de performances du fait des pointeurs sera négligeable. Dans ce cas, une approche old-school sans ECS ne sera pas critique. Tant que ton jeu n'évolue pas trop).

    Citation Envoyé par mazertys17 Voir le message
    Chose que je voulais éviter puisqu'on arrête pas de dire, probablement à raison que c'est un signe de mauvaise conception.
    Déjà dit et répété, la mauvaise conception, c'est les propriétaires multiples. Pas l'utilisation de shared_ptr en soi. Mais on va pas relancer le débat

    Citation Envoyé par mazertys17 Voir le message
    si déjà je suis dans le juste sur le concept Entity/Component/System
    Pas de définition absolue de comment doit être implémenté un ECS, donc cela dépend. Mais dans ma vision de la chose, c'est pas du tout pareil que toi. (je pense que je suis proche de la vision de Iradrille)

    @Iradrille: l'idée de masques est intéressante, je ne l'avais jamais vue. Par contre, il y aura vite un problème si on a beaucoup de composants de types différents. (et donc cela va dépendre la taille du jeu et de comment on sépare les composants - j'ai tendance à en créer beaucoup, pour avoir des composants "atomiques").

    Pour l'étape intermédiaire, c'est quelque chose que j'avais vu. Je crois que l'on peut en fait généraliser le concept : que les systèmes puissent travailler en plusieurs fois (pas simplement parcourir 1 fois les composants lors de l'update), pour faire des tries, des partitionnements des données (un grand classique est réorganiser les infos utilisées par la partie graphique, pour regrouper ensemble les éléments qui utilisent le même contexte GL, pour des raisons de performances), update de cache pour faciliter le boulot des systèmes, etc.

    Citation Envoyé par LittleWhite Voir le message
    Mais j'ose croire qu'il est impossible de faire un jeu complet avec des composants et un système ECS. C'est mon ressenti, mon impression et surement à cause de mon ignorance.
    Il y a des parties de l'ECS que j'aime bien. Mais à part ça...
    A mon sens, la question de savoir si on peut faire un jeu complet basé sur un ECS est mal posée. On peut faire un jeu complet sans ECS. Mais c'est lourd. On peut faire qu'avec ECS, mais c'est forcer l'utilisation d'une technique.
    La question serait plutôt si on utilise un ECS, seul ou avec d'autres techniques, quel sera le coût (performances, lourdeur à mettre en place, code très complexe pour certaines fonctionnalités, etc) et les bénéfices (faciliter de création et évolution des éléments du jeu, faciliter de travail avec des game designers, etc).
    Je crois aussi que l'ECS ne peut pas tout faire. En particulier, même si on a un système "graphique" dans l'ECS, il faut un backend pour le graphisme, qui optimise le rendu (scenegraph, space partition, caches, etc)

    Citation Envoyé par LittleWhite Voir le message
    Je serai vous, pour tester l'ECS, je ne tenterai même pas de mettre ça dans mon jeu ni rien. Je tenterai de mettre ça dans un programme à part, un bac à sable, avec un main et quelques classes de base. Cela permet de tester les limitations, les contraintes, les avantages et mieux comprendre les articles que l'on lit sur Internet.
    Oui.
    Et commencer par des projets pour apprendre à gérer les ressources, la lib graphique, etc (au moins 1 projet de test pour chaque chose, et probablement plusieurs)

    Citation Envoyé par Iradrille Voir le message
    En quoi la position est spéciale et doit être intégrée à l'entité ? (J'avoue que j'ai du mal à trouver un exemple d'entité qui n'aurait pas de position, mais ça existe surement).
    Pour moi, toutes les données sont des le composants, donc tout ce qui a une donnée et interagit avec d'autres composants doit être une entité + composant(s). En partant de là, il est facile de concevoir des entités sans position :
    - un effet de vent, qui agirait sur le déplacement des joueurs et monstres
    - le soleil (qui sera représenté par une direction plutôt qu'une position), qui produit des ombres (et donc interagit avec le système graphique) ou modifiera la détection des ennemis s'ils ont le soleil dans les yeux
    - le vent qui produira un effet sonore 3D
    etc.

    Bref, je suis d'accord, pas de données dans Entité, juste un identifiant.

    Citation Envoyé par mazertys17 Voir le message
    Si un composant est utilisé par un système, n'est-il pas aussi contenu par celui ci, associé à l'Entity ( le numéro ) ?
    Comme l'a dit Iradrille, si un composant est utilisé par 2 systèmes, quel système doit être le conteneur ?

  3. #23
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par mazertys17 Voir le message
    Si un composant est utilisé par un système, n'est-il pas aussi contenu par celui ci, associé à l'Entity ( le numéro ) ?
    Non,

    Un système va posséder une référence (au sens large, un pointeur ou un référence, les deux marchent) sur des ensembles de composants. Tout comme il va contenir une référence sur la liste d'entités.

    Il utilise les entités, mais ne les possède pas, c'est pareil pour les composants.

    @mintho carmo Pour les masques, jusque 64 (vui, spas énorme) composants ça devrait pas poser de problèmes, au delà faudra surveiller la performance de std::bitset (qui n'est pas réputé pour être rapide).

  4. #24
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Merci pour toutes ces explications, que je vais prendre soin de relire

    Si j'ai bien compris, le grand avantage dans le ECS, et qui est aussi un peut son inconvénient, c'est qu'il traite les données "en masse", dans la mémoire, donnés ( ou composants ) placées les unes a côtés des autres dans des (vector/tableaux), augmentant ainsi considérablement les performances.
    Mais cela implique que pour toutes les interactions, il faut faire "bouger" de l'information ( ce qui peut être assez coûteux ).

    Donc, les composants ne doivent pas utiliser de références ou de pointeurs. Ils se contentent d'être des données "concrètes", associés à un id, conçu pour être traités à la chaîne, en somme.

    En revanche, les systèmes, eux, utilisent des références/ptr de ces composants pour les faire fonctionner ( ainsi plusieurs systèmes différents peuvent utiliser des mêmes données )?

    Dans le cas d'un affichage, par ex, on pourrait avoir ceci :

    -un vector de texture, avec un/des id associés.
    -un vector de vertex, avec un id associé.

    -un system Draw, qui prendrait les vertex, puis les utiliserait sur les textures appropriés et les dessinerait, puis continuerait et ferait ca a l'infini...?

    -un autre System Positioner, par ex, prendrait les positions de l'objet, et irait repositionner les vertex, faisant ça lui aussi a l'infini ?

    Suis-je sur la bonne voie ?

  5. #25
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Si j'ai bien compris le principe, je me pose quelques questions sur les fondations du programme :

    -une class Game, qui va charger les composants demandés et les systems, le tout directement sur l'Engine. ( cela vous paraît-il correct ) ?
    -une class Engine, qui va contenir tous les "containers" de composants, ce qui pourrait donner ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
     
    class Engine
    {
     
    public :
     
        Engine() ;
        ~Engine() ;
     
        void                                                                           update () ;
     
     
        std::map < size_t, sf::Texture >                                C_Texture ;
        std::map < size_t, sf::Vector2f >                               C_Position ;
        std::map < size_t, sf::Vertex[4] >                              C_Vertex ;
        etc...
     
        std::vector < std::unique_ptr < System > >               S_System
     
     
    };
    Merci si vous pouvez m'aider

  6. #26
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Citation Envoyé par mazertys17 Voir le message
    Si j'ai bien compris, le grand avantage dans le ECS, et qui est aussi un peut son inconvénient, c'est qu'il traite les données "en masse", dans la mémoire, donnés ( ou composants ) placées les unes a côtés des autres dans des (vector/tableaux), augmentant ainsi considérablement les performances.
    L’intérêt premier de l'ECS est la souplesse d'utilisation (mais pas pour le développeur...).
    Si tu as une hiérarchie en profondeur (par exemple, la classe "Monster" hérite de "Entity", qui hérite de "Movable", qui hérite de "Drawable", qui hérite de "Object"), si tu veut donner une même propriété a 2 classes qui ne sont pas dans la même hiérarchie, tu devras le mettre dans "Object" (bof) ou dupliquer le code (re-bof).
    Si tu as un composition par hiérarchie (la classe "Monster" hérite de "Entity", de "Movable", de "Drawable" et de "Object"), c'est mieux en termes de réutilisabilité, mais un game designer ne peut pas modifier le comportement sans modifier le code (bof).
    Avec l'ECS, tu peux créer des comportements paramétrables en dehors du code.

    Et la, je rejoins LittleWhite : l'ECS ne sert pas dans tous les jeux. Il n'y a aucun intérêt (sauf pédagogique) a créer un casse brique ou un arcanoide avec un ECS. A partir du moment ou changer le game design n'est pas critique (parce que le code du jeu n'est pas assez complexe, parce que il n'y a pas beaucoup de type d’éléments, que les types d’éléments ne changent pas, ou d'autres raisons), un ECS ne va rien apporte en qualité de code, mais va le complexifier.

    Le traitement des données "en masse" n'est qu'une obligation qui decoule de l'ECS, du fait que la multitude d'objets (entity et component) fait que l'on a beaucoup plus d’accès, trie et recherche a faire et donc besoin d'une meilleure optimisation du code.
    Mais les techniques de regroupement mémoire utilisées dans l'ECS n'est pas obligatoire (si les performances ne sont pas critiques) et peuvent être utilisées aussi sans ECS (cf les pool objects par exemple). Quand tu as une hiérarchie d'objet, si tu as besoin de mettre a jour la position en utilisant les infos de vitesse, tu peux y accéder directement, c'est dans le même objet. Avec un ECS, ces infos sont dans 2 composants différents, tu as besoin de parcourir les 2 tableaux pour trouver les infos correspondantes a la même entity, c'est plus long.

    Citation Envoyé par mazertys17 Voir le message
    Dans le cas d'un affichage, par ex, on pourrait avoir ceci :

    -un vector de texture, avec un/des id associés.
    -un vector de vertex, avec un id associé.

    -un system Draw, qui prendrait les vertex, puis les utiliserait sur les textures appropriés et les dessinerait, puis continuerait et ferait ca a l'infini...?

    -un autre System Positioner, par ex, prendrait les positions de l'objet, et irait repositionner les vertex, faisant ça lui aussi a l'infini ?

    Suis-je sur la bonne voie ?
    C'est pour cela que j'ai parle du backend OpenGL, justement parce que je me doutais que ca allait poser probleme (l'affichage est toujours un cas particulier )

    Quel composant a besoin d’accéder aux infos "id de texture" ou "id de vertex" ? Les texture/vertex ont besoin d’accéder a quels autres composant ?
    A priori, aucun aux 2 questions. Conclusions, ces infos n'ont pas besoin d’être dans l'ECS.

    De la même façon, OpenGL n'a pas besoin des infos de l'ECS, tu lui dis "utilises ces vertices et ces textures" et il s'en moque de ce que cela représente.

    Tu as besoin des id de texture peut être dans l'ECS (pour afficher la bonne arme dans les mains du perso et la bonne armure sur son dos), mais les textures/meshs en eux même sont des infos du backend graphique, qui doit faire son boulot a part (en bossant directement avec le ressource provider)
    Si tu as par exemple 2 perso affiches et que chacun a une arme, tu dois gérer les 2 persos comme 2 entités dans ton ECS, mais ton backend graphique devrait (pour faire les choses au mieux), regrouper les 2 armes ensembles (pour utiliser la même texture/shaders sur les 2 armes) puis la peau des perso (idem, même texture et shader), les tissus, etc. Bref, la facon dont le backend graphique fait le rendu du "monde" est différent de la façon dont l'ECS le gère.

    Bref, je rejoins encore une fois LittleWhite, l'ECS ne gère pas tout.

  7. #27
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Salut

    Même si l'ECS n'est pas la formule magique qui résout tous les problèmes, ça me paraît tout de même assez adapté pour un STR vous ne trouvez pas ? c'est typiquement le genre de jeu dans lequel on va avoir plein d'unités qui vont partager des propriétés, et les choix de ces propriétés reviennent plus aux level designers et aux créatifs qu'au développeur. Même si ces deux rôles sont remplis par la même personne dans un premier temps, c'est une manière saine de s'y prendre.
    Find me on github

  8. #28
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Bref, je rejoins encore une fois LittleWhite, l'ECS ne gère pas tout.
    Merci pour ta réponse.

    Donc dans ce cas, par ex, il pourrait y avoir une fonction indépendante à la class Engine, qui pourrait être "print", et gérerait toute seul l'affichage, ex :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
     
    class Engine 
    {
         ...
         std::map < size_t , Position >      C_position ;
         ...
         std::map < size_t , Graphics >     U_graphic ; //avec U pour Unique peut être ? histoire de le classer dans une catégorie autre
                                                                            //que S pour System et C pour Composant ?
     
    };
     
    class Engine::print()
    {
         for ( auto& it : _graphic  )
        {
              it.seond.draw() ;
        }
    }
    de la même façon qu'un composant, un Graphics serait associé à un ID, mais le Graphics ici serait également un mini system qui s'affiche tout seul, en ayant accès, par ex au composant "Position", qu'il vérifie pour updater ses vertexs ?

    cette solution vous paraît viable/optimisé ?


    Je serai vous, pour tester l'ECS, je ne tenterai même pas de mettre ça dans mon jeu ni rien. Je tenterai de mettre ça dans un programme à part, un bac à sable, avec un main et quelques classes de base. Cela permet de tester les limitations, les contraintes, les avantages et mieux comprendre les articles que l'on lit sur Internet.
    Oui, effectivement, merci pour le conseil, LittleWhite. C'est comme ça que je vais devoir procéder


    Merci si vous pouvez m'aider

    ps : et que pensez vous d'utiliser un std::vector < unique_ptr < System > > pour les Systems, sachant que celui ci est a héritage ?
    pps : et aussi des noms donnés aux composants avec C_composantX, et system avec S_ia etc...

  9. #29
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    A tout hasard, il existe anax et EntityX, deux bibliothèques d'ECS.

    Je ne les connais pas encore, mais votre discussion m'a amené à chercher ce qu'est précisément un ECS, et je suis tombé dessus.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  10. #30
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    26 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 858
    Points : 218 575
    Points
    218 575
    Billets dans le blog
    120
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Salut

    Même si l'ECS n'est pas la formule magique qui résout tous les problèmes, ça me paraît tout de même assez adapté pour un STR vous ne trouvez pas ? c'est typiquement le genre de jeu dans lequel on va avoir plein d'unités qui vont partager des propriétés, et les choix de ces propriétés reviennent plus aux level designers et aux créatifs qu'au développeur. Même si ces deux rôles sont remplis par la même personne dans un premier temps, c'est une manière saine de s'y prendre.
    Oui, il y a certaines parties qui sont cools et avantages dans l'ECS, pour un STR et là, je suis d'accord avec votre point de vue. L'ECS est adapté dans les cas "comportements" :
    - une unité movable (peux avoir une destination et s'y rendra) ;
    - une unité peut attaquer
    - une unité peut construire
    - une unité peut lancer des sorts
    - une unité peut ramasser des ressources
    ...
    - un batiment peut produire
    ...
    (et bien sur, chacun de ces comportements peux ne pas exister)

    Mais, là, on ne parle que du comportement des éléments du jeu, comportement correspondant à un type précis de jeu. En y réfléchissant bien, on peux aussi appliquer assez facilement ce type de cas à d'autres jeux, juste les comportements changent.
    par contre, comme hier (page 1), je pense que je replonge dans une arborescence classique OO, réalisable avec le pattern Strategy ...

    Maintenant, il y a une partie, que tous les jeux ont :
    - une boucle de jeu (update des éléments)
    - l'affichage à l'écran des éléments
    ...
    - contrôle par le joueur ou l'IA

    Et là, en réalité, on a un second niveau d'ECS, d'après moi. Dans le sens, vous avez la partie "comportementale des éléments du jeux" (soit, le gameplay), qui aura son propre ECS, indépendant (ou séparé), de l'ECS "moteur". Et ce second sera applicable (en théorie) à tous les jeux. Malheureusement, l'affichage est ultra ultra contraignante dans les jeux vidéos (les comportements des éléments aussi ).

    @mazertys17 :
    Je trouve que vous avez un souci de sémantique (utilisation des mots), qui se répercute dans le nommage des variables. Autant que dans les probabilité en mathématique, la sémantique en programmation est ultra importante, pour que le code soit rapide à comprendre.
    Je vous donne un exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Engine::print()
    {
         for ( auto& it : _graphic  )
        {
              it.seond.draw() ;
        }
    }
    Pourquoi une fonction qui fait un bouclage pour faire des "draw", va s'appeler "print()" ?
    Cela n'a pas de sens, car l’utilisation d'un mot différent suggère une action différente ... alors que ce n'est pas le cas.

    Par contre, pour moi, l’existence de cette fonction "draw()/print()" était obligatoire.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  11. #31
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Par contre, pour moi, l’existence de cette fonction "draw()/print()" était obligatoire.
    donc vous voulez dire que c'est en effet une démarche correcte ( mis à part le nom ) ?

    voici l'exemple de l'affichage, que je viens de mettre en place :

    déjà, je prend le parti de construire a la base le composant position :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class Engine 
    {
        ...
        std::map < size_t , sf::Vector2f >                              C_position ;
        ...
        std::map < size_t, Graphics >                                   U_graphics ;
        ...
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    Game::build ( std::string& name, float x , float y )
    {
        ....
        _number ++ ;
        _engine.C_position.insert( std::pair < size_t , sf::Vector2f > ( _number , sf::Vector2f ( x , y ) ) ) ;
        ....
    }
    puis je construit le module Graphics ( s'il est appelé )

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    Game::buildGraphics ( std::ifstream& flux )
    {
                    //lecture des données qui sont transfomés en word1, int0, int1 etc...
                    ...
                    std::map < size_t , sf::Vector2f > :: iterator itPosition ;
                    itPosition = _engine.C_position.find ( _number ) ;
                    _engine.U_graphics.insert ( std::pair < size_t , Graphics >( _number , Graphics ( _data , itPosition -> second ) ) ) ; // je mets ici la ref de position
                    std::map < size_t , Graphics > :: iterator it ;
                    it = _engine.U_graphics.find ( _number ) ;
                    it -> second.add ( "empty" , _resources.getTexture( word1 ) , int0 , int1 , int2 , int3 , int4 , int5 ) ;
                    ...
    }
    ( sachant que ma class graphics a une référence sf::Vector2f& _position ).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
     
    void Engine::draw()
    {
        _data._window.clear() ;
        for ( auto& it : U_graphics )
        {
            it.second.draw() ;
        }
        _data._window.display() ;
    }
    ( personne ne m'a tellement dit, sauf erreur, si l'idée d'un unique "data" qui contient les infos de base du system, comme la fenêtre, le temps etc... est une bonne idée, alors j'ai pris le parti de le garder pour l'instant. C'est pourquoi l'Engine l'utilise pour afficher la fenêtre ).

    Dans mon cas, il faut anticiper le fait que si un objet est détruit, l'affichage doit être le 1er à pratiquer la destruction du composant. ( pour éviter ainsi les pb ).

    ps: j'ai obtenu l'hébergement Developpez pour mon projet, ( et j'en profite pour remercier chalereusement l'équipe au passage ), alors je vais pas tarder à mettre un forum et tt ce qu'il faut en place, pour avancer plus proprement. Tout programmeur qui voudra contribuer à mon projet sera bienvenu .

  12. #32
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Et la, je rejoins LittleWhite : l'ECS ne sert pas dans tous les jeux. Il n'y a aucun intérêt (sauf pédagogique) a créer un casse brique ou un arcanoide avec un ECS. A partir du moment ou changer le game design n'est pas critique (parce que le code du jeu n'est pas assez complexe, parce que il n'y a pas beaucoup de type d’éléments, que les types d’éléments ne changent pas, ou d'autres raisons), un ECS ne va rien apporte en qualité de code, mais va le complexifier.
    Le choix d'utiliser un ECS ou pas doit être plus basé sur l'évolution du gameplay (avec un ECS on ne "bloque" aucune possibilité).
    Pour un casse-brique, si les game designers viennent te voir demain en te disant "on vient d'avoir une pu***n d'idée : des billes apparaissent régulièrement (toutes les x secondes), et quand il y a plus de billes que de briques, les billes deviennent des briques, et les briques deviennent des billes".
    Avec un ECS, passer d'un casse brique classique à ça, c'est relativement simple.

    Si on doit ajouter des briques spéciales qui, une fois détruites, deviennent des raquettes supplémentaires que le joueur contrôle, c'est simple aussi.

    Mettre en place un ECS c'est pas forcément simple et demande pas mal de boulot, si ça ne t’apporte rien (parce que le gameplay est défini et changera pas (ou peu)), alors c'est probablement pas une bonne idée.
    Mais je ne pense pas qu'il existe un type de jeu en particulier qui ne doive pas être implémenté grâce à un ECS (ou, dit autrement, qui ne puisse pas bénéficier de la puissance d'un ECS).

    Citation Envoyé par mintho carmo Voir le message
    Mais les techniques de regroupement mémoire utilisées dans l'ECS n'est pas obligatoire (si les performances ne sont pas critiques) et peuvent être utilisées aussi sans ECS (cf les pool objects par exemple). Quand tu as une hiérarchie d'objet, si tu as besoin de mettre a jour la position en utilisant les infos de vitesse, tu peux y accéder directement, c'est dans le même objet. Avec un ECS, ces infos sont dans 2 composants différents, tu as besoin de parcourir les 2 tableaux pour trouver les infos correspondantes a la même entity, c'est plus long.
    Parcourir un ou deux tableaux ne change (quasiment) rien (si les tableaux sont triés dans le même ordre) tant qu'on a les données dont on a besoin et rien d'autre.
    Quant on à des données inutiles, ça prend de la place dans le cache pour rien et ça va augmenter le nombres de cache misses.

    Parcourir 2 tableaux "positions" et "velocities" sera très certainement plus rapide que de parcourir un seul tableau d'entités qui contiennent tout un tas de données inutile au traitement. Dans le cas d'une hiérarchie d'objets, tu auras au strict minimum un pointeur vers une vtable qui sera "fournie" avec tes objets mais qui sera inutile au traitement.

    Mais on se rapproche dangereusement de l'optimisation prématurée. =)

    Citation Envoyé par mintho carmo Voir le message
    Bref, je rejoins encore une fois LittleWhite, l'ECS ne gère pas tout.
    Oui, l'ECS gère le gameplay (toute la logique du jeu), la partie affichage doit être à part.

    Citation Envoyé par leternel Voir le message
    A tout hasard, il existe anax et EntityX, deux bibliothèques d'ECS.

    Je ne les connais pas encore, mais votre discussion m'a amené à chercher ce qu'est précisément un ECS, et je suis tombé dessus.
    Jamais entendu parler de anax, mais j'ai par contre entendu parler de EntityX en bien à plusieurs reprises.
    Jamais utilisé ni l'une ni l'autre par contre.

  13. #33
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    J'irai jeter un oeil attentif sur EntityX, dans ce cas.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  14. #34
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    A tout hasard, il existe anax et EntityX, deux bibliothèques d'ECS.
    Oui, j'en ai entendu parler. Après, au point ou j'en suis, je pense que c'est jouable de bâtir mon programme juste avec la SFML.

    Oui, l'ECS gère le gameplay (toute la logique du jeu), la partie affichage doit être à part.
    Oui, donc il faut un peut mixer l'ECS avec la poo, si je comprend bien, pour pouvoir avoir un peut de flexibilité...En tout cas, je reconnais que ce concept d'ECS a l'air vraiment pratique.

    Encore quelques questions :

    un std::map < size_t , Composant > pour chaque composant est-il une bonne solution ( ou le std::map n'est pas forcément le plus optimisé/utile dans ce cas ) ?

  15. #35
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par mazertys17 Voir le message
    Encore quelques questions :

    un std::map < size_t , Composant > pour chaque composant est-il une bonne solution ( ou le std::map n'est pas forcément le plus optimisé/utile dans ce cas ) ?
    Il n'y a pas de réponse générale. C'est au cas par cas.

    Pour un composant que toutes (ou presque) tes entités possèderont, un std::vector<Composant> peut être un bon choix, il y aura quelques "cases vides" mais tu aura une bonne localité pour tes données.

    Pour un composant que très peu d'entités possèderont, une std::map<Entity, Composant> peut être un bon choix si tu as besoin d'un ordre précis, sinon un std::unordered_map<Entity, Composant> peut faire l'affaire. Ou peut être qu'une boost::container::flat_map<Entity, Composant> sera meilleure.

    Bref, pas de réelle réponse, désolé.

    Citation Envoyé par mazertys17 Voir le message
    Si j'ai bien compris, le grand avantage dans le ECS, et qui est aussi un peut son inconvénient, c'est qu'il traite les données "en masse", dans la mémoire, donnés ( ou composants ) placées les unes a côtés des autres dans des (vector/tableaux), augmentant ainsi considérablement les performances.
    Mais cela implique que pour toutes les interactions, il faut faire "bouger" de l'information ( ce qui peut être assez coûteux ).

    Donc, les composants ne doivent pas utiliser de références ou de pointeurs. Ils se contentent d'être des données "concrètes", associés à un id, conçu pour être traités à la chaîne, en somme.

    En revanche, les systèmes, eux, utilisent des références/ptr de ces composants pour les faire fonctionner ( ainsi plusieurs systèmes différents peuvent utiliser des mêmes données )?
    J'avais pas vu ce post, ça résume bien le principe oui.

    Il est préférable (quand c'est possible) d'avoir des systèmes qui soient totalement indépendants : système A travaille sur les composants 1 et 2, système B travaille sur les composants 3, 4 et 5.
    Çà permet d'avoir des composants triés correctement pour les systèmes, et de multithreader facilement les systèmes. (Cas le plus simple : 1 thread par système).

    Mais c'est pas toujours possible, dans ce cas il faut faire gaffe aux data races (et à l'ordre d'éxécution des systèmes).

  16. #36
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par mazertys17 Voir le message
    Oui, j'en ai entendu parler. Après, au point ou j'en suis, je pense que c'est jouable de bâtir mon programme juste avec la SFML.



    Oui, donc il faut un peut mixer l'ECS avec la poo, si je comprend bien, pour pouvoir avoir un peut de flexibilité...En tout cas, je reconnais que ce concept d'ECS a l'air vraiment pratique.

    Encore quelques questions :

    un std::map < size_t , Composant > pour chaque composant est-il une bonne solution ( ou le std::map n'est pas forcément le plus optimisé/utile dans ce cas ) ?
    De manière tout à fait générale, tu dois te dire qu'une très grosse majorité du travail d'utilisation de tes composants sera basé soit sur l'exécution par lot (de begin(), à end()), soit sur la recherche (quelle est la valeur du composant (56 998) ou de celui correspondant à l'entité 12 345 ) avec très peu (pour ainsi dire aucun) ajouts pendant le jeu (du moins, une fois que les données ont été chargées s'entend) et possibilité de modification (mais sans obligation) de certaines valeurs.

    Les composants traités par lots, mais sur lesquels tu n'a que peu de recherches à effectuer seront sans doute placés dans un std::vector car cela permet de profiter de la mise en cache des données effectuée par le processeur.

    Ceux pour lesquels tu as beaucoup de recherches à faire prendront sans doute place dans un std::set/ std::unordered_set s'ils ne sont jamais modifiés (la description associée à une entité ne change théoriquement jamais )o u dans une std::map/std::unordered_map s'il doivent être modifiables (vie, vélocité (avec accélération/ décélération possible), ...) voir dans un conteneur "multi index" comme ceux fournis par boost::multi_index si tu te rend compte que tu utilises régulièrement plusieurs types de clé pour tes recherhes

    Evidemment, il s'agit d'une approche "à la grosse louche", qui mérite amplement d'être peaufinée en cours de route et en fonction des composants utilisés... En plus, tout ne sera pas forcément toujours aussi simple que ces deux choix "principaux"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  17. #37
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    placés dans un std::vector
    Ok, cela supose qu'il faut mettre l'ID dans les attributs du vector j'imagine..?..

    Pour aller pas à pas, je commence a élaborer quelques fonctions ( pour l'instant c'est avec std::map, mais je vais changer ca ). S.V.P, j'aimerais savoir si cela vous parait aller dans le bon sens ( ca marche, mais est-ce bien correct comme facon de faire, selon vous ) :

    Dans cette exemple, le but est simplement de sélectionner un objet ( ou plusieurs ), en mettant les ID concernés dans un vector ( qui pourra servir par la suite pour d'autres opérations, comme donner des ordres de déplacement etc... ).

    ici, un système "S_Control" qui contient des références sur les containers de composants suivant : C_Position, C_SelectionZone, I_selected.

    -le C_Position contient des positions ( sf::vector2f )
    -le C_SelectionZone contient un sf::IntRect ( la zone de selection )
    -le I_Selected contient tous les IDs selectionnés. ( en fait ce n'est pas un composant, d'où le I devant au lieu de C ).

    Cela donne ceci dans mon Engine :
    (encore une fois, il faudra que je change les std::map en vector )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
     
    class Engine
    {
     
    public :
     
        Engine( Data& data ) ;
        ~Engine() ;
     
        void                                                            add () ;
        void                                                            update () ;
        void                                                            draw() ;
     
        Data&                                                           _data ;
     
        std::vector < std::shared_ptr < System > >                      S_system ;
     
        std::map < size_t, Graphics >                                   U_graphics ;
     
        std::map < size_t , sf::Vector2f >                              C_position ;
        std::map < size_t , sf::IntRect >                               C_selectionZone ;
     
        std::vector < size_t >                                          I_selected ;
     
    };
    voici mon system "S_Control", hérité de System :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    class S_Control : public System
    {
    public :
     
        S_Control(  std::map < size_t , sf::Vector2f >& position ,
                    std::map < size_t , sf::IntRect >&   controlZone ,
                    std::vector < size_t >& selected ) ;
     
        ~S_Control() ;
     
        void                                                             update() ;
     
     
        std::map < size_t , sf::Vector2f >&                              C_position ;
        std::map < size_t , sf::IntRect >&                               C_selectionZone ;
        std::vector < size_t >&                                          I_selected ;
     
    protected :
     
    };
    et enfin ( et c'est surtout là que j'ai besoin de votre avis ), son utilisation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
     
     
    void S_Control::update()
    {
        if ( sf::Mouse::isButtonPressed ( sf::Mouse::Left ))
        {
            if ( !sf::Keyboard::isKeyPressed ( sf::Keyboard::LControl )) //si Control n'est pas enfoncé, alors on désélectionne tt le monde.
            {
                I_selected.clear() ;
            }
            for ( auto& it : C_position )
            {
                for ( auto& itS : C_selectionZone )
                {
                    if (  itS.first == it.first )
                    {
                        if (  sf::Mouse::getPosition().x > it.second.x + itS.second.left && sf::Mouse::getPosition().x < it.second.x + itS.second.left + itS.second.width
                        &&    sf::Mouse::getPosition().y > it.second.y + itS.second.top  && sf::Mouse::getPosition().y < it.second.y + itS.second.top  + itS.second.height )
                        {
                            I_selected.push_back ( it.first ) ;
                            it.second.x ++ ; // ca c'est pour le test, quand un objet est selectionné, on le voit se déplacer vers la droite.
                        }
                    }
                }
            }
        }
    }
    J'ai donc une double boucle, et pour une chose très basique, ce qui doit pas être top. Est-ce pourtant bien cela le ECS ?

    Bref. Tout cela me parait fort lourd au départ, mais je supose que plus ca avancera, plus ca sera "rentable".

    Merci si vous pouvez m'aider

  18. #38
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Ne t'intéresse pas trop à la "machinerie" dans un premier temps...

    Dans un premier temps, ce qu'il te faut, c'est quelques composants simples qui te permettront d'en créer de plus complexes et un moyen de disposer d'entités.

    Les entités, c'est très simple : ce ne sont que des identifiants uniques. Cela n'a rien : ni comportement, ni donnée. Le plus simple et le plus efficace pour identifier quelque chose, c'est d'utiliser une valeur entière (sans doute non signée) et de s'assurer qu'elle n'est utilisée que pour une chose. Mais comme "créer, c'est nommer", on peut envisager (ce sera plus facile pour le débuggage) de rajouter une chaine de caractères qui représente le nom de chaque entité.
    Tu auras donc une structure Entity qui prendra une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct Entity{
        size_t id;
        std::string name;
    };
    // nécessaire pour l'utilisation d'un set
    bool operator<(Entity const & one, Entity const & two){
        return one.id<two.id;
    }
    Puis, il te faut "quelque chose" qui te permette d'avoir "toutes les entités sous la mains"... Cela prendra la forme d'une classe "EntityHolder" proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    class EntityHolder{
        public:
            using const_iterator = std::set<Entity>::const_iterator;
            /* Crée une nouvelle entité et revoie son identifiant unique */
            size_t create(std::string const & name){
                 auto id=items_.size()+1; // l'identifiant (unique) de chaque
                                          // entité est son ordre d'insertion
                                          //  dans la table ;)
                 items_.insert(Entity{id, name});
                 return id;
            }
            /* tu voudras sans doute, lors du développement, savoir si une 
             * entité existe déjà ;) */
            bool exists(std::string const & name) const{
                 return find(name)!=0;
            }
            /* lors du développement, tu voudras sans doute récupérer
             *  l'identifiant d'une entité que tu connait par son nom
             */
            size_t find(std::string const & name) const{
                auto const & it = find_if(begin(),end(),
                                          [name](Entity const & e)->bool {
                                              return e.name == name;
                                          });
                return it == end()? 0 : it->id;
            }
            const_iterator begin() const{
                return items_.begin();
            }
            const_iterator end() const{
                return items_.end();
            }
    private:
        std::set<Entity> items_;
    };
    L'idée est que tu voudras sans doute travailler avec tes entités (durant le développement) sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    int main(){
        EntityHolder holder;
        holder.create("petite epee rouillee");
        holder.create("petite hache rouillee");
        holder.create("petit gourdin de bois vert");
        holder.create("petite potion de sante");
        holder.create("petite potion de mana");
        holder.create("petite potion de force");
        holder.create("vers sanguinaire");
        holder.create("gobelin des montagnes");
        holder.create("Mercenaire Troll");
        holder.create("Chaman Troll");
        holder.create("Sort de soins");
     
        /*... Beaucoup plus loin, en développpement
         */
        auto found = holder.find("petite epee rouillee");
    }
    (bon, je sais... je me dirige beaucoup plus vers un RPG que vers un STR là, mais le principe reste valide )

    Ensuite, il faut les composants qui t'intéressent. On peut dire qu'il y a deux types essentiels de composants:
    • Les composants "généraux" qui représentent des caractéristiques essentielles comme le type de dégat, le type d'arme, la race, ...
    • Les composants plus "spécifiques" comme "le nombre de point de vie du monstre correspondant à l'entité 123 896"
    Tout composant a forcément un identifiant unique. Les composant "généraux" pourraient d'ailleurs se limiter à cet identifiant unique, mais on peut (comme on l'a fait pour les entités) envisager de rajouter une chaine de caractères qui les nommes.

    Les composants "spécifiques" nécessiteront en plus une "clé étrangère" représentant l'entité à laquelle ils font référence et disposerons sans doute aussi de valeurs supplémentaires. Je m'explique: Nous pourrions avoir une "liste de caractéristiques proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    id    |   nom
    =================
      1   | vie
      2   | mana
      3   | force
      4   | agilite
      5   | magie de la terre
      6   | magie de l'eau
      7   | magie du feu
      8   | magie de l'air
    ....
    Tout comme tu pourrais avoir une liste de type d'armes proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    id    |   nom
    =================
      1   | arme a une main
      2   | arme a deux main
      3   | arme de jet
      4   | arme contondante
      5   | arme perforante
      6   | arme tranchante
    ...
    ( tu remarquera que "petite epee rouillee" est à la fois 1(arme à une main) et 6 (arme tranchante) )

    Et, si l'on y regarde bien, une arme et une potion agirons (peu ou prou) de la même manière : en modifiant la valeur d'une caractéristique bien particulière de l'entité qui en fait les frais.

    Ainsi, nous pourrions avoir un composant "modifieur de caracteristique" qui ferait le lien entre les entités et les composants "de base", sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct Modifier{
        size_t id;               // l'identifiant unique
        size_t related_to;      // l'entité dont on parle
        size_t caracteristique; // que modifie-t-on
        int difference;         // Dans quelle ordre de grandeur
    }
    tu pourras donc définir ce qui agit sur quelle caractéristique sous la forme d'un table qui pourrait ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    id    | related | caracteristique | difference
    =================================
      1   |     4   |    1            | 50 // petite potion de vie (4) ajoute 50 à la vie (1)
      2   |     5   |    2            | 50 // petite potion de mana(5) ajoute 50 à la mana (2)
      3   |     1   |    1            | -3  // petite épée rouillée (1) retire 3 à la vie (1)
    ...
    Et bien sur, ce ne sont que des exemples... Des composants de base pourraient aussi être du type "ressources" (comprends :or, bois, minerai, ...) ou autre... Seule ton imagination sera ta limite

    Une fois que tu commence à avoir une idée précise des composants que tu utilises (ceux que j'ai mis en exemple ne sont pas forcément adaptés à tes besoins propres ) tu peux commencer à réfléchir à comment les utiliser et, surtout, à "qui en a besoin"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  19. #39
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Merci pour ta réponse et toutes les explications

    Si je te suis bien, le EntityHolder est une sorte de "livret" qui contient toutes les entitys qui tournent dans le jeu. Ce "livret" pourrait être utilisable par des systems pour chercher les entitys correspondantes a leurs attentes. ( ex, le système builder va avoir besoin de "worker", il le demande au EntityHolder de lui envoyer tous les "worker" et celui ci lui renverra les id 451, 54987, 145 et 44 ). Le System fera ensuite son boulot avec sous la main les bons IDs.

    Pour rebondir sur mon exemple précédent avec la sélection, on pourrait décider de rajouter une fonction "getSelected" dans le "EntityHolder", qui renverrait une série d'ID, tous ceux qui sont sélectionnés par le joueur, ( ajouté ici, dans le vector par S_Control ).
    ( si je te suis toujours ). A moins que tout ce que je viens de dire risque d’alourdir une class qui se doit d'être minimaliste pour des raisons de perfs ( mais je verrais pas trop pourquoi ) ?

    Tout composant a forcément un identifiant unique. Les composant "généraux" pourraient d'ailleurs se limiter à cet identifiant unique, mais on peut (comme on l'a fait pour les entités) envisager de rajouter une chaine de caractères qui les nommes.

    Les composants "spécifiques" nécessiteront en plus une "clé étrangère"
    Et ceci reviendrais au finale à traduire un std::string par un nombre ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    id    | related | caracteristique | difference
    =================================
      1   |     4   |    1            | 50 // petite potion de vie (4) ajoute 50 à la vie (1)
      2   |     5   |    2            | 50 // petite potion de mana(5) ajoute 50 à la mana (2)
      3   |     1   |    1            | -3  // petite épée rouillée (1) retire 3 à la vie (1)
    ...
    dans cet exemple, la clef caracteristique 1 ne pourrait représenter que "petite potion de vie"
    en revanche, il pourrait y avoir une clef armement 1 qui elle, serait un "petit couteau" ?


    @Iradrille
    Çà permet d'avoir des composants triés correctement pour les systèmes, et de multithreader facilement les systèmes. (Cas le plus simple : 1 thread par système).
    J'avais un peu peur de ca...Mais cela veut-il dire que pour pratiquer le ECS, le multi-thread est indispensable ?

    ps: que pensez vous du nomage que j'ai présenté plus haut ( pour les struct et les class ) ?

    S_MySystem // pour les System ( ex S_Control , S_Physics )
    C_MyComponent // pour les composants ( ex C_Position, C_SelectionZone )
    U_MyUnique // pour des SystemComponent ( ex U_Draw, qui est a la fois un Composant et System )

    ( et ici pour des composants individuels )
    I_MyIndividual // pour des vector qui contiennent des Entités particulières ( ex I_Selected , I_Magic, I_Building ) ( ceux-ci seraient contenu dans le Holder ?

  20. #40
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    @Koala, J'ai vraiment l'impression que tu pousses l'aspect relationnel trop loin.

    Dans ton dernier post tu m'as un peu surpris en parlant de std::set pour stocker des composants, ou en disant que la description d'une entité ne changeait jamais.

    Mais je comprend mieux (je crois ), tu pousses le concept à l’extrême pour au final avoir un jeu fait exclusivement de requêtes SQL (que ce soit en utilisant un SGBDR existant ou en recréant un) ?

    J'ai par exemple du mal à voir pourquoi un id et une clef étrangère sont présentes ici
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct Modifier{
        size_t id;               // l'identifiant unique
        size_t related_to;      // l'entité dont on parle
        size_t caracteristique; // que modifie-t-on
        int difference;         // Dans quelle ordre de grandeur
    }
    Dans une DB, oui ça sera stocké comme ça, mais en à-t-on besoin ici ?
    Un système travaille sur un tuple de composants sans se préoccuper de l'entité à laquelle ces composants appartiennent non ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    enum Carac {
        CARAC_A,
        CARAC_B,
        CARAC_C
    };
     
    struct Modifier{
        Mask mask = 1<< 0;
        Carac caracteristique; // que modifie-t-on
        int difference;         // Dans quelle ordre de grandeur
    };
     
    struct HasCarac {
        Mask mask = 1<< 0;
        int caracteristiques[3];
    };
     
    std::unordered_map<Entity, Modifiers> modifiers;
    std::unordered_map<Entity, HasCarac> caracteristiques;
     
    struct SystemModifier {
        Mask mask = HasCarac::mask | Modifier::mask;
        void update() {
            for(auto e: entities) {
                if(masks[e] & mask == mask) {
                    Modifier &m = modifiers[e];
                    HasCarac &hc = caracteristiques[e];
     
                    hc.caracteristiques[m.caracteristique] += m.difference;
                }
            }
        }
    };
    Citation Envoyé par mazertys17 Voir le message
    J'avais un peu peur de ca...Mais cela veut-il dire que pour pratiquer le ECS, le multi-thread est indispensable ?
    Indispensable non, bien sur que non. Mais si les systèmes sont indépendants c'est très simple à multi-threader (et dans ce cas, pourquoi se priver ?).

Discussions similaires

  1. Conception et programmation orientées objet
    Par forum dans le forum Livres
    Réponses: 2
    Dernier message: 31/08/2018, 16h44
  2. Réponses: 0
    Dernier message: 15/07/2014, 21h31
  3. Réponses: 0
    Dernier message: 15/07/2014, 21h31
  4. concepts de programmation en couches
    Par solitude dans le forum Windows Forms
    Réponses: 3
    Dernier message: 07/11/2008, 14h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo