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. #41
    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
    Je me demande si maintenir des std::vector triés de composants est une solution valable

    Par exemple si on à 4 composants 'A', 'B', 'C' et 'D' et 9 entités.


    L'entité 1 possède les composants : A, B et C.
    L'entité 2 possède les composants : A, B, C et D.
    Etc...

    Avec des std::vector non triés, c'est valable que pour les composants utilisés par quasi toutes les entités (C par exemple).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // on a
    std::vector<A> as;
    std::vector<B> bs;
    std::vector<C> cs;
    std::vector<D> ds;
     
    as[0] = composant de l'entité 1
    as[1] = composant de l'entité 2
    as[2] = vide
    as[3] = composant de l'entité 4
    // etc...
    Si on a 5 systèmes :
    - 1 qui utilise les composants A, B et C
    - 1 qui utilise les composants A, B, C et D
    - 1 qui utilise les composants A, C et D
    - 1 qui utilise les composants B et C
    - 1 qui utilise les composants B et D

    Une fois trié :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // on a
    std::vector<A> as;
    std::vector<B> bs;
    std::vector<C> cs;
    std::vector<D> ds;
     
    as[0] = composant de l'entité 2
    as[1] = composant de l'entité 5
    as[2] = composant de l'entité 1
    as[3] = composant de l'entité 4
    as[4] = vide
    // etc...
    En insérant les composants, on garde les vector triés, et on peut garder la trace du nombre d'entités qui intéresse chaque système ainsi que l'endroit dans les vector où ces éléments sont placés.

    Si on ajoute les composants C et D à l'entité 4, les vecteurs deviennent :


    Pas mal de changements, ce qui laisse penser que les ajouts / suppressions de composants seront couteuses. Mais la plus grande partie du temps sera passée à itérer et modifier ces composants, l'ajout / suppression devraient être beaucoup plus rare. Si le fait de les avoir triés augmente la vitesse de traitement, ça peut être rentable.

    Mais j'y vois 2 problèmes :
    - ça à l'air non trivial à trier.
    - on est obliger de "couper" ces suites de composants de temps en temps.

    Par exemple si on ajoute une 10eme entité, possédant les composants B et D.


    Y a-t-il moyen de savoir en combien de morceaux maximum une suite de composants sera coupée ?
    Une idée de la complexité du tri ?

    Ça vous semble utilisable ?

  2. #42
    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
    Je ne sais pas si j'ai bien compris, mais en gros, l'idée serait de rajouter pour chaque entité tous les composants, même s'ils sont vide ?en triant les ID, aurait ainsi accès directement a chaque composant d'une entité?
    ex:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
     
    void System::doSomethingTo ( size_t entity )
    {
       std::vector < Composant > :: iterator it = _composant.begin() + entity ;
      it -> setBidul( truc ) ; //on sait a l'avance quel entité il s'agit ?
     
    }
    Si c'est bien ca, ca m'a l'air en effet rentable. Rien que dans mon exemple "Control", j'étais confronté au fait de devoir faire un tour d'ittérateur2 pour chaque ittérateur1, ce qui est une perte, et d'autant plus qu'il y a de composants a utiliser.
    Le seul petit pb, je suppose, c'est qu'il faut rajouter quelque chose qui dit que le composant est vide pour pas que le system tente de l'utiliser ? mais bon ce devrait pas être grand chose.

    Juste pour confirmation, dans le concept d'ECS, faut il que bien que chaque composant soit minimaliste, même si ca implique d'avoir plus recourt à des ittérateurs ?


    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

    I_MyIndividual//pour des vector ou autres qui ne contiennet que des ID particuliers ( exemple I_Worker , I_Warrior, I_LifePotion etc...)

  3. #43
    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
    Je ne sais pas si j'ai bien compris, mais en gros, l'idée serait de rajouter pour chaque entité tous les composants, même s'ils sont vide ?en triant les ID, aurait ainsi accès directement a chaque composant d'une entité?
    Le but est que si un système utilise les composants A et B, on lui dise "il y à 3 entités qui correspondent à tes besoins, elles sont placées à la suite les unes des autres à partir de l'indice 1". Le but est d'améliorer la localité des données.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct System {
       std::vector<A>& as;
       std::vector<B>& bs;
       void update() {
          // 3 entités à traiter à partir de l'indice 1
          for(size_t idx = 1; idx < 4; ++idx) {
             // utilisation de as[idx] et de bs[idx]
          }
       }
    };
    Pas besoin de donner tous les composants à chaque entité.

    Pour le nommage, perso j'aime pas le mélange de CamelCase et d'underscore, donc je partirais sur SMySystem, CMyComponent, pour les conteneurs d'id probablement EntityVec, EntityMap, etc..
    Mais tant que tu respectes ta norme de nommage partout, tout est valide.

    Par contre une classe qui est à la fois un Composant et un System, ça sent l'embrouille.

  4. #44
    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 une classe qui est à la fois un Composant et un System, ça sent l'embrouille.
    C'est pourtant nécessaire pour le cas de l'affichage, par ex ( enfin tout le monde s'accordait là dessus il me semble ) ? qui est en fait un module a part, et ne fonctionne pas avec ECS...

    Le but est que si un système utilise les composants A et B, on lui dise "il y à 3 entités qui correspondent à tes besoins, elles sont placées à la suite les unes des autres à partir de l'indice 1". Le but est d'améliorer la localité des données.
    En somme ce serait trier les composants pour leur utilisation spécifique par des Systems ?

  5. #45
    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
    C'est pourtant nécessaire pour le cas de l'affichage, par ex ( enfin tout le monde s'accordait là dessus il me semble ) ? qui est en fait un module a part, et ne fonctionne pas avec ECS...
    Si c'est a part (comme pour l'affichage par exemple), alors il n'y à plus de notions de composants ou de systèmes. Tu auras une (ou plusieurs) classes, au sens "traditionnel" du terme (POO).



    Citation Envoyé par mazertys17 Voir le message
    En somme ce serait trier les composants pour leur utilisation spécifique par des Systems ?
    Exactement.

  6. #46
    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 pour les conseils, je vais donc nommer que les elements ECS en commencant par une maj spécial.

    Pour ton idée, c'est probablement intéressant surtout dans le cas (comme un STR) ou de nombreuses unités sont similaires ( donc devraient en effet se trouver rangés côte a côte vu qu'elles vont avoir les même composants et les mêmes traitements, et ce quelque soit leur ordre d'apparition dans le jeu ). Donc une idée a garder en tête pour la conception. Merci

    Sinon, selon vous, qu'est ce qui est plus efficace, pour une recherche d'un id spécifique ,entre un std::unordered_map qui aurait l'id et e composant ou un vector avec une surcharge d'opérateur pour le trouver avec un find_if ?
    ( en fait je trouve le std::map bien pratique, et je vois pas trop l'intérêt de mettre un vector et de s'embêter a créer des surcharges et des find_if, sauf si ca offre a coup sur des meilleurs perfs ? )

  7. #47
    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
    Toujours dans la conception, voici pour l'instant les fondations :

    Le main :

    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
     
     
    int main()
    {
        std::vector < sf::VideoMode > resolution ;
        resolution = sf::VideoMode::getFullscreenModes() ;
        std::vector < sf::VideoMode > :: iterator itVideo = resolution.begin() ;
        int resolutionX = itVideo -> width ;
        int resolutionY = itVideo -> height ;
        bool forceRefresh ( false );
     
        sf::RenderWindow window ( sf::VideoMode ( resolutionX , resolutionY ) , "BigBangElement" , sf::Style::Fullscreen ) ;
        sf::Event _event;
     
        Data data ( window , forceRefresh );
     
        Resources                              resources ;
        Engine                                 engine ( data ) ;
        Builder                                builder( resources , data ,
                                                        engine._graphics,
                                                        engine.C_position, 
                                                        engine.C_selectionZone ) ;
        Game                                   game( engine , builder ) ; // le Builder prend dans le constructeur la ref de
                                                                                          //tous les containers de composant
     
     
        engine.setBuilder( &builder ) ; //oui, c'est malheuresement un pointeur...Peût être l'exeption qui confirme la règle ?
     
        sf::Event                              event ;
     
        game.load() ;
     
        sf::Clock clock , delatClock ;
        sf::Time elapsed , deltaTime ;
     
        while ( window.isOpen() )
        {
            while (window.pollEvent ( _event ) )
            {
                if ( _event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed ( sf::Keyboard::Escape ) )
                {
                    window.close() ;
                }
            }
            deltaTime = delatClock.getElapsedTime();
            delatClock.restart();
            elapsed = clock.getElapsedTime();
     
            game.play() ;
            forceRefresh = false ;
            if(elapsed.asMicroseconds() >=  62500 )//31250)
            {
                clock.restart();
                forceRefresh = true ;
            }
        }
        return 0;
    }
    Pour résumer : ici les 4 class fondamentales du jeu ( l'Editeur sera a rajouter ) :

    Game : lit les fichiers depuis le disque pour les faire construire par "Builder" (s'occupera probablement du menu et de l'editeur )
    Resource : contient toutes les resources ( graphics, sound etc... )
    Engine : fait tourner les Systems et possède tous les containers de composants et les IDs. A acces au "Builder" pour construire des objets en cours de route
    Builder : a accès à tous les containers de l'Engine ( mis en référence dans le constructeur ). Il contient une série de composants model associé à chaque Objet du jeu, il charge ainsi depuis le disque dur tous les composants nécessaire a chaque objet, et se contentera de les copier dans les bon containers dès qu'un objet déjà chargé est appelé.

    n'hésitez pas à me dire svp si vous trouvez cela bancal, surtout à propos de la relation entre Builder et Engine :
    Le but étant que l'engine puisse avoir acces au "Builder" pour créer en temps réel de nouveaux objets, et que "Builder" ait accès aux containers de composants pour les ajouter et gérer lui même la diversité des composants de chaque objet.

    Le problème pour l'instant, c'est qu'en partant sur ce principe, j'ai un pointeur, celui de "builder" pour l'engine.

  8. #48
    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
    Et ceci reviendrais au finale à traduire un std::string par un nombre ?
    Si tu entend par là calculer -- d'une manière ou d'une autre -- une somme de controle (hash key, md5 ou autre), oui... on peut dire que cela reviendrait à traduire la chaine de caractères en nombre..

    Si ce n'est que les sommes de contrôle ont la fâcheuse manie de permettre la "collision" à un moment ou à un autre. Comprend par là le fait que l'on ne peut ignorer le fait que deux chaines de caractères différentes peuvent générer la même somme de contrôle. Et, comme en plus, on n'a pas vraiment besoin de somme de contrôle pour l'instant, on peut "simplement" faire en sorte que l'identifiant de chaque entité soit représenté par ce que l'on appelle en base de données "une clé primaire auto incrémentée". Dans le code, cela représente simplement une valeur numérique entière (de préférence non signée) dont la valeur est incrémentée à chaque fois qu'une entité est rajoutée à la liste (c'est la raison pour laquelle j'ai utilisé size()+1 pour définir cet identifiant
    dans cet exemple, la clef caracteristique 1 ne pourrait représenter que "petite potion de vie"
    Oui,... Mais non : l'id 1 de Modifier ne pourra, effectivement, jamais représenter qu'une seule et unique chose, dans le cas présent. Mais la chose en question dépendra exclusivement de l'ordre dans lequel j'aurai rajouté mes composants de type Modifier. Si j'avais décidé de commencer par les modifier de "petite épée rouillee" (c'est à dire par une valeur 1 pour related_to), le composant Modifier dont l'id aurait été égal à 1 aurait fait référence... à la petite épée rouillée...
    en revanche, il pourrait y avoir une clef armement 1 qui elle, serait un "petit couteau" ?
    Sois très attentif aux termes que tu utilises, autrement, nous n'en sortirons pas . Ce que l'on appelle généralement une clé correspond à un attribut (ici, une des données membres de nos structure) qui sera utilisé pour définir un ordre de tri. Si nous voulions placer nos structures dans une std::map, la clé correspondrait à la donnée membre que nous utiliserions en premier pour l'insertion

    Les identifiants permettent quant à eu d'identifier chaque élément de "manière unique et non ambigüe" au sein de la collection dans laquelle se trouve l'élément. Ces identifiants sont des "clés primaires" (pour reprendre un terme propre aux base de données) au niveau de la collection dans laquelle il se trouve, dans le sens où c'est la manière la plus rapide d'accéder à un élément donné mais peuvent servir de "clé étrangère" (toujours en reprenant un terme propre aux bases de données ) dans "d'autres collections" dans le sens où cet identifiant (qui sera représenté par le membre related_to) de récupérer tous les éléments qui font, justement, référence à un élément particuier.

    Citation Envoyé par Iradrille Voir le message
    @Koala, J'ai vraiment l'impression que tu pousses l'aspect relationnel trop loin.
    Ce n'est pas du tout exclu... A vrai dire, j'ai pondu cet exemple en même temps que je l'écrivais. Je suis donc peut être parti de composants "quelques peu trop limités", (un numéro d'identification unique et une chaine de caractères que j'ai hésité à n'utiliser qu'en débug, ca fait peut etre effectivement très peu ). Mais ca restait cohérent avec les explications
    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.
    Je crois que tout dépend finalement du type de composants que tu veux créer... Si c'est un composant comme "TypeDarme"("épée à une main","épée à deux main", "couteau de lancer","sort de magie de terre","arc" ou, à défaut, quelque chose qui se rapprocherait de ces descriptions), oui, chaque composant peut s'avérer constant et peut donc être placé dans un set.

    De même, lorsque je dis que la description d'une entité ne change jamais, c'est pour la simple et bonne raison que, a priori, aucune entité ne sera jamais "sortie" de la "base de données". Et dés qu'on a défini la description de l'entité N°1 comme étant une "petite épée rouillée servant à l'entrainement des novices", il n'y a, effectivement, que peu de chances que cette description ne doive être changée

    C'était surement un raccourci honteux que de faire cette assertion. C'est peut-être un pari risqué à faire sur un projet réel, mais cela correspond très clairement à l'optique dans lequel il faut envisager les choses
    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) ?
    Non, je ne veux pas recréer un modèle SQL et, non, je n'envisage absolument pas d'intégrer un SGBDR quelconque dans la logique de jeu.

    Par contre, je n'exclus pas la possibilité que certaines des informations puissent, effectivement, être sauvegardées sous la forme d'une BDD (SQL ou non ) sur le serveur de jeu.
    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 ?
    A dire vrai, je n'en ai aucune idée... Comme je te l'ai dit, j'ai réfléchi à cet exemple en meme temps que je l'écrivais...

    Mais, comme je ne peux pas garantir que l'on n'aura jamais besoin de id, et que je subodore surtout que l'on pourrait en avoir besoin quelque part, je l'ai mis par "sécurité" (après tout, si on a une "table" "ModifierDescription", l'id du modifier prend du sens pour créer la relation, tu ne penses pas )
    Un système travaille sur un tuple de composants sans se préoccuper de l'entité à laquelle ces composants appartiennent non ?
    A priori, oui... Mais on ne peut pas en faire une règle absolue, ne serait-ce que parce qu'il y a des composants qui sont très fortement corrélés à certaines entités particulières ou, pour être plus précis, que nous voudrons peut-être sélectionner en fonction de l'entité à laquelle ils correspondent.

    Autrement, je ne vois pas vraiment où tu pourrais faire le lien entre l'entité et le composant, ne serait-ce que pour permettre l'affichage des caractéristique de l'arme que tu as dans ton inventaire (l'arme étant représentée dans ton inventaire par l'identifiant d'une entité, comment pourrais tu déterminer "ce qui doit être affiché" si tu n'as pas la possibilité de retrouver les composants associés à cette entités )

    Est-ce toujours nécessaire Surement pas (et c'est sans doute justement quand l'id de l'entité associée n'est pas nécessaire que l'id d'un composant particulier le devient ) Est-ce utile Dans beaucoup de cas, très certainement...

    Ai-je fais des monstruosités dans mes explications Je n'ai, très certainement rien fait de foncièrement faux. Mais je ne prétend pas non plus avoir fait les chose "au mieux de ce qu'elle auraient pu être", ne serait-ce que parce que mon exemple a été écrit et réfléchi "à la volée", et que c'est une très mauvaise manière de travailler
    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

  9. #49
    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 tes explications.

    Je n'ai pas encore vraiment tout saisi de ce concept, mais je vais relire au fur et à mesure pour essayer de bien comprendre. Cependant, ce n'est pour moi, pas la priorité. Pour l'instant il me bâtir les fondations de mon programme. Je vois que le grand avantage de l'ECS est qu'il offre une très grande flexibilité, a condition probablement, que les fondations soient solides.

    J'aimerais s.v.p avoir des avis sur mon main, par exemple, exposé plus haut. J'ai pris le parti de créer un objet "Builder", accessible depuis le Game et l'Engine, qui a lui même accès à tous les containers de composants de l'Engine. Je pars du principe que l'Engine, à chaque fin de boucle, utilise une liste d'objet à construire faîchement établie, et les demande à "Builder" qui les construits et les insert directement dans les containers...

    Evidement, je sens que cette solution n'est pas forcément la meilleur, puisque cela implique de mettre le "Builder" en pointeur dans l'Engine, ( chose pas forcément dommageable vu que ni le Builder, ni l'Engine ne seront dupliqué, instancié, ou détruit ). Et probablement que cela est dangereux, particulièrement si un jour je décide d'utiliser le "multi-thread" ( dont j'ignore tout pour l'instant ) : si j'insert un élément depuis un élément externe à l'Engine alors qu'un System est en train de le parcourir...plantage probable. Cependant, si je prends le partie que c'est l'Engine qui gère la demande de Build, cela reste contrôlable, puisque l'Engine gère également les Systems...

    Le but de l'opération est d'anticiper le fait que l'Engine puisse créer dynamiquement des objets ( chose on ne peut plus courante dans les STR ). Il me faudra donc une class qui ait mémorisé la spécificité de tous les composants associés à un objet et qui puisse les dupliquer rapidement. Laisser ça a l'Engine me paraît cafouilleux, donc une class spécialisé ( "Builder" ) serait une bonne chose. Mais pour éviter de mettre des accesseurs ou une assommante quantité de fonctions, je me dit qu'envoyer directement les références des containers est une solution ?

    Merci si vous pouvez m'aider/proposer une meilleurs solution si jamais vous en avez une

  10. #50
    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 koala01 Voir le message
    De même, lorsque je dis que la description d'une entité ne change jamais, c'est pour la simple et bonne raison que, a priori, aucune entité ne sera jamais "sortie" de la "base de données". Et dés qu'on a défini la description de l'entité N°1 comme étant une "petite épée rouillée servant à l'entrainement des novices", il n'y a, effectivement, que peu de chances que cette description ne doive être changée

    C'était surement un raccourci honteux que de faire cette assertion. C'est peut-être un pari risqué à faire sur un projet réel, mais cela correspond très clairement à l'optique dans lequel il faut envisager les choses
    En début de partie solo dans un RTS, il peut être intéressant de ne pas donner le contrôle de la caméra au joueur immédiatement, et d'avoir un script qui la controle pour présenter les points d'intérêts avant de donner la main au joueur.
    Dans ce cas la caméra (qui serait une entité ?) aurait un composant "controlé par un script", qu'elle perdrait par la suite. Puis elle gagnerait un composant "controlé par le joueur".

    Il peut aussi y avoir des unités qui changent de propriétaire : prise du contrôle d'unités ennemies (ou amies).

    (Mais je comprend bien que c'est une idée rapide, j'avais juste cette idée en tête)

    Citation Envoyé par koala01 Voir le message
    A priori, oui... Mais on ne peut pas en faire une règle absolue, ne serait-ce que parce qu'il y a des composants qui sont très fortement corrélés à certaines entités particulières ou, pour être plus précis, que nous voudrons peut-être sélectionner en fonction de l'entité à laquelle ils correspondent.
    Je pense que c'est une indication d'un manque de composants. Si on veut sélectionner les entités qui ont le composant A et un id pair, il est surement préférable d'ajouter un composant "aUnIdPair" plutôt que de garder les entités qui ont un id pair parmi celle qui ont le composant A.
    Mais il y à peut être des cas assez compliqués pour mériter des exceptions.

    Citation Envoyé par koala01 Voir le message
    Mais, comme je ne peux pas garantir que l'on n'aura jamais besoin de id, et que je subodore surtout que l'on pourrait en avoir besoin quelque part, je l'ai mis par "sécurité" (après tout, si on a une "table" "ModifierDescription", l'id du modifier prend du sens pour créer la relation, tu ne penses pas )

    Autrement, je ne vois pas vraiment où tu pourrais faire le lien entre l'entité et le composant, ne serait-ce que pour permettre l'affichage des caractéristique de l'arme que tu as dans ton inventaire (l'arme étant représentée dans ton inventaire par l'identifiant d'une entité, comment pourrais tu déterminer "ce qui doit être affiché" si tu n'as pas la possibilité de retrouver les composants associés à cette entités )
    Je partais du principe que le lien entre entité et composant était stocké en dehors du composant.

    Pour l'id du composant, je le lierais probablement à l'entité, mais je le stockerais en dehors. (Mais ça sous entend qu'une entité ne peut pas avoir plusieurs exemplaire d'un même composant).

    Je ferais tout pour avoir une classe Entité contenant seulement un id, et des composants qui ne contiennent que des données utiles aux calculs.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct Component { };
    std::map<EntityId, Component> components;
    std::map<ComponentId, EntityId> componentIds; // les map c'est vraiment pas l'idéal.
     
    // vs
    struct Component {
       EntityId e;
       ComponentId id
    };
    std::set<Component> components; // avec le comparateur qui va bien
    Citation Envoyé par koala01 Voir le message
    Ai-je fais des monstruosités dans mes explications
    Le sujet m'intéresse, mais je n'ai aucune expérience pratique : jamais codé d'ECS. Je pose simplement des questions pour mieux comprendre, n'y vois aucune critique.

  11. #51
    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
    En fait, ne y réfléchissant après avoir répondu cette nuit, je me suis rendu compte que le gros problème était sans doute le manque de précision du terme entité...

    Car, si on y réfléchit bien, il existe deux types très corrélés et pourtant distincts d'entités :

    D'un coté, on a les entités que je désignerai sous le terme de "modèle", à défaut de meilleur terme. Ce type d'entité représenterait "n'importe quel élément du jeu que l'on peut retrouver à un moment ou à un autre dans le jeu" ou, si tu préfères, les entités telles que décrites par le game disigner, le graphiste et les autres intervenants.

    Et, de l'autre coté, tu aurais les entités -- faut il les appeler réelles "physiques" ou "concrètes" " -- qui se trouvent, à un instant T du jeu, à une position bien précise dans le jeu (un avatar dans le jeu, sa main droite, son inventaire ou la maison du cordonnier de tel village sont autant de positions bien précises), qui apparaissent, subissent des modifications, cassent parfois, sont parfois réparées et disparaissent lorsqu'elles sont revendues ou après avoir "trainé à terre" pendant un certain temps.

    Ces entités sont -- forcément -- des instances particulières des entités "modèles", ce qui fait que l'on peut parfaitement avoir, comme "modèle", la petite épée rouillée qui a l'id 1 et des entités "réelles" dont les id sont 32 569, 78 523, 145 987 et 65 120 qui sont autant d'entités issu de l'entités modèle 1 mais dont certaines caractéristiques (la "résistance", par exemple) ont été modifiées au fil du temps, de leur utilisation dans le jeu et du soin qu'y a apporté le joueur

    Donc, oui, il y a forcément des composants qui risquent d'être modifiables (la résitstance de l'entité 32 569 qui est la petite épée rouillée qui appartient à l'avatar 523 698) mais il y a aussi des composants qui ne seront jamais modifiés (sauf catastrophe et changement d'avis de la part de l'équipe de développement) : la résistance maximale de l'entité modèle 1 (qui a servi pour créer l'entité 32 569 est et sera toujours de X ou de Y (20 40 c'est au game designer de décider )

    Mais ce qui importe, c'est que, quoi qu'il arrive, une entité n'est jamais que l'identifiant qui permet de la sélectionner et d'y faire référence de manière aisée. et qu'il n'y a absolument aucune raison pour que l'objet identifié soit modifié "en cours de route" : l'entité 32 569 est et restera une petite épée rouillée tant que son propriétaire n'aura pas décidé de s'en débarasser . Il n'est pas question qu'elle se transforme en "Super épée du chasseur de dragon", une épée rare de niveaux 32 avec des caractéristiques impressionnantes "comme cela, par magie" .

    Après, on peut se poser la question de savoir ce que l'on fait des identifiants des entités réelles qui cessent d'exister : quand le propriétaire de l'entité 32 569 décide de se débarasser de cette petite épée rouillée, que devient cet identifiant est-il "récupérable" pour désigner une entité peut être totalement différente Est-il purement et simplement "supprimé du jeu", mais jamais réutilisé

    La réponse la plus sensée serait sans doute que cet identifiant ne soit jamais réutilisé, pour une question de facilité. Mais la réponse la plus utile pour éviter de se retrouver dans une situation dans laquelle on ne pourrait plus créer de nouvelles entités "réelles" serait qu'il soit justement réutilisable pour représenter "la prochaine entité qui sera créée".

    Quoi qu'il en soit, je reste convaincu que, d'une manière ou d'une autre, il faut forcément faire le lien, à un moment donné, entre les différents composants et l'entité, ne serait-ce que parce que les différents composants sont "l'essence même" des entités auxquelles ils se rapportent
    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

  12. #52
    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
    Quoi qu'il en soit, je reste convaincu que, d'une manière ou d'une autre, il faut forcément faire le lien, à un moment donné, entre les différents composants et l'entité, ne serait-ce que parce que les différents composants sont "l'essence même" des entités auxquelles ils se rapportent
    Pour résoudre au problème dont tu parles, je suis parti sur ce concept :

    -une class "Builder" contient une association de chaque "objet neuf" du jeu a tous les composants qu'il détient :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     
        std::unordered_map < std::string , std::vector<std::string> >        _componentByID;
    un "worker" par exemple serait associé à "C_Position", "C_Selection" , "C_State", "C_BuildBuilding"
    un "warrior" serait associé à "C_Position", "C_Selection", "C_State", "C_Attack" etc...

    Toutes ces associations sont construites soit "à la main" dans le constructeur de "Builder", ou mieu, pendant le chargement de chaque objet depuis le disque dur : quand un objet neuf se charge, tous les composants chargés s'écrivent au fur et à mesure dans ce container.

    On aurait par la suite, pour chaque composant du jeu, un container associé comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
     
    std::map < std::string , C_Component >       C_componentSource ;
    std::map < std::string , C_Graphics >      C_graphicsSource ;
    etc...
    ex : C_componentSource.emplace( "Worker" , C_componentModel ) ; // chargement du "worker" dans ce container source
      C_graphicsSources.emplace( "Worker" , C_graphicsModel ) ;
    et donc, si dans l'Engine, une commande dit "Build a Worker", alors le Builder va :
    -chercher tous les composants que possède l'objet avec "_componentByID", puis copier chacun des composants associés à l'objets. ( et directement dans les bons containers de l'Engine dans mon cas ).

    voilà l'idée. ( a moitié déjà mise en place ).
    Bien sur ca implique beaucoup de code...

    Donc dans mon cas il y aurait des "ID" sources qui sont contenus dans Builder et ne sont pas utilisables, et les ID du jeux, qui elles sont jouables, copiés à l'origine depuis les ID sources.

    ps : personne ne m'a dit ce qu'il pensait de mon main / fondations de mon programme ?

  13. #53
    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
    Bonjour ( re )

    Vu que personne ne m'a répondu sur la cohérence des fondations de mon programme , je me suis dit que c'était plutôt bon signe car s'il y avait une grosse erreur , un noble programmeur me l'aurait sans doute fait remarqué , donc j'ai poursuivit en allant dans ce sens.

    Maintenant je suis en train de m'attaquer aux requêtes, et je pars sur le principe suivant :

    les Systems ont accès également à un std::Vector < Request >, dans lequel ils peuvent écrire des requetes et en lire :

    ex : implémentation d'icônes HUD addapté à chaque objet, lorsque celui-ci est contrôlé par le joueur

    un objet est sélectionné, depuis le System "S_Control": une Request est alors ajouté :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
           _request.push_back( Request ( id , "buildHud" ) ; //quand un objet est sélectionné, une requete "buildHud" est ajouté
    Lorsque le System "S_Hud" se met en route, il étudie le std::vector < Request > et voie que "buildHud" le concerne.
    Il ajoute alors au container ( "_buildListing" ) contenue dans l'Engine, tous les noms des icônes qui concerne l'id, et leurs positions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
           _buildListing.insert( std::pair < std::string , sf::Vector2f > ( it2 , sf::Vector2f( position.x , position.y ) ) ) ;
           //it2 est le nom de l'icone
    Toutes ses interfaces HUD sont alors construites par l'Engine à la fin de l'update :
    ( j'ai donc crée comme je l'ai dit plus haut, une liste d'objet a construire dans l'Engine qui est lut puis effacé à la fin de chaque update, et va utiliser l'objet "_builder", qui a mémorisé tous les objets utilisable du niveau et leurs composants associés, pouvant alors créer un nouvel id en recopiant toutes les donnés concernés dans tous les containers de composants de l'Engine )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
        for ( auto& it : _buildListing )
        {
            _builder -> build( it.first , it2.second.x ,  it2.second.y ) ;
        }
        _buildListing.clear() ;
    Si un id est désélectionné, alors le System "S_Control", enverra une requête "deselect", qui sera lu par le System "S_Hud", qui ajoutera une requête "cleanHud", qui sera lu par l'Engine, qui effaçera tous les ids concernés ( ici les icones ).

    Les premiers test sont concluant puisque cela fonctionne, mais bien évidement, le but est d'avoir encore une fois des fondations solides pour pouvoir enrichir toujours un peu plus les éléments du jeux en étant confronté au moins de problème possible...
    Merci si vous signalez quelque chose qui ne va pas / avez des commentaires / une meilleur facon de faire etc...

  14. #54
    Membre régulier
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Points : 105
    Points
    105
    Par défaut
    À tout hasard, tu peux test mon implémentation (pas terrible, j'utilise ça pour l'instant mais ça mérite des révisions) :

    https://github.com/JeanLouisMassei/E...omponentSystem

    Je bosse actuellement sur une refonte du truc. L'objectif :

    - Stocker tous les Components de toutes les Entity dans un seul vector, pour profiter de la contiguïté de la mémoire lorsque les System font leur boulot
    - Chaque System stocke uniquement l'index des Components qui "l'intéresse" dans un vector (les pointeurs c'est pas terrible, les déférencements prennent du temps à chaque boucle)
    "There should be no boundaries to human endeavor" - Hawking
    Retrouvez moi sur GitHub : https://github.com/JeanLouisMassei

  15. #55
    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.

    - Stocker tous les Components de toutes les Entity dans un seul vector, pour profiter de la contiguïté de la mémoire lorsque les System font leur boulot
    Ce qui me paraît bizarre avec cette technique, c'est qu'un System va devoir se coltiner des composants qui ne le concerne pas à un moment donné ou l'autre, or si tu assigne un System à différents types de composant répartis dans des containers appropriés, cela ne remet pas en cause la contiguïté de ceux ci, tant qu'ils sont identiques, ce qui est le cas en data driven ( si je ne dis pas de bêtises ). La perte de contiguïté ne se fait que lorsqu'on passe d'un container de composant à un autre, ce qui est minime ( sauf bien sur si on a presque autant de composants que de containers, ce qui me paraît pas intéressant , du moins pour le ECS ou même un STR ).
    Il me semble que le but du ECS justement est aussi d'éviter l'héritage en ce qui concerne les composants, permettant ainsi de pouvoir implémenter n'importe quel composant sur un id.

    - Chaque System stocke uniquement l'index des Components qui "l'intéresse" dans un vector (les pointeurs c'est pas terrible, les déférencements prennent du temps à chaque boucle)
    Oui, tout compte fait, je suppose que ça veut dire que les systems ne vont pas faire défiler systématiquement l'intégralité du vector, donc mon premier argument n'est pas valable, cependant, ca me paraît impliquer beaucoup de complications pour peut être pas grand chose : si tu ajoute un composant spécial, il va falloir le trier obligatoirement, pour que le système le trouve, et également "ré-indéxer" le System, et les Systems "contingent", enfin tous les Systems.
    Mais c'est surtout l'héritage qui me dérange avec cette idée.

  16. #56
    Membre régulier
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Points : 105
    Points
    105
    Par défaut
    Il y a effectivement une étape de réindexation des System dès qu'un Component est ajouté, ou supprimé. J'ai trouvé le moyen de faire ça en un temps N en fait (N=nombre de System). C'est une question de choix :

    - Soit tu passes beaucoup de temps à chaque boucle, parce que chaque System va sélectionner les Entity qui l'intéressent avant de procéder à sa logique, et effectivement, plus besoin de réindexation, et les ajout/suppression sont rapides.
    - Soit chaque System sait déjà sur quels Components il va passer (gain énorme de temps à chaque boucle) et les ajouts/suppressions sont un peu plus lents. Je suis parti de l'hypothèse qu'on ne fait pas des ajouts/suppressions à chaque boucle, mais des update, si

    Mais c'est surtout l'héritage qui me dérange avec cette idée.
    J'aimerais aussi pouvoir m'en passer, malheureusement je vois mal comment tu peux faire autrement. Dis tu ça pour les System ou pour les Component ?
    "There should be no boundaries to human endeavor" - Hawking
    Retrouvez moi sur GitHub : https://github.com/JeanLouisMassei

  17. #57
    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
    Soit chaque System sait déjà sur quels Components il va passer (gain énorme de temps à chaque boucle)
    Pourquoi gain énorme de temps ? Si on utilise un System pour un type de composants particulier, il se passe la même chose non ? le System parcourt tous les composants du container. La seule différence est lorsqu'il va avoir plusieurs types de composants à gérer, là il devra passer d'un container à l'autre. Mais je suppose qu'un System, même complexe, ne va utiliser au grand maximum que quelques dizaines de composants différents, donc quelques dizaines de containers, ce qui est peu comparé aux nombres de composants eux mêmes, qui seront plutôt de l'ordre de la centaine.

    - Soit tu passes beaucoup de temps à chaque boucle, parce que chaque System va sélectionner les Entity qui l'intéressent avant de procéder à sa logique, et effectivement, plus besoin de réindexation, et les ajout/suppression sont rapides.
    pourquoi sélectionner les Entity avant de procéder à sa logique ? Normalement, sauf s'il y a une requête particulière ( ce qui reporte le pb avec le cas d'un vector unique de composant ), le System va traiter tous les composants contenue dans le le container, car chacun d'entre eux est assigné à une entité, donc pas besoin de sélectionner chaque entity, mais simplement les faire défiler les unes après les autres ?

    J'aimerais aussi pouvoir m'en passer, malheureusement je vois mal comment tu peux faire autrement. Dis tu ça pour les System ou pour les Component ?
    pour les Components . Perso, j'utilise l'héritage pour les Systems, comme on me l'a conseillé : cela permet de les implémenter rapidement depuis l'Engine et d'avoir simplement une fonction update que sert à tout, par ex.
    Alors que les composants ne sont que des données, et le fait qu'ils soient le plus simple possible facilite leur utilisation il me semble.

  18. #58
    Membre régulier
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Points : 105
    Points
    105
    Par défaut
    Pourquoi gain énorme de temps ? Si on utilise un System pour un type de composants particulier, il se passe la même chose non ? le System parcourt tous les composants du container. La seule différence est lorsqu'il va avoir plusieurs types de composants à gérer, là il devra passer d'un container à l'autre. Mais je suppose qu'un System, même complexe, ne va utiliser au grand maximum que quelques dizaines de composants différents, donc quelques dizaines de containers, ce qui est peu comparé aux nombres de composants eux mêmes, qui seront plutôt de l'ordre de la centaine.
    (...)
    pourquoi sélectionner les Entity avant de procéder à sa logique ? Normalement, sauf s'il y a une requête particulière ( ce qui reporte le pb avec le cas d'un vector unique de composant ), le System va traiter tous les composants contenue dans le le container, car chacun d'entre eux est assigné à une entité, donc pas besoin de sélectionner chaque entity, mais simplement les faire défiler les unes après les autres ?
    Je crois qu'on se perd un peu dans la théorie. En fait j'ai vu pas mal d'ECS qui procédaient comme ça, et j'ai l'impression que c'est ce que tu veux faire :

    -Pour chaque System s
    --Pour chaque Entity e
    ---Si e a les Components qui intéressent s
    ----s met à jour les Components de e

    -> C'est pas terrible, on se coltine des boucles à travers toutes les Entity alors qu'elle ne sont pas toutes intéressantes pour tous les System, et en plus, à chaque itération, on teste si e a les Components qui intéressent le System.

    Il serait plus simple de faire :

    -Pour chaque System s
    --Passer sur les Component qui intéressent s (dont les indices sont déjà stockés)
    ---Les mettre à jour

    Pour les Components. Perso, j'utilise l'héritage pour les Systems, comme on me l'a conseillé : cela permet de les implémenter rapidement depuis l'Engine et d'avoir simplement une fonction update que sert à tout, par ex.
    Alors que les composants ne sont que des données, et le fait qu'ils soient le plus simple possible facilite leur utilisation il me semble.
    Indispensable en effet pour les System, pour les raisons que tu pointes. Pour ce qui est des Components, quelle solution proposes tu ?

    Je te propose d'aller voir cette discussion (vers les derniers messages)

    http://www.developpez.net/forums/d15...r-destructeur/
    "There should be no boundaries to human endeavor" - Hawking
    Retrouvez moi sur GitHub : https://github.com/JeanLouisMassei

  19. #59
    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
    -> C'est pas terrible, on se coltine des boucles à travers toutes les Entity alors qu'elle ne sont pas toutes intéressantes pour tous les System, et en plus, à chaque itération, on teste si e a les Components qui intéressent le System.
    Non, au pire, on se coltines quelques composants qui ne sont pas nécessaire, dans le seul cas ou plusieurs Systems se partagent les mêmes composants.

    J'ai du mal m'expliquer :

    prenons un exemple : un System va gérer gérer simplement le déplacement d'une entity. Il va alors avoir besoin , mettons, de 3 composants :

    -C_Position ( une simple structure avec x et y )
    -C_Destination ( une vector de structures x et y, au cas ou il y ai plusieurs destinations )
    -C_Speed ( la vitesse pour calculer la bonne distance a parcourir à chaque frame ).

    Le System en question, mettons S_Physics, aura donc accès aux 3 différents containers qui les contiennent dans l'Engine :

    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
     
     
    class S_Physics : public System
    {
    public :
     
        S_Physics( Data& data ,
                   std::map < size_t , sf::Vector2f >&  position ,
                   std::unordered_map < size_t , std::vector < sf::Vector2f > >& destination ,
                   std::unordered_map < size_t , double >& speed ) ;
        ~S_Physics() ;
     
        virtual void                                                    update() ;
     
     
    protected :
     
        Data&                                                            _data ;
     
        std::map < size_t , sf::Vector2f >&                              C_position ;
        std::unordered_map < size_t , std::vector < sf::Vector2f > >&    C_destination ;
        std::unordered_map < size_t , double >&                          C_speed ;
     
    };
    Et rien d'autre. Ainsi on pourrait avoir ceci dans l'update du System S_Physics :

    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
     
     
    void S_Physics::update()
    {
        for ( auto& it : C_destination )
        {
            if ( !it.second.empty() && _data.getTime() ) // si seulement l'objet à au moins une destination a faire
            {
                auto itP = C_position.find ( it.first ) ; // alors je cherche sa position
                auto itS = C_speed.find ( it.first ) ; // et sa vitesse 
     
                std::vector < sf::Vector2f >::iterator itDest = it.second.begin() ;
                if ( *itDest == itP -> second )
                {
                    it.second.clear() ; // s'il est précisement à la destination, alors on vide la liste des destinations..( le boulot n'est pas fini )
                }
                else
                {
                    float distance = sqrt ( pow ( itDest -> x - itP -> second.x , 2 ) + pow ( itDest -> y - itP -> second.y , 2 )) ;
                    float speedX = ( itS -> second / distance ) * ( itDest -> x - itP -> second.x ) ;
                    float speedY = ( itS -> second / distance ) * ( itDest -> y - itP -> second.y ) ;
                    if (  ( distance / itS -> second ) < 1 )
                    {
                        std::vector < sf::Vector2f >::iterator itE = it.second.begin() ;
                        it.second.erase ( itE ) ;
                    }
                    itP -> second.x += speedX ; //après avoir calculer la bonne vitesse a addapter à x et y, on déplace l'objet en modifiant sa position
                    itP -> second.y += speedY ;
     
                }
            }
        }
    }
    Dans cet exemple ( que j'utilise et qui fonctionne ), on a une boucle seulement sur les composants C_Destination, que ne concerne que quelques id.
    Imaginons que, dans le STR, j'ai un décor avec 15657 entity , des bâtiments 450 entity , et 5 unités, alors le System S_Physics bouclera continuellement sur 5 id avec 1 composants. Si un d'eux est amené à bouger, alors seulement, il cherchera les autres composants ( dont peut être C_position, qui fera certe partie des 15657 + 450 entity, si je n'ai pas pris le parti de faire des containers séparés pour éviter ca, mais C_speed concernera en revanche que 5 composants ).

    Voici donc la solution que je propose : créer un container par composant.

  20. #60
    Membre régulier
    Homme Profil pro
    Développeur du dimanche
    Inscrit en
    Février 2013
    Messages
    154
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur du dimanche

    Informations forums :
    Inscription : Février 2013
    Messages : 154
    Points : 105
    Points
    105
    Par défaut
    D'accord, on avait à peu près la même idée alors.

    Mais du coup, tes itérations se font sur des unordered_map, et les itérations sur des unordered_map sont très (très(très)) lentes. Voilà pourquoi il peut être intéressant d'utiliser des vectors pour stocker tes Components, fut-ce au prix de l'héritage.

    Mais je pense qu'il y a une erreur de conception dans ton code : les Components sont stockés dans les System ? Imaginons un System0 qui passe sur Position et Health, et un System1 qui passe sur Position et Name. Dans notre jeu, il y a une seul Entity qui a les Components Position,Health et Name. Concrètement, ça va se passer comment ? Les deux System devront avoir un Component en commun, Position. Comment gères tu ça ?
    "There should be no boundaries to human endeavor" - Hawking
    Retrouvez moi sur GitHub : https://github.com/JeanLouisMassei

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