J'ai récemment demandé comment séparer les entités de leur comportement et la principale réponse liée à cet article: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/
Le concept ultime écrit ici est celui de: L'OBJET COMME UNE AGRÉGATION PURE.
Je me demande comment je pourrais créer des entités de jeu sous forme d'agrégation pure en utilisant C #. Je n'ai pas encore tout à fait compris comment cela pourrait fonctionner. (Peut-être que l'entité est un tableau d'objets implémentant une certaine interface ou un certain type de base?)
Ma pensée actuelle implique toujours d'avoir une classe concrète pour chaque type d'entité qui implémente ensuite les interfaces pertinentes (IMoveable, ICollectable, ISpeakable, etc.).
Comment puis-je créer une entité uniquement comme une agrégation sans avoir de type concret pour cette entité?
la source
Réponses:
L'approche «d'agrégation pure» décrite par West dans cet article lié évite complètement un objet «entité». Il existe des composants flottant dans la mémoire, mais ils ne sont liés que par des relations implicites, voire pas du tout.
Une façon de procéder est une approche dite hors - bord . Dans un tel système, les composants sont détenus par des systèmes qui les gèrent ou les contrôlent d'une autre manière (j'utilise ici le terme "gérer", mais vous ne devriez pas considérer cela comme signifiant que je suggère que vous ayez un tas de classes * Manager à tenir types de composants). Par exemple, votre système physique peut s'accrocher à un tas de choses représentant chaque corps rigide dans son monde de simulation, et peut exposer ces choses en tant que composants physiques. Les composants peuvent être les objets réels gérés par le sous-système en question, ou ils peuvent être des proxys pour ces objets, selon les besoins.
Dans un tel système, il n'est pas nécessairement nécessaire qu'une classe "Entité" contienne une collection de références aux composants qui la composent; à la place, une notification est émise concernant la création ou la destruction d'une "entité" et chaque sous-système qui gère les composants examine la description de l'entité créée / détruite (qui est généralement chargée à partir de certaines données) et détermine si un composant est nécessaire pour cela.
L'un des avantages de cette approche est que vous obtenez une très bonne localité de référence pour chaque composant. Malheureusement, c'est un peu bizarre, dans l'ensemble, et ce n'est pas la saveur la plus conviviale des entités basées sur des composants que j'ai rencontrée. Parfois, il est vraiment pratique d'avoir un objet réel qui représente une entité, même si cet objet ne fait guère plus que d'agréger des références faibles à des composants qui sont toujours détenus par d'autres sous-systèmes (si rien d'autre, il fournit un moyen facile de router les messages entre les composants) .
Il existe plusieurs bonnes façons de mettre en œuvre des systèmes d'objets de jeu orientés composants; cela aide vraiment, vraiment, vraiment si vous avez une bonne idée des exigences que vous souhaitez retirer de votre système - vous pouvez voir ce que font les frameworks populaires comme Unity pour des exemples. Sans fixer des exigences strictes pour vous-même, vous pouvez rencontrer le problème de la «conception» sans fin du système sans jamais vraiment le construire, en essayant en vain de trouver la mise en œuvre parfaite. Pour une raison quelconque, je l'ai souvent vu avec les systèmes de composants.
la source
La façon dont Unity procède est que tous les scripts (dans votre cas, le code de jeu spécifique à un type d'objet de jeu) dérivent d'une classe de base
MonoBehaviour
, qui dérive elle-même de la classe la plus pertinente pour votre casComponent
. Vous n'éditez jamais (et n'avez pas accès au code de) la classe GameObject.L'objet de jeu est chargé de contenir tous ces
Component
s. Il est également ostensiblement en charge d'appeler les fonctions pertinentes sur eux (ieUpdate
). Unity utilise la réflexion pour déterminer les fonctions à appeler (consultez la section "Fonctions remplaçables" sur cette page ), mais vous voudrez probablement les rendre virtuelles.Ainsi, l'un des mécanismes que vous utilisez dans Unity consiste à obtenir des composants sur votre objet de jeu actuel (ou ses enfants) par type. Il existe des propriétés d'assistance qui enveloppent certaines des choses courantes. Par exemple, si vous souhaitez accéder au
Transform
composant de l'objet de jeu (pour contrôler la position / rotation / échelle de l'objet de jeu), vous devrez normalement faire quelque chose commethis.GetComponent<Transform>().position
, mais ils encapsulent cela dans unthis.transform.position
appel d' aide . Un autre motif courant consiste à accéder auRenderer
composant de l'objet de jeu actuel . Donc, si vous voulez faire quelque chose comme changer le matériel de l'objet de jeu actuel, à partir d'un autre script, vous pourriez faire quelque chose commethis.renderer.material = someOtherMaterial
, et votre objet de jeu se met à jour de manière appropriée.L'une des façons dont cela fonctionne dans Unity est que leur éditeur est configuré de telle sorte que vous pouvez créer des objets de jeu dans votre scène qui ont déjà des composants connectés. Dans le cas d'Unity, tous les objets de jeu ont un
Transform
composant, mais il peut également contenir des types intégrés commeAudioListener
ouRenderer
, qui font ce que vous attendez. Ou vous pouvez ajouter vos propres composants qui font ce que vous voulez qu'ils fassent. L'éditeur expose également des champs publics / sérialisables sur vos composants afin que vous n'ayez pas à créer des scripts différents si vous souhaitez utiliser le même code de base mais changer quelques nombres magiques.Dans l'ensemble, c'est assez lisse, et je suggère de télécharger la version gratuite d'Unity et de jouer avec la façon dont leur système de script est configuré comme une preuve de concept assez décente de ce que vous voulez réaliser.
la source
J'aime une approche plus proche de la vision d'Adam du blog t-machine. Les composants ne doivent être que des données et les systèmes font tout le travail avec eux.
Voir un exemple d'implémentation ES en C #: https://github.com/thelinuxlich/artemis_CSharp
Et un exemple de jeu l'utilisant (XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
la source