• # Duck typing et implementation

    Posté par (site web personnel) . En réponse à la dépêche Je crée mon jeu vidéo E01 : les systèmes à entités. Évalué à 5.

    En dehors de l'implémentation (dont je parlerai dans la deuxième partie du commentaire), le système d'entités me fait grandement penser au duck typing que l'on retrouve dans d'autres langages (notamment python):

    Si une entité a des ailes alors c'est une entité volante.
    Si un objet a des plumes et fait "coin coin" alors c'est un canard.

    C'est très similaire comme approche.

    Et sans aller jusqu'au système d'entité ou le duck typing (on en est même loin), il y a déjà des "avancés" dans ce sens dans les langages objet "classiques" avec les interfaces et les templates.


    Tu parles d'optimisation en "linéarisant" les composants dans un tableau et ainsi en améliorer l'accès en utilisant les systèmes de cache. Je suis d'accord avec toi sur le principe mais, désolé, je ne le vois pas dans ton implémentation.

    (je me base sur ton implémentation de la lib et du tutoriel des balles rebondissantes)

    Dans la lib, le Store stocke les Component dans une map (std::map).
    - Je ne sais pas ce qu'il en est avec la norme du C++11 mais à ma connaissance rien ne garantie que les données d'une map soient stockées de manière linaire.
    - Si c'était le cas, te ne stockes pas un Component mais un pointeur. Et comme dans ton tuto tu alloues les composants à coup de new en alternant les différents types de composant, il y a très peu de chance que les composants d'un même type soit alignés.
    - Si c'était le cas, le système ne parcoure pas dans l'ordre des composants mais dans celui des entités. Pour un exemple simple, il se peut que le parcours des composants se fasse dans l'ordre, mais dans un cas concret avec surpression/ajout d'entités, il y a de fortes chances que ça soit pas le cas. Qui plus est, ça casse quand même le cache vu que tu recharges ton conteneur des entités pour passer à la suivante.

    La critique est facile, l'art est difficile. Alors je vais tenter de te proposer des axes d'améliorations :

    • Stocker les composants directement dans le Store dans un vecteur. Avoir un autre vecteur au lieu d'une map pour faire le lien entre les entités et les composants (un tableau pouvant être vu comme une map dont les clés sont des entier). Le principe est simple, tu continues d'avoir des accès en O(1) et tu gardes tes composants alignés. Dans la pratique ça se complexifie avec l'ajout/suppression d'élément dans le Store puisque qu'il faut tracer les index libérés & co (donc avoir d'autres vecteurs internes à maintenir). Du temps où je bossais dans le jeu vidéo, il était coutume de penser qu'il ne fallait pas utiliser les conteneurs de la std mais de refaire les siens. Ça fait un peu "réinventer la roue", mais la std est assez générique. Même si elle est bien implémentée, il y a beaucoup de choses qui servent à rien dans un contexte "restreint", si en plus il y a de forts besoins particuliers (performance, organisation mémoire) ça vaut souvent le cout de dev des conteneurs adaptés
    • Tu parles de Boost.Pool dans ton tuto, c'est une bonne solution pour éviter les allocations dynamiques mais je pense que c'est à la bibliothèque de gérer ce problème de mémoire et de garantir que la mémoire est alignée. (Ce point là est discutable, je l'accorde)
    • Dans ton exemples tu pourrais réunir les composants Coords et Look en un seul. À ce moment, le système Render peut parcourir directement les composants "GraphicalInfo" de manière linéaire sans passer par les entités. Par contre je n'ai pas de solution évidente pour ce qui est des systèmes "transverses" du type Graphics et Physics: vu que tu travailles sur deux composants en même temps, tu es obligé de passer par les entités. (Peut être que la littérature sur le sujet apporte une solution)

    Juste une réflexion (je sais pas où ça mène ou si c'est utile mais j'ai envie de le dire :-) ) :
    On peut voir le système d'entités/composants comme un système qui associe à un même nom (l'entité) différentes valeurs (les composants) selon "l'espace" dans lequel on regarde (les types des composants). La map dans ton Store (ou le deuxième vecteur dans ma proposition d'implémentation) étant le traducteur entre l'espace commun et les espaces spécifiques.

    Enfin, je critique mais c'est quand même une petite lib sympa que fait le boulot qui lui est demandée. Qui plus est le code est propre et il y a de la doc. (Et vu le code de merde que je suis obligé de lire en ce moment, ça fait vraiment du bien :-) ) J'attends la suite avec impatience.

    Matthieu Gautier|irc:starmad