Il s'agit d'une suite à cette question, à laquelle j'ai répondu, mais celle-ci aborde un sujet beaucoup plus spécifique.
Cette réponse m'a aidé à mieux comprendre Entity Systems que l'article.
J'ai lu l'article (oui, le) sur Entity Systems, et il m'a dit ce qui suit:
Les entités ne sont qu'un identifiant et un tableau de composants (les articles indiquent que le stockage d'entités dans des composants n'est pas une bonne façon de faire les choses, mais ne fournit pas d'alternative).
Les composants sont des éléments de données qui indiquent ce qui peut être fait avec une certaine entité.
Les systèmes sont les "méthodes", ils effectuent des manipulations de données sur des entités.
Cela semble vraiment pratique dans de nombreuses situations, mais la partie sur les composants qui ne sont que des classes de données me dérange. Par exemple, comment pourrais-je implémenter ma classe Vector2D (Position) dans un système d'entité?
La classe Vector2D contient des données: les coordonnées x et y, mais elle a également des méthodes , qui sont cruciales pour son utilité et distinguent la classe d'un simple tableau à deux éléments. Méthodes sont par exemple: add()
, rotate(point, r, angle)
, substract()
, normalize()
et toutes les autres normes, méthodes utiles et absolument nécessaires que les positions (qui sont des instances de la classe Vector2D) devrait avoir.
Si le composant n'était qu'un détenteur de données, il ne pourrait pas avoir ces méthodes!
Une solution qui pourrait probablement apparaître serait de les implémenter dans des systèmes, mais cela semble très contre-intuitif. Ces méthodes sont des choses que je veux effectuer maintenant , qu'elles soient complètes et prêtes à l'emploi. Je ne veux pas attendre la MovementSystem
lecture d'un ensemble coûteux de messages qui lui demandent d'effectuer un calcul sur la position d'une entité!
Et, l'article indique très clairement que seuls les systèmes devraient avoir une fonctionnalité, et la seule explication à cela, que j'ai pu trouver, était "d'éviter la POO". Tout d'abord, je ne comprends pas pourquoi devrais-je m'abstenir d'utiliser des méthodes dans les entités et les composants. La surcharge de mémoire est pratiquement la même, et lorsqu'ils sont couplés à des systèmes, ils devraient être très faciles à mettre en œuvre et à combiner de manière intéressante. Les systèmes, par exemple, ne pouvaient fournir une logique de base qu'aux entités / composants qui connaissent eux-mêmes la mise en œuvre. Si vous me demandez - c'est essentiellement en prenant les goodies à la fois de ES et OOP, quelque chose qui ne peut pas être fait selon l'auteur de l'article, mais cela me semble être une bonne pratique.
Pensez-y de cette façon; il existe de nombreux types d'objets à dessiner dans un jeu. Images pures et simples, des animations ( update()
, getCurrentFrame()
, etc.), des combinaisons de ces types primitifs, et tous pourraient simplement fournir une draw()
méthode pour le système de rendu, qui ne puis pas besoin de se soucier de la façon dont l'image - objet d'une entité est mis en œuvre, seulement sur l'interface (dessiner) et la position. Et puis, je n'aurais besoin que d'un système d'animation qui appellerait des méthodes spécifiques à l'animation qui n'ont rien à voir avec le rendu.
Et juste une autre chose ... Existe-t-il vraiment une alternative aux tableaux quand il s'agit de stocker des composants? Je ne vois aucun autre endroit pour les composants à stocker autre que des tableaux à l'intérieur d'une classe d'entité ...
C'est peut-être une meilleure approche: stocker les composants comme de simples propriétés d'entités. Par exemple, un composant de position serait collé entity.position
.
La seule autre façon serait d'avoir une sorte de table de recherche étrange à l' intérieur des systèmes, qui fait référence à différentes entités. Mais cela semble très inefficace et plus compliqué à développer que de simplement stocker des composants dans l'entité.
Réponses:
Je pense qu'il est tout à fait correct d'avoir des méthodes simples pour accéder, mettre à jour ou manipuler les données dans les composants. Je pense que la fonctionnalité qui devrait rester en dehors des composants est une fonctionnalité logique. Les fonctions utilitaires sont très bien. N'oubliez pas que le système entité-composant n'est qu'un guide, et non des règles strictes que vous devez suivre. Ne sortez pas de votre chemin pour les suivre. Si vous pensez qu'il est plus logique de le faire dans un sens, faites-le de cette façon :)
ÉDITER
Pour clarifier, votre objectif n'est pas d'éviter la POO . Ce serait assez difficile dans la plupart des langues courantes utilisées de nos jours. Vous essayez de minimiser l' héritage , qui est un aspect important de la POO, mais pas obligatoire. Vous voulez vous débarrasser de l'héritage Object-> MobileObject-> Creature-> Bipedal-> Human type.
Cependant, c'est OK d'avoir un héritage! Vous avez affaire à une langue fortement influencée par l'héritage, il est très difficile de ne pas l'utiliser. Par exemple, vous pouvez avoir une
Component
classe ou une interface que tous vos autres composants étendent ou implémentent. Même chose avec votreSystem
classe. Cela rend les choses beaucoup plus faciles. Je vous recommande fortement de jeter un œil au framework Artemis . C'est open source et il a quelques exemples de projets. Ouvrez ces choses et voyez comment cela fonctionne.Pour Artemis, les entités sont stockées dans un tableau, simple. Cependant, leurs composants sont stockés dans un ou plusieurs tableaux (séparés des entités). Le tableau de niveau supérieur regroupe le tableau de niveau inférieur par type de composant. Ainsi, chaque type de composant a son propre tableau. Le tableau de niveau inférieur est indexé par ID d'entité. (Maintenant, je ne sais pas si je le ferais de cette façon, mais c'est comme ça que ça se passe ici). Artemis réutilise les ID d'entité, de sorte que l'ID d'entité max ne dépasse pas votre nombre actuel d'entités, mais vous pouvez toujours avoir des tableaux clairsemés si le composant n'est pas un composant fréquemment utilisé. De toute façon, je ne vais pas trop séparer cela. Cette méthode de stockage des entités et de leurs composants semble fonctionner. Je pense que ce serait une excellente première étape pour mettre en œuvre votre propre système.
Les entités et les composants sont stockés dans un gestionnaire distinct.
La stratégie que vous mentionnez, faire en sorte que les entités stockent leurs propres composants (
entity.position
), est en quelque sorte contraire au thème des composants d'entité, mais elle est totalement acceptable si vous pensez que cela a le plus de sens.la source
EntityManager
comme c'est où les choses sont stockées.«Cet» article n'est pas celui avec lequel je suis particulièrement d'accord, donc ma réponse sera quelque peu critique, je pense.
L'idée n'est pas de s'assurer que rien dans votre programme n'est autre qu'un ID d'entité, un composant ou un système - c'est de s'assurer que les données et le comportement de l'entité sont créés en composant des objets plutôt qu'en utilisant un arbre d'héritage complexe ou pire en essayant de mettre toutes les fonctionnalités possibles dans un seul objet. Pour implémenter ces composants et systèmes, vous aurez certainement des données normales comme des vecteurs qui sont, dans la plupart des langues, mieux représentées en tant que classe.
Ignorez la partie de l'article qui suggère que ce n'est pas la POO - c'est tout aussi la POO que toute autre approche. Lorsque la plupart des compilateurs ou des exécutions de langage implémentent des méthodes objet, c'est essentiellement comme n'importe quelle autre fonction, sauf qu'il existe un argument caché appelé
this
orself
, qui est un pointeur vers un endroit en mémoire où les données de cet objet sont stockées. Dans un système basé sur des composants, l'ID d'entité peut être utilisé pour trouver où se trouvent les composants pertinents (et donc les données) pour une entité donnée. Ainsi, l'ID d'entité équivaut à un pointeur this / self, et les concepts sont fondamentalement la même chose, juste un peu réarrangés.Bien. Les méthodes sont un moyen efficace d'organiser votre code. La chose importante à retirer de l'idée "éviter la POO" est d'éviter d'utiliser l'héritage partout pour étendre les fonctionnalités. Au lieu de cela, divisez la fonctionnalité en composants qui peuvent être combinés pour faire la même chose.
L'idée d'un système basé sur les composants est que vous n'auriez pas de classes séparées pour celles-ci, mais que vous auriez une seule classe Objet / Entité, et l'image serait un Objet / Entité qui a un ImageRenderer, les Animations seraient un Objet / Entité qui a un AnimationRenderer, etc. Les systèmes concernés sauraient comment rendre ces composants et il n'y aurait donc pas besoin d'avoir de classe de base avec une méthode Draw ().
Bien sûr, mais cela ne fonctionne pas bien avec les composants. Vous avez 3 choix:
Vous pouvez stocker les composants dans le système. Le tableau n'est pas le problème, mais l'emplacement de stockage du composant.
la source
Un vecteur est une donnée. Les fonctions sont plus comme des fonctions utilitaires - elles ne sont pas spécifiques à cette instance des données, elles peuvent être appliquées à tous les vecteurs indépendamment. Une bonne façon de penser est la suivante: ces fonctions peuvent-elles être réécrites en tant que méthodes statiques? Si c'est le cas, c'est juste une utilité.
la source