C'est franchement flippant car il y a des vraies stratégies intéressantes, des prises de décisions correctes et d'autres trucs perfectible.
Sa force c'est aussi sa faiblesse, la gestion des unités est parfaite et ne se limite pas à 2 mains. Du coup je pense qu'il faudra limiter un peu plus l'ia pour voir émerger des stratégies moins basées sur de la micro gestion pure je pense =/
La VoD est toujours disponible
C'est dommage qu'il y ait eu qu'une seule partie avec champ de vision limité quand même (c'est un plus gros atout que l'apm ifini amha)
J'attends des rematchs.
Ah.... les gens qui oublient des virgules... Sur un panneau de commentaire a la question "Where do you want to be in 20 years?" quelqu'un a repondu: "I want to be married with children!" J'imagine que cette personne voulait ecrire: "I want to be married, with children!"
C'est juste une virgule, mais bon dieu ce que ca change tout!
https://pastebin.com/356ZeTEL
Je me sens comme Frankeinstein
Comme toujours les systemes a base de types variants c'est tres moche.
Faire un unordered map pour chaque type primaire et une fonction get/set pour chaque unordered_map est ptet plus propre
unordered_map<string, int> int_properties;
unordered_map<string, float> float_properties;
etc.
mais tu te retrouves a devoir verifier differente map pour collisions.
Pour les noms des propriétés tu veux dire? A la limite ça ça va, pour variant aussi il faut connaitre le type à l'appel donc je suppose que je sais dans quelle map aller chercher la propriété. (évidemment un aspect lourd c'est de spécifier explicitement le typage partout)
Pour un de mes jeux j'avais fait un système de composant similaire mais sans nom, uniquement des types. Du coup ça donnait genre enity.get<HitboxComponent>() ce qui du coup est pas compliqué à écrire et sécurisé (tu peux pas associer un composant avec un mauvais type) mais par contre t'es obligé d'écrire une structure pour chaque composant. Dans mon cas c'était pas dérangeant parce que tous les composants avec plusieurs informations donc j'étais obligé mais dans certains cas ça peut devenir lourd j'imagine.
Sinon tu peux écrire des suffixes custom depuis C++11, t'es pas obligé de passer par les defines pour faire des g / kg / mg /etc.
https://en.cppreference.com/w/cpp/language/user_literal
Et pour les types les typedef c'est mieux que les defines.
J'avoue les g / kg etc.. c'était juste pour m'amuser, mais je connaissais pas le suffixe custom
mais par contre t'es obligé d'écrire une structure pour chaque composant
Je veux un truc ultraflexible c'est ça la contrainte. Evidemment je sais que c'est difficilement possible mais j'essaie de voir où ça bloque Et si c'est possible, est-ce que les contraintes temps/mémoire sont pas trop importantes? Avoir une entité A qui a 3 composants, créer une B qui a les deux mêmes que la A mais une en moins et une autre en plus etc.. C'est le principe de l'ECS évidemment que j'essaie de reproduire à ma sauce et sans avoir suivi de tuto précis de l'ECS
Enfin j'essaie pas avec une grande ardeur hein, c'est pour m'amuser un peu mais si j'ai un truc propre je le réutiliserai c'est garantie
Et si tu utilisais des enums ? Genre t'as un type d'enum "double_composant", un type "int_composant" etc, bon la contrainte ça serait de devoir pré-déclarer tous les composants mais au moins t'es sur de pas pouvoir faire une faute de frappe et de récupérer un faux composant, puis pour le type de retour il sera déduit automatiquement en fonction du type de l'enum avec des surcharges de fonction.
Ajouter un nouveau type de composant (genre avec une structure custom) ça sera chiant mais ajouter un nouveau composant d'un type existant ça sera pas compliqué.
En l'occurence c'est un systeme ou les propriete sont stocke expliquement avec leur nom. Ca permet des proprietes dynamiques. C'est le genre de chose que tu veux pour faire un parser JSON, mais c'est probablement pas ce que tu veux pour faire un systeme ECS:
-ca brule probablement 3 ou 4 fois la memoire que ca devrait.
-ca empeche completement les optimizations du compilateur
-ca empeche certainement la vectorization du code
-ca cause des pointer chase pour n'importe quel access de valeur, donc ca augmente les latences et pollue les caches.
lokilok en fait la séparation int/double/string... se fait sur les propriétés d'un composant, pas sur le composant. Dans mon idée, le composant c'est une partie d'une entité qui exerce des fonctions sur ses propriétés ou celle d'autres composants de l'entité dans laquelle il est.
Par exemple une entité humain a un composant "vie" qui contient un float "point de vie" et une fonction "prend des dégats". Si jamais l'entité a un composant "armure" qui a un string "nom de l'armure", alors la fonction "prend des dégats" divise par deux les dégats reçus.
Avec ce type de code, je suis ultraflexible, j'ai pas besoin de changer beaucoup mon code si jamais j'avais pas pensé au type armure avant. J'ai juste à instancier l'objet, donner ses propriétés, et vérifier si un composant "armure" existe dans l'entité. La méthode classique demande de d'abord déclarer l'objet armure, et ses propriétés et de les initialiser.
Je peux créer de nouvelles classes et les dériver en claquant des doigts sans avoir à risquer un héritage infini mal fait.
godrik Le coup des propriétés dynamiques moi ça me va très bien pour plein de raisons, tu peux parser les propriétés facilement pour faire de la sérialization d'objet, tu peux ajouter les propriétés à l'objet en même temps que tu les initialises (pas besoin de les déclarer avant), tu peux enlever des propriétés à un objet, vérifier leur existance si elles sont optionnelles...
Evidemment l'ECS a pas besoin d'autant de flexibilité pour fonctionner, et évidemment cette façon de faire enlève pas mal de possibilité d'optimisation. Je suis d'accord qu'il faut optimiser un peu les choses mais quand tu vois Java/Python etc ça prend aussi masse de mémoire pour rien ET t'as pas la flexibilité que j'essaie d'avoir. Si ce que je fais marche je considère que prendre 30Mo plutôt que 10Mo c'est pas un gros sacrifice.
Ok je vois, perso pour moi le fait de pouvoir accéder à des variables sans avoir à les déclarer avant c'est un défaut du concept et pas une force de flexibilité (tout comme le fait de devoir déclarer le type en plus du nom) mais bon on a pas tous les mêmes attentes.
Ce que je trouve génant c'est que si tu veux appliquer un traitement sur toutes tes propriétés (serialisation etc..) ou toutes tes fonctions (log / debug etc..), ça parait impossible en programmation standard alors que là ça s'envisage. On peut se passer de la déclaration de type si je fais un unoredered_map pour chaque type..enfin tu fais a.getFloat("nom") plutôt que juste a.nom mais c'est pas très différent normalement en C++ tu connais le type de la variable que t'utilises au moment où tu l'utilises.
Le problème que j'ai avec ton approche c'est pas le fait de tout stocker dans des maps, c'est le fait d'y accéder via des strings. Si c'était via des enum par exemple, où tu dois déclarer à l'avance ce qui existe ça m'irait mieux.
Le a.getFloat est pas si différent de ce que tu as actuellement, faudrait juste selon moi implémenter une vérification de type (là dans ton code t'as une exception qui est lancée si tu veux récupérer un composant en tant qu'int alors que tu l'as set en tant que double).
normalement en C++ tu connais le type de la variable que t'utilises au moment où tu l'utilises.
Oui, mais c'est quand même redondant et pas pratique de devoir tout le temps l'écrire, surtout que si tu te trompes ça te donnera pas une erreur à la compilation mais à l'exécution ce qui pour moi est une très mauvaise chose.
En fait pour moi plus tu peux écrire du code compilable mais erroné plus ton design est mauvais. Faut limiter les possibilités pour qu'une erreur dans le code résulte d'une erreur de compilation et non d'exécution.
c'est le fait d'y accéder via des strings
c'est hashé donc je me suis dit que c'était pas problématique, mais..oui ça peut se tenter avec un enum. Mais si j'utilsie un const char* à la place c'est pas la même chose que l'enum ? Mais je comprends le surcoût d'avoir une string pour chaque propriété
Oui, mais c'est quand même redondant et pas pratique de devoir tout le temps l'écrire
ça je suis d'accord, mais après pour moi t'es pas censé faire d'erreur tout simplement. Un nom c'est un string pas un int, une mesure c'est un flottant, une année c'est un entier etc.. c'est rare qu'on hésite sur ces sujets.
En fait pour moi plus tu peux écrire du code compilable mais erroné plus ton design est mauvais. Faut limiter les possibilités pour qu'une erreur dans le code résulte d'une erreur de compilation et non d'exécution.
Je comprends l'idée qui est d'avoir une compilation qui certifie l'exécution correcte, même si c'est rarement vrai quelque soit le cas d'usage. Je pense pas que le risque rajouté soit si gros que ça et surtout si la correction des erreurs est vérifable et définitive. Si tu fais une erreur de typage, un appel, ça plante, tu corriges, c'est facile. C'est pas comme quand t'as un x/y, qui plante si y=0, qui peut fonctionner 500 fois et auquel tu peux passer à côté pendant 5ans jusqu'au jour où y=0.
Après je sais que ce design est mauvais sur plein de sujets hein, je vois que vous attaquez toutes ses faiblesses et il en a d'énormes, j'essaie juste de lui permettre d'avoir des forces qui compensent ses faiblesses structurelles.