Je vais parler d'un peu d'expérience, passant d'une conception OO rigide à une conception ECS (Entity-Component-System).
Il y a quelque temps, j'étais comme vous , j'avais un tas de différents types de choses qui avaient des propriétés similaires et j'ai construit divers objets et essayé d'utiliser l'héritage pour le résoudre. Une personne très intelligente m'a dit de ne pas faire cela et d'utiliser à la place Entity-Component-System.
Maintenant, ECS est un grand concept, et il est difficile de bien faire les choses. Il y a beaucoup de travail à faire pour construire correctement des entités, des composants et des systèmes. Avant de pouvoir le faire, cependant, nous devons définir les termes.
- Entité : c'est la chose , le joueur, l'animal, le PNJ, peu importe . C'est une chose qui a besoin de composants attachés.
- Composant : il s'agit de l' attribut ou de la propriété , tel qu'un "Nom" ou "Âge", ou "Parents", dans votre cas.
- Système : c'est la logique derrière un composant, ou un comportement . En règle générale, vous créez un système par composant, mais ce n'est pas toujours possible. De plus, les systèmes doivent parfois influencer d' autres systèmes.
Voici donc où j'irais avec ceci:
D'abord et avant tout, créez un ID
pour vos personnages. Un int
, Guid
comme vous voulez. Ceci est "l'entité".
Deuxièmement, commencez à réfléchir aux différents comportements que vous avez. Des choses comme le "Family Tree" - c'est un comportement. Au lieu de modéliser cela comme des attributs sur l'entité, construisez un système qui contient toutes ces informations . Le système peut alors décider quoi en faire.
De même, nous voulons construire un système pour "Le personnage est-il vivant ou mort?" C'est l'un des systèmes les plus importants de votre conception, car il influence tous les autres. Certains systèmes peuvent supprimer les caractères "morts" (comme le système "sprite"), d'autres systèmes peuvent réorganiser en interne les choses pour mieux prendre en charge le nouveau statut.
Vous allez construire un système "Sprite" ou "Dessin" ou "Rendu", par exemple. Ce système aura la responsabilité de déterminer avec quel sprite le personnage doit être affiché et comment l'afficher. Ensuite, lorsqu'un personnage meurt, retirez-les.
De plus, un système "AI" qui peut dire à un personnage quoi faire, où aller, etc. Cela devrait interagir avec de nombreux autres systèmes et prendre des décisions en fonction d'eux. Encore une fois, les personnages morts peuvent probablement être supprimés de ce système, car ils ne font plus vraiment rien.
Votre système "Name" et votre système "Family Tree" devraient probablement garder le personnage (vivant ou mort) en mémoire. Ce système doit rappeler ces informations, quel que soit l'état du personnage. (Jim est toujours Jim, même après que nous l'avons enterré.)
Cela vous donne également l'avantage de changer lorsqu'un système réagit plus efficacement: le système a sa propre minuterie. Certains systèmes doivent se déclencher rapidement, d'autres non. C'est là que nous commençons à comprendre ce qui fait qu'un jeu fonctionne efficacement. Nous n'avons pas besoin de recalculer la météo toutes les millisecondes, nous pouvons probablement le faire toutes les 5 environ.
Cela vous donne également un effet de levier plus créatif: vous pouvez créer un système "Pathfinder" qui peut gérer le calcul d'un chemin de A à B, et peut mettre à jour si nécessaire, permettant au système Movement de dire "où dois-je allez ensuite? " Nous pouvons désormais séparer pleinement ces préoccupations et les raisonner plus efficacement. Le mouvement n'a pas besoin de trouver le chemin, il a juste besoin de vous y conduire.
Vous voudrez exposer certaines parties d'un système à l'extérieur. Dans votre Pathfinder
système, vous en aurez probablement besoin Vector2 NextPosition(int entity)
. De cette façon, vous pouvez conserver ces éléments dans des tableaux ou des listes étroitement contrôlés. Vous pouvez utiliser plus petits, struct
types, qui peuvent vous aider à garder les composants dans les plus petits, des blocs de mémoire contigus, qui peuvent faire des mises à jour du système beaucoup plus rapide. (Surtout si les influences externes sur un système sont minimes, il n'a plus qu'à se soucier de son état interne, par exemple Name
.)
Mais, et je ne saurais trop insister sur ce point, maintenant un Entity
est juste un ID
, y compris les tuiles, les objets, etc. Si une entité n'appartient pas à un système, alors le système ne le suivra pas. Cela signifie que nous pouvons créer nos objets "Tree", les stocker dans les systèmes Sprite
et Movement
(les arbres ne bougeront pas, mais ils ont un composant "Position"), et les garder hors des autres systèmes. Nous n'avons plus besoin d'une liste spéciale pour les arbres, car le rendu d'un arbre n'est pas différent d'un personnage, à part le paperdolling. (Ce que le Sprite
système peut contrôler, ou le Paperdoll
système peut contrôler.) Maintenant, notre NextPosition
peut être légèrement réécrit:, Vector2? NextPosition(int entity)
et il peut renvoyer une null
position pour des entités qui ne l'intéressent pas. Nous appliquons également cela à notre NameSystem.GetName(int entity)
, il revient null
pour les arbres et les rochers.
Je vais conclure ceci, mais l'idée ici est de vous donner quelques informations sur ECS, et comment vous pouvez vraiment l' utiliser pour vous donner une meilleure conception de votre jeu. Vous pouvez augmenter les performances, découpler les éléments non liés et conserver les choses de manière plus organisée. (Cela se marie également bien avec les langages / configurations fonctionnels, comme F # et LINQ, que je recommande fortement de vérifier F # si vous ne l'avez pas déjà fait, il se marie très bien avec C # lorsque vous les utilisez conjointement.)