J'essaie de me familiariser avec la conception d'entités basée sur les composants.
Ma première étape a été de créer divers composants pouvant être ajoutés à un objet. Pour chaque type de composant, j'avais un gestionnaire, qui appelait la fonction de mise à jour de chaque composant, transmettant des choses comme l'état du clavier, etc., au besoin.
La prochaine chose que j'ai faite a été de supprimer l'objet et de simplement avoir chaque composant avec un ID. Un objet est donc défini par des composants ayant les mêmes identifiants.
Maintenant, je pense que je n'ai pas besoin d'un gestionnaire pour tous mes composants, par exemple j'ai un SizeComponent
, qui a juste une Size
propriété). Par conséquent, le SizeComponent
n'a pas de méthode de mise à jour et la méthode de mise à jour du gestionnaire ne fait rien.
Ma première pensée a été d'avoir une ObjectProperty
classe sur laquelle les composants pourraient interroger, au lieu de les avoir comme propriétés de composants. Un objet aurait donc un certain nombre de ObjectProperty
et ObjectComponent
. Les composants auraient une logique de mise à jour qui interroge l'objet pour les propriétés. Le gestionnaire gérerait l'appel de la méthode de mise à jour du composant.
Cela me semble être une ingénierie excessive, mais je ne pense pas pouvoir me débarrasser des composants, car j'ai besoin d'un moyen pour les gestionnaires de savoir quels objets ont besoin de la logique du composant à exécuter (sinon je supprimerais simplement le composant complètement et pousser sa logique de mise à jour dans le gestionnaire).
- Est - ce (ayant
ObjectProperty
,ObjectComponent
etComponentManager
cours) sur l' ingénierie? - Quelle serait une bonne alternative?
la source
SizeComponent
est exagéré - vous pouvez supposer que la plupart des objets ont une taille - ce sont des choses comme le rendu, l'IA et la physique où le modèle de composant est utilisé; La taille se comportera toujours de la même manière, vous pouvez donc partager ce code.RenderingComponent
et aPhysicsComponent
. Suis-je en train de réfléchir à la décision de mettre la propriété? Dois-je simplement le coller dans l'un ou l'autre, puis demander à l'autre de rechercher un objet pour le composant qui a la propriété requise?PhysicalStateInstance
(un par objet) à côté d'unGravityPhysicsShared
(un par jeu); Cependant, je suis tenté de dire que cela s'aventure dans le domaine de l'euphorie des architectes, ne vous architectez pas dans un trou (exactement ce que j'ai fait avec mon premier système de composants). BAISER.Réponses:
La réponse simple à votre première question est Oui, vous êtes en train de trop concevoir la conception. Le "Jusqu'où dois-je ventiler les choses?" est très courante lorsque l'étape suivante est franchie et que l'objet central (généralement appelé entité) est supprimé.
Lorsque vous décomposez les objets à un niveau si détaillé qu'ils ont leur propre taille, le design est allé trop loin. Une valeur de données en soi n'est pas un composant. Il s'agit d'un type de données intégré et peut souvent être appelé exactement comme vous avez commencé à les appeler, une propriété. Une propriété n'est pas un composant, mais un composant contient des propriétés.
Voici donc quelques lignes directrices que j'essaie de suivre lors du développement dans un système de composants:
Donc, la directive selon laquelle les composants ne sont pas des structures, le SizeComponent a été trop détaillé. Il ne contient que des données et ce qui définit la taille de quelque chose peut varier. Par exemple, dans un composant de rendu, il peut s'agir d'un scalaire 1d ou d'un vecteur 2 / 3d. Dans un composant physique, il pourrait s'agir du volume englobant de l'objet. Dans un élément d'inventaire, il peut s'agir de l'espace qu'il occupe sur une grille 2D.
Essayez de tracer une bonne ligne entre la théorie et l'aspect pratique.
J'espère que cela t'aides.
la source
Vous avez déjà accepté une réponse, mais voici mon coup de couteau à un CBS. J'ai trouvé qu'une
Component
classe générique avait certaines limites, alors j'ai opté pour une conception décrite par Radical Entertainment à GDC 2009, qui a suggéré de séparer les composants enAttributes
etBehaviors
. (" Théorie et pratique de l'architecture des composants des objets de jeu ", Marcin Chady)J'explique mes décisions de conception dans un document de deux pages. Je vais juste poster le lien car il est trop long pour tout coller ici. Il ne couvre actuellement que les composants logiques (pas les composants de rendu et de physique également), mais il devrait vous donner une idée de ce que j'ai essayé de faire:
► http://www.pdf-archive.com/2012/01/08/08/entity-component-system/preview/page/1
Voici un extrait du document:
Edit: Et voici un diagramme de relations qui montre comment les composants communiquent entre eux:
Un détail d'implémentation: le niveau entité
EventManager
n'est créé que s'il est utilisé. LaEntity
classe stocke juste un pointeur sur unEventManager
qui n'est initialisé que si un composant le demande.Edit: Sur une autre question, j'ai donné une réponse similaire à celle-ci. Vous pouvez le trouver ici pour une explication peut-être meilleure du système:
► /gamedev//a/23759/6188
la source
Cela dépend vraiment des propriétés dont vous avez besoin et de l'endroit où vous en avez besoin. La quantité de mémoire dont vous disposerez et la puissance de traitement / type que vous utiliserez. J'ai vu et j'essaye de faire ce qui suit:
Ces principes ont leurs limites. Bien sûr, vous devrez pousser la géométrie vers le moteur de rendu, mais vous ne voudrez probablement pas la récupérer à partir de là. La géométrie principale sera stockée dans le moteur physique dans le cas où des déformations s'y produisent et synchronisée avec le rendu (de temps en temps en fonction de la distance des objets). Donc, d'une certaine manière, vous le dupliquerez de toute façon.
Il n'y a pas de systèmes parfaits. Et certains jeux seront mieux lotis avec un système plus simple tandis que d'autres nécessiteront des synchronisations plus complexes entre les systèmes.
Dans un premier temps, assurez-vous que toutes les propriétés sont accessibles de manière simple à partir de vos composants afin que vous puissiez changer la façon dont vous stockez les propriétés de manière transparente une fois que vous avez commencé à affiner vos systèmes.
Il n'y a aucune honte à copier certaines propriétés. Si quelques composants doivent contenir une copie locale, il est parfois plus efficace de copier et de synchroniser plutôt que d'accéder à une valeur "externe".
De plus, la synchronisation ne doit pas nécessairement se produire à chaque image. Certains composants peuvent être synchronisés moins fréquemment que d'autres. Les composants de rendu sont souvent un bon exemple. Ceux qui n'interagissent pas avec les joueurs peuvent être synchronisés moins fréquemment, tout comme ceux qui sont éloignés. Ceux qui se trouvent loin et en dehors du champ de la caméra peuvent être synchronisés encore moins fréquemment.
En ce qui concerne votre composant de taille, il pourrait probablement être intégré à votre composant de position:
Au lieu de la taille, vous pouvez même utiliser un modificateur de taille, cela pourrait être plus pratique.
Quant au stockage de toutes les propriétés dans un système de stockage de propriété générique ... Je ne suis pas sûr que vous alliez dans la bonne direction ... Concentrez-vous sur les propriétés qui sont au cœur de votre jeu et créez des composants qui regroupent autant de propriétés connexes que possible. Tant que vous résumez correctement l'accès à ces propriétés (via des getters dans les composants qui en ont besoin par exemple), vous devriez pouvoir les déplacer, les copier et les synchroniser plus tard, sans casser trop de logiques.
la source
Si des composants peuvent être ajoutés arbitrairement à des entités, vous avez besoin d'un moyen de rechercher si un composant donné existe dans une entité et d'obtenir une référence à celui-ci. Vous pouvez donc parcourir une liste d'objets dérivés d'ObjectComponent jusqu'à ce que vous trouviez celui que vous voulez et le renvoyiez. Mais vous renverriez un objet du type correct.
En C ++ ou C #, cela signifie généralement que vous auriez une méthode de modèle sur l'entité comme
T GetComponent<T>()
. Et une fois que vous avez cette référence, vous savez exactement quelles données de membre elle contient, alors accédez-y directement.Dans quelque chose comme Lua ou Python, vous n'avez pas nécessairement un type explicite de cet objet, et ne vous en souciez probablement pas non plus. Mais encore une fois, vous pouvez simplement accéder à la variable membre et gérer toute exception qui survient en tentant d'accéder à quelque chose qui n'est pas là.
L'interrogation des propriétés d'objet ressemble explicitement à la duplication du travail que la langue peut faire pour vous, soit au moment de la compilation pour les langues à typage statique, soit au moment de l'exécution pour les langues à typage dynamique.
la source
Le fait est que RenderingComponent utilise la position, mais le PhysicsComponent la fournit . Vous avez juste besoin d'un moyen de dire à chaque composant utilisateur quel fournisseur utiliser. Idéalement de manière agnostique, sinon il y aura une dépendance.
Il n'y a pas de règle commune. Dépend d'une propriété spécifique (voir ci-dessus).
Créez un jeu avec une architecture laide mais basée sur des composants, puis refactorisez-le.
la source
PhysicsComponent
faut faire alors. Je le vois comme gérer la simulation de l'objet dans un environnement physique, ce qui m'amène à cette confusion: toutes les choses qui doivent être rendues ne doivent pas être simulées, il me semble donc mal d'ajouterPhysicsComponent
quand j'ajouteRenderingComponent
car il contient une position quiRenderingComponent
utilise. Je pouvais facilement me voir me retrouver avec un réseau de composants interconnectés, ce qui signifie que tous / la plupart doivent être ajoutés à chaque entité.Votre intestin vous dit que d' avoir la
ThingProperty
,ThingComponent
etThingManager
pour chaqueThing
type d'un composant peu exagéré. Je pense que c'est vrai.Mais, vous avez besoin d'un moyen de garder une trace des composants connexes en termes de quels systèmes les utilisent, à quelle entité ils appartiennent, etc.
TransformProperty
va être assez courant. Mais qui en est responsable, le système de rendu? Le système physique? Le système de son? Pourquoi unTransform
composant aurait-il même besoin de se mettre à jour?La solution consiste à supprimer tout type de code de vos propriétés en dehors des getters, setters et initializers. Les composants sont des données qui sont utilisées par les systèmes du jeu pour effectuer diverses tâches telles que le rendu, l'IA, la lecture du son, le mouvement, etc.
Lisez à propos d'Artemis: http://piemaster.net/2011/07/entity-component-artemis/
Regardez son code et vous verrez qu'il est basé sur des systèmes qui déclarent leurs dépendances comme des listes de
ComponentTypes
. Vous écrivez chacun de vosSystem
classes et dans la méthode constructeur / init déclarez de quels types dépend le système.Lors de la configuration des niveaux ou ainsi de suite, vous créez vos entités et leur ajoutez des composants. Ensuite, vous dites à cette entité de faire rapport à Artemis, et Artemis détermine ensuite en fonction de la composition de cette entité quels systèmes seraient intéressés à connaître cette entité.
Ensuite, pendant la phase de mise à jour de votre boucle, vos
System
s ont maintenant une liste des entités à mettre à jour. Maintenant , vous pouvez avoir granularité des composants afin que vous puissiez concevoir des systèmes fous, construire des entités d'unModelComponent
,TransformComponent
,FliesLikeSupermanComponent
etSocketInfoComponent
, et faire quelque chose de bizarre comme faire une soucoupe volante qui vole entre clients connectés à un jeu multijoueur. D'accord, peut-être pas ça, mais l'idée est que cela garde les choses découplées et flexibles.Artemis n'est pas parfait, et les exemples sur le site sont un peu basiques, mais la séparation du code et des données est puissante. C'est aussi bon pour votre cache si vous le faites correctement. Artemis ne le fait probablement pas sur ce front, mais c'est bon d'apprendre.
la source