1- Pourquoi transmets tu un float par référence constante à ta fonction cela n'a pas vraiment de sens
Si tu descend au niveau du code binaire exécutable, tu te rendras compte qu'une référence est considérée par le compilateur exactement de la même manière qu'un pointeur.
Or, il n'y a absolument aucun intérêt à transmettre l'adresse d'une donnée (qui nécessite l'équivalent d'un registre entier) si c'est pour représenter une donnée qui est plus petite que la taille de ce registre.
Par principe, on considère donc de ne transmettre par référence que ... les informations dont la taille est plus grande que celle d'un pointeur
Par facilité, et parce que cela ne pose pas vraiment de problème, on simplifie cette règle de principe en décidant de ne transmettre par référence que ... les informations dont la taille est supérieure à celle des différents types primitifs.
Ainsi, il y aurait sans doute largement intérêt à transmettre une donnée de typecar sa taille n'est que de ... deux bytes
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 struct Point{ char x; char y; };
Sous windows 10, avec Gcc-8.1.0 en version 64 bits, j'obtiens les tailles suivantes :
- sizeof(int*) --> taille d'un pointeur = 8
- sizeof(float) --> taille d'un float = 4
- et, au cas où tu douterais de la taille d'un pointeur: sizeof(char *) --> un pointeur vers une donnée de type différent : 8
Enfin, la meilleure raison que l'on aie d'éviter la copie de certaines informations (outre la quantité de mémoire qu'elles nécessitent pour être représentées) est que leur processus de copie prend "un temps bête", par exemple, parce qu'il implique:
- d'allouer de la mémoire en suffisance pour contenir l'ensemble des informations
- de copier l'ensemble des informations internes de l'objet d'origine vers l'objet de destination
Un tel problème ne se pose pas avec les types primitifs
2- Pourquoi travailler avec des pointeurs pour pos et vel (que tu ferais bien d'appeler position et velocity, vu que cela ne changera absolument rien au final )
Pourquoi ne pas travailler avec des références sous la forme deCela te simplifierais la tâche
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 auto & position = component::get<Position>(e); auto & velocity =component::get<Velocity>(e);
Connais-tu le principe d'une clé Voici à quoi cela ressemble:* quelle autre solution ais-je pour éviter d'avoir à réaliser cet héritage, tout en me permettant de filtrer uniquement sur les entités concernées par les composants souhaités ?Quand tu fais entrer ta clé dans le barillet, chaque pointe va enfoncer une petite pièce métallique dans son encoche de manière à ce qu'elle soit à fleur avec le système rotatif. Si toutes ces pièces sont correctement enfoncées, la clé peut tourner dans le barillet. Si une des pièce n'est pas à fleur, la clé ne peut pas tourner.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 __ \ \_____________ _____________\ __ / /\/\/\/\/ \/ / 1 2 3 4 5 6 __/
Par exemple, tu remarque qu'il n'y a pas de pointe au niveau du numéro 5 sur le dessin que j'ai fait
Cela signifie que, s'il y avait une petite pièce métallique au niveau de ce numéro 5, elle ne serait pas enfoncée, et qu'elle empêcherait la clé de tourner, parce que la clé que tu essaye d'utiliser est -- peut-être -- destinée à la maison voisine
Hé bien ce que je vais te proposer, c'est de créer un système de clés qui te permettra de déterminer rapidement si "tous les composants" nécessaires sont présent pour une entité donnée.
En effet, tout ce dont on a besoin pour être en mesure de déterminer ce point, c'est ... un bit spécifique par type de composant : si ce bit a une valeur égale à 0, c'est que le composant est absent, si le bit a une valeur égale à 1, c'est que le composant est présent.
Sur le nombre de bits qui permet de rerpésenter une valeur numérique entière de type unsigned long long (pour lequel on dispose -- sans doute -- d'un alias de type hyper connu, à savoir : size_t) nous devrions donc -- a priori -- être de représenter la présence (ou l'absence) de pas moins de ... 64 composants différents!
Comment Hé bien faisons simple, et utiliser la classe std::bitset, sous une forme proche de
Une fois que le type représentant notre notion de clé est créé, le principe est "relativement simple":
Code : Sélectionner tout - Visualiser dans une fenêtre à part using KeyType = std::bitset<64>;
1- pour chaque entité, on crée une clé
2 - pour chaque type de composant existant, on défini l'index (dans la clé) du bit qui permet de définir s'il est présent (ou non) pour une entité donnée.
Par exemple, le composant de type Position pourrait prendre l'indice 0 et le composant de type Velocity pourrait prendre l'indice 1.
3- Chaque fois que l'on ajoute un composant à une entité, on fait passer le bit correspondant à 1 dans la clé correspondant à l'entité
4- Chaque fois que l'on retire un composant à une entité, on fait passer le bit correspondant à 0 dans la clé correspondant à l'entité
5- pour chaque service, on crée une "emprunte" représentant les composants dont on a besoin pour travailler
6- Avant de faire quoi que ce soit au niveau du service, on compare la clé correspondant à l'entité sur laquelle on a prévu de travailler avec l'emprunte que l'on a créé.
Cette comparaison se fera sous une forme proche deCe genre de test étant particulièrement rapide, si tu organise correctement la manière de maintenir toutes les clés existantes en mémoire, tu n'a aucun soucis à te faire quant aux performances de ton programme
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 static const KeyType pass{keyIndex(Velocity)|keyIndex(position)/*|... */}; // l'emprunte auto working = getKey(entity); if(working & pass == pass){ /* on a ce qu'il faut, on peut travailler }
Par contre, je vais être "très méchant" avec toi, et te laisser chercher un peu comment mettre une telle logique en oeuvre... sinon, tu n'apprendra rien
Mais, entre les template et quelques macros bien senties (pour faciliter la réécriture d'un code qui est toujours quasiment le même), tu devrais pouvoir t'en sortir "sans trop de problème"
Te souviens tu des cinq principe élémentaires, et du SRP en particuliera la destruction d'une entité, comment m'assurer que tous les composants seront détruits
Car, pour représenter "l'ensemble des composant" (d'un type particulier) qui existent, nous avons très certainement intérêt à créer une une permettant de représenter la notion de "container de composants"; quitte à créer des fonctions libres qui cachent l'existence de cette classe.
Cette classe prendrait sans doute une forme proche de(je vais encore être très mauvais en te laissant le reste, mais je suis persuadé d'en avoir déjà parlé )
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 template <typename COMPTYPE> class ComponentHolder{ public: template <typename ... ARGS> COMPTYPE & create(EntityType entity, ARGS ... param); void destroy(EntityType entity); /* ... */ }
Et nous pourrions alors envisager un système de signaux et de slots (j'en ai développé un qui pourrait être sympa, et que tu retrouveras -->ici<-- ) qui permette à notre "gestionnaire d'entité" d'envoyer un signal à tous les "conteneurs de composant" lorsqu'une entité est détruite.
Pour ce faire, notre "gestionnaire d'entité" prendrait une forme proche deet nous pourrions donc corriger notre notion de conteneur de composant pour lui donner la forme de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 class EntityHolder{ using destroy_type = Tools::Signal<EntityType>; public: using slot_type = typename destroy_type::slot_type Tools::Connection onEntityDestroy(slot_type slot); private: destroy_type destroy_; }
en veillant à connecter ce qui deviendra le slot (entityDestroyed) à notre signal au travers de la fonction onEntityDestroyed de notre "gestionnaire d'entités"
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 template <typename COMPTYPE> class ComponentHolder{ public: template <typename ... ARGS> COMPTYPE & create(EntityType entity, ARGS ... param); void destroy(EntityType entity); void entityDestroyed(EntityType entity){ if(exists(entity) destroy(entity); } /* ... */ }
(reporte toi à la documentation pour savoir comment implémenter ceci )
Au final, il y a beaucoup à dire sur le sujet... Mais mon intervention est déjà largement assez longue que pour que je décide de te laisser chercher un peu par toi-même
Si tu n'y arrive vraiment pas, je courrerai de nouveau le risque de faire péter toutes les limites imposées par le forum pour te donner la solution
Partager