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 :

ECS et Signaux / slots


Sujet :

C++

  1. #1
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut ECS et Signaux / slots
    Bonjour à tous,

    Depuis un petit moment je met en place un... "Pattern" si je puis dire concernant un ECS.
    Pour le moment, et grâce à l'aide du forum, j'ai pu mettre en place la partie "Entity" et "Component". Afin de pouvoir m'y retrouver dans tout ça, voici globalement les fonctionnalités du code :
    * Entity : pour moi, une entité est composée de (au moins) deux choses, une clé principale ainsi qu'un bitset faisant office de signature quant aux différents types de composant attachés à cette entité
    * EntityHolder : une classe permettant de stocker/récupérer/supprimer les signatures (bitsets) de chaque entité.
    * Component : cette classe/structure n'existe pas en tant que telle dans ce pattern, libre à l'utilisateur de créer les données de son choix (sous forme de struct dans la mesure du possible)
    * ComponentHolder : classe permettant de stocker/récupérer/supprimer les composants accompagnés d'une clé (Entité).
    * Driver : le nom de cette classe pourrait probablement être choisi plus judicieusement, mais cette dernière permet à l'utilisateur de "piloter" tout le petit monde listé ci-dessus. Cette classe donne alors accès à l'utilisateur, via des fonctions membres virtual la possibilité de Créer/supprimer une entité, Lier/Ajouter/supprimer des composants relativement à l'entité à laquelle ils appartiennent.
    * Utils : classe contenant des fonctions static utilitaires, permettant principalement de générer des clés uniques pour chaque entité, mais aussi des clés uniques pour chaque type de composants (ces clés uniques sont liées aux entités lors de leurs créations, et les clés uniques relatives aux différents types de composants permettent de modifier le bitset des entités à chaque ajout/suppression des composants.

    La suite logique à tout cela est alors de venir y greffer la fonctionnalité "Systems" (le S de ECS ). Affin de remplir ce rôle, je vais donc, comme le suggérais Koala1 dans un précédent post, utiliser le "pattern" Signal/slot".
    Globalement, il ne s'agit finalement "Que" de réaliser un container de std::function variadic templatées (vous devinez ce que je veux dire) que j'arrive aisément à lier aux différentes clés (entités).
    Un petit essai parallèle me fait me rendre compte que ces quelques lignes de code permettent un style de programmation très fun... Pour ceux qui ne connaissent pas, je vous invite à essayer.

    Cependant, ce qu'il me reste à faire, c'est de trouver un moyen de comparer les types envoyés à un variadic template avec le bitset des entités. Dans un premier temps, et afin de m'assurer que l'on utilise les bonnes clés, j'imagine utiliser exactement la même fonction permettant d'obtenir les clés de types. Cependant, il faut que je puisse boucler sur cette fonction avec chaque type d'un variadic template, uns par uns, et ça, j'ai besoin de trouver comment faire....

    Si quelqu'un sait a une idée de la manière de réaliser, je prends volontiers son idée ^^.
    Si quelqu'un à dans son chapeau un moyen plus sure de réaliser la comparaison, je prends aussi.

    D'avance, merci pour vos retours.

    EDIT : j'ai vu que l'on pouvait accéder aux différents types avec typename Selector<0, Args...>::type; mais je me demande tout de même s'il n'existe pas de meilleure solution...

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En fait, tu auras très rarement besoin d'un système de signaux et de slots dans un ECS.

    Attention, je ne dis pas que tu n'en as jamais besoin, mais juste que... ce n'est pas sur un tel système que tu devrais baser la mise en place de la partie S d'un ECS .

    Il n'y a -- en effet -- en gros que lorsque tu veux avoir des états "temporaires", qui changent après qu'un certain délais est dépassé, que le système de signaux et de slots peut s'avérer utile (ainsi, éventuellement, pour marquer certains composants comme "devant être détruits", suite à la destruction de l'entité auxquels ils sont reliés, par exemple).

    Mais, de manière générale, tant qu'il ne s'agit ni de détruire une entité devenue inutile, ni de forcer des états temporaires à changer après qu'un certain délais soit écoulé, la base des services ne va être rien d'autre qu'une grosse boucle plus ou moins infinie (une "boucle principale", en sommes), 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
    void mainLoop(){
        std::chrono::time_point<std::chrono::high_resolution_clock> previous;
        while(! willQuit){
            /* la plupart des services pourront avoir besoin du délais qui s'est écoulé
             * depuis la dernière fois qu'ils ont été appelés
             */
            auto now = std::chrono::high_resolution_clock::now() ;
            service1(now- previous);
            service2(now- previous);
            /* ... */
            service120(now- previous);
            previous = now;
        }
    }
    Et ce sera aux différents services de savoir de quel composants ils ont besoin pour ... faire le taf que l'on attend de leur part.

    Il faut savoir que, de manière générale, tous les services que tu pourras envisager vont:
    1. dépendre de la présence de plusieurs composant (ex : un composant "mouvement" et un composant "position")
    2. s'appliquer sur tous les composants présents

    mais qu'il y aura souvent un composant qui "mènera la danse", par sa seule présence, en permettant de sélectionner les entités qui devront -- effectivement -- être modifiées.

    Par exemple, service1 pourrait en réalité calculer la nouvelle position des entités "en cours de déplacement". Mais seules les entités "en cours de dépalcement" devront être modifiées.

    Nous pourrions donc avoir quelque chose qui ressemble à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    vois moveAllEntities(std::chrono::duration<double> enlapsedTime){
        for(auto const & it : movableEntities){   // on ne s'inquiète que des entités qui ont le composant "en dépalcement"
            auto id = it.id; // on a besoin de l'identifiant de chacune des entités à déplacer.
            auto & position = getPosition(id); // on a besoin de la position "précédente" de l'entité
            /* il va falloir utiliser la position précédente, les données de déplacement et le temps écoulé
             * depuis le dernier déplacement pour évaluer la nouvelle position de l'entité concernée
             */
           position.x = position.x+((it.xSpeed* enlapsedTime.count()*it.xDirection);
           position.y = position.y+((it.ySpeed* enlapsedTime.count()*it.yDirection);
           position.z = position.z+((it.zSpeed* enlapsedTime.count()*it.zDirection);
        }
    }
    alors que service2 pourrait correspondre au fait d'appliquer les dégats magiques aux entités touchées par un sort, par exemple, ce qui pourrait correspondre à quelque chose comme
    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
    void magicalDamages(){
        /* on va chercher tous les sorts d'attaque qui existent */
        for(auto const & it : hurtingSpells(){
            /* il faut trouver les entités qui risquent de souffrir d'une collision avec le sort */
           auto hurted = collideWith(it.id); // c'est un service à part ;)
           /* il n'y a pas forcément collision, mais, si il y en a une, l'entité atteinte subit des dégats */
          if(hurted!= invalidEntity){
              auto damage =findDamages(it.id);
              auto & live = findLive(hurted);
              /* y a peut être des défenses qui permettent d'éviter "une partie des dégats"
              * faudra les chercher et en tenir compte :D 
              */
             auto const & defenses = findDefences(hurted, id); // cela aussi, c'est un autre service
             auto result = damage.value - defenses.value;
             /* on ne peut pas être plus que mort :D */
             if(live.value < result)
                 live.value = 0;
             else
                live.value-=result;
        }
    }
    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

  3. #3
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut
    Merci pour ce retour.

    Effectivement, je me suis aperçu qu'il est tout à fait possible, dans le cas présent, de réaliser ce type de fonctions... Chaque service peut être utilisé au sein d'un autre service !
    Cela implique, lorsque l'on créé un composant, de réaliser un "Getter" pour ce composant afin que ce dernier puisse être utilisé dans les services.

    Merci pour ces éléments de réponse.

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Tout dépend de ce que tu appelle un "getter", et de l'endroit où tu envisages de le placer...

    Si tu veux parler de fournir un accesseur dans le composant en lui-même, la réponse est forcément non, car toutes les données qui "composent" le composant sont publiques.

    Si tu veux parler de permettre d'accéder au composant associé à une entité particulière au niveau de ce qui "maintient la liste des composants (d'un type particulier) à jour", alors, la réponse est -- effectivement -- oui.

    Car, outre le besoin d'un itérateur sur les éléments de la liste (car on préférera toujours travailler sur l'ensemble des composants de la liste, quand c'est possible), il faudra en effet pouvoir accéder "ponctuellement" être en mesure d'accéder au composant associé à une entité particulière, ce qui impliquera de rechercher ce composant particulier (s'il existe, du moins )
    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

  5. #5
    Membre éclairé Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Par défaut
    Bonjour,

    Oui, je parlais bien de "accéder au composant associé à une entité particulière au niveau de ce qui "maintient la liste des composants (d'un type particulier) à jour"", d'où les guillemets à "Getter" (c'en est pas vraiment un).

    Après, tant que l'on évite les boucles, cela me va !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problèmes de slots et signals
    Par femtosa dans le forum Qt
    Réponses: 4
    Dernier message: 13/12/2005, 09h39
  2. Réponses: 10
    Dernier message: 09/10/2005, 22h33
  3. Installer un pentium III sur slot 1
    Par major2 dans le forum Composants
    Réponses: 2
    Dernier message: 11/05/2005, 11h55
  4. Qt::connect: no such slot ...
    Par fdrouhin dans le forum Qt
    Réponses: 8
    Dernier message: 02/12/2004, 19h11
  5. Apache et nombre de slots
    Par Acti dans le forum Apache
    Réponses: 7
    Dernier message: 23/10/2004, 23h57

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