Comment mettre à jour les états d'entités et les animations dans un jeu basé sur des composants?


10

J'essaie de concevoir un système d'entité basé sur des composants à des fins d'apprentissage (et plus tard pour certains jeux) et j'ai des problèmes pour mettre à jour les états d'entité.

Je ne veux pas avoir de méthode update () dans le composant pour éviter les dépendances entre les composants.

Ce que je pense actuellement, c'est que les composants contiennent des données et des composants de mise à jour des systèmes.

Donc, si j'ai un jeu 2D simple avec certaines entités (par exemple, joueur, ennemi1, ennemi2) qui ont des composants Transformation, Mouvement, État, Animation et Rendu, je pense que je devrais avoir:

  • Un MovementSystem qui déplace tous les composants Movement et met à jour les composants State
  • Et un RenderSystem qui met à jour les composants d'animation (le composant d'animation doit avoir une animation (c'est-à-dire un ensemble d'images / textures) pour chaque état et la mettre à jour signifie sélectionner l'animation correspondant à l'état actuel (par exemple, saut, déplacement_gauche, etc.), et mise à jour de l'index de trame). Ensuite, le système de rendu met à jour les composants de rendu avec la texture correspondant à l'image actuelle de l'animation de chaque entité et rend tout à l'écran.

J'ai vu quelques implémentations comme le framework Artemis, mais je ne sais pas comment résoudre cette situation:

Disons que mon jeu a les entités suivantes. Chaque entité a un ensemble d'états et une animation pour chaque état:

  • joueur: "idle", "moving_right", "jumping"
  • ennemi1: "moving_up", "moving_down"
  • ennemi2: "moving_left", "moving_right"

Quelles sont les approches les plus acceptées pour mettre à jour l'état actuel de chaque entité? La seule chose à laquelle je peux penser est d'avoir des systèmes séparés pour chaque groupe d'entités et des composants State et Animation séparés, donc j'aurais PlayerState, PlayerAnimation, Enemy1State, Enemy1Animation ... PlayerMovementSystem, PlayerRenderingSystem ... mais je pense que c'est une mauvaise solution et brise le but d'avoir un système basé sur les composants.

Comme vous pouvez le voir, je suis assez perdu ici, donc j'apprécierais beaucoup toute aide.

EDIT: Je pense que la solution pour faire fonctionner ce que je veux est celle-ci:

Vous créez des composants statecoment et animationcomponent suffisamment génériques pour être utilisés pour toutes les entités. Les données qu'ils contiennent seront le modificateur pour changer des choses comme les animations qui sont jouées ou les états disponibles. - Byte56

Maintenant, j'essaie de comprendre comment concevoir ces 2 composants suffisamment génériques pour pouvoir les réutiliser. Pourrait avoir un UID pour chaque état (par exemple marcher, courir ...) et stocker des animations dans une carte dans le composant d'animation composé par cet identifiant serait une bonne solution?


Je suppose que vous avez vu ceci: modifier les états des entités ou des composants ? Votre question est-elle fondamentalement différente de celle-là?
MichaelHouse

@ Byte56 Oui, je l'ai lu il y a quelques heures. La solution que vous avez suggérée est similaire à l'idée que j'ai exposée ici. Mais mon problème survient lorsque StateComponent et AnimationComponent ne sont pas les mêmes pour toutes les entités dans le système. Dois-je diviser ce système en systèmes plus petits qui traitent des groupes d'entités qui ont les mêmes états et animations possibles? (voir la dernière partie de mon message d'origine pour une meilleure clarification)
miviclin

1
Vous faites statecomponentet animationcomponentassez générique pour être utilisé pour toutes les entités. Les données qu'ils contiennent seront le modificateur pour changer des choses comme les animations qui sont jouées ou les états disponibles.
MichaelHouse

Lorsque vous parlez de dépendance, voulez-vous dire la dépendance aux données ou la dépendance de l'ordre d'exécution? De plus, dans votre solution proposée, le MovementSystem doit maintenant implémenter toutes les différentes façons dont quelque chose peut bouger? On dirait que cela brise l'idée du système basé sur les composants ...
ADB

@ADB Je parle de la dépendance des données. Afin de mettre à jour l'animation (par exemple, passer de l'animation move_right à l'animation move_left), j'ai besoin de connaître l'état actuel de l'entité et je ne vois pas comment rendre ces 2 composants plus génériques.
miviclin

Réponses:


5

À mon humble avis, le Movementcomposant doit conserver l'état actuel ( Movement.state) et le Animationcomposant doit observer les changements Movement.stateet mettre à jour son animation actuelle ( Animation.animation) en conséquence, en utilisant une simple recherche de l'état id à l'animation (comme suggéré à la fin de l'OP). Évidemment, ce moyen Animationdépendra Movement.

Une structure alternative serait d'avoir un Statecomposant générique , qui Animationobserve et Movementmodifie, qui est fondamentalement model-view-controller (état-animation-mouvement dans ce cas).

Une autre alternative serait de demander à l'entité d'envoyer un événement à ses composants lorsque son état change. Animationécouterait cet événement et mettrait à jour son animation en conséquence. Cela élimine la dépendance, bien que vous puissiez affirmer que la version dépendante est une conception plus transparente.

Bonne chance.


L'animation observe donc l'État et l'État observe le mouvement ... Les dépendances sont toujours là mais je pourrais essayer. La dernière alternative serait-elle quelque chose comme ceci: le mouvement notifie les changements à l'entité et l'entité envoie un événement à l'État, puis le même processus serait répété pour l'État et l'animation? Comment cette approche pourrait-elle influer sur les performances?
miviclin

Premier cas: Movementne contrôle State (pas observer). Dernier cas: Ouais le Movementferait entity.dispatchEvent(...);ou non, et tous les autres composants écoutant ce type d'événement le recevront. La performance est bien sûr pire que les appels de méthode pure, mais pas beaucoup. Vous pouvez regrouper des objets d'événement par exemple. Btw, vous n'avez pas à utiliser l'entité comme «nœud d'événement», vous pouvez également utiliser un «bus d'événements» dédié, en laissant votre classe d'entités complètement hors de lui.
Torious

2

À propos de votre problème, si l'ETAT n'est utilisé que dans les animations, vous n'avez même pas besoin de l'exposer à d'autres composants. S'il a plus d'une utilisation, vous devez l'exposer.

Le système de composants / sous-système que vous décrivez semble plus basé sur la hiérarchie que sur les composants. Après tout, ce que vous décrivez comme des composants sont en fait des structures de données. Cela ne signifie pas qu'il s'agit d'un mauvais système, mais je ne pense pas qu'il corresponde trop bien à l'approche basée sur les composants.

Comme vous l'avez noté, les dépendances sont un gros problème dans les systèmes basés sur des composants. Il existe différentes façons de gérer cela. Certains exigent que chaque composant déclare ses dépendances et effectue une vérification stricte. D'autres recherchent des composants implémentant une interface spécifique. D'autres encore font référence aux composants dépendants lorsqu'ils instancient chacun d'eux.

Indépendamment de la méthode que vous utilisez, vous aurez besoin d'un GameObject quelconque pour agir comme une collection de composants. Ce que GameObject fournit peut varier considérablement et vous pouvez simplifier vos dépendances inter-composants en poussant certaines données fréquemment utilisées au niveau GameObject. Unity fait cela avec la transformation par exemple, oblige tous les objets du jeu à en avoir un.

Concernant le problème que vous posez à différents états / animations pour différents objets de jeu, voici ce que je ferais. Tout d'abord, je ne serais pas trop sophistiqué à ce stade de la mise en œuvre: implémentez uniquement ce dont vous avez besoin maintenant pour le faire fonctionner, puis ajoutez des cloches et des sifflets selon vos besoins.

Donc, je commencerais par un composant 'State': PlayerStateComponent, Enemy1State, Enemy2State. La composante étatique se chargerait de changer l'état au moment opportun. L'état est quelque chose à peu près tous vos objets auront, donc il peut résider dans le GameObject.

Ensuite, il y aurait un composant AnimationCompoment. Cela aurait un dictionnaire d'animations indexé sur l'état. Dans update (), changez l'animation si l'état change.

Il y a un excellent article sur la création de framework que je ne trouve pas. Il a dit que lorsque vous n'avez pas d'expérience dans le domaine, vous devez choisir un problème et faire l'implémentation la plus simple qui résout le problème actuel . Ensuite, vous ajoutez un autre problème / cas d'utilisation et développez le cadre au fur et à mesure, afin qu'il se développe de manière organique. J'aime vraiment cette approche, en particulier lorsque vous travaillez avec un nouveau concept comme vous le faites.

L'implémentation que j'ai proposée est assez naïve, mais voici quelques améliorations possibles à mesure que vous ajoutez plus de cas d'utilisation:

  • remplacez la variable GameObject par un dictionnaire. Chaque composant utilise le dictionnaire pour stocker des valeurs. (assurez-vous de gérer correctement la collision ...)
  • remplacez le dictionnaire de valeurs simples par des références: class FloatVariable () {public value [...]}
  • Au lieu d'un composant à états multiples, créez un StateComponent générique dans lequel vous pouvez créer des machines à états variables. Vous devez avoir un ensemble générique de conditions sur lesquelles un état peut changer: pressions sur les touches, entrée de la souris, changements de variables (vous pouvez lier cela à la variable flottante ci-dessus).

Cette approche fonctionne, j'ai implémenté quelque chose de similaire il y a un an, mais le problème est que presque tous les composants dépendent d'autres composants, donc cela me semble moins flexible. J'ai également pensé à pousser les composants les plus courants (par exemple, transformer, rendre, état ...) dans l'entité, mais je pense que cela rompt le but des composants car certains d'entre eux sont liés à l'entité et certaines entités peuvent ne pas en avoir besoin. C'est pourquoi j'essaie de le repenser avec des systèmes responsables de la mise à jour de la logique afin que les composants ne se connaissent pas car ils ne se mettent pas à jour eux-mêmes.
miviclin

0

En plus de la réponse d'ADB, vous pouvez utiliser http://en.wikipedia.org/wiki/Dependency_injection , qui vous aide lorsque vous avez besoin de construire de nombreux composants en les passant comme références à leurs constructeurs. Évidemment, ils dépendront toujours les uns des autres (si cela est requis dans votre base de code), mais vous pouvez mettre toute cette dépendance en un seul endroit où les dépendances sont configurées et le reste de votre code n'a pas besoin de connaître la dépendance.

Cette approche fonctionne également bien si vous utilisez des interfaces, car chaque classe de composants demande simplement ce dont elle a besoin ou où elle doit être enregistrée et seul le cadre d'injection de dépendances (ou l'endroit où vous configurez tout, généralement l'application) sait qui a besoin de quoi .

Pour les systèmes simples, vous pourriez vous en tirer sans utiliser DI ou du code propre, vos classes RenderingSystem semblent comme vous devez les appeler statiquement ou au moins les avoir disponibles dans chaque composant, ce qui les rend à peu près dépendantes les unes des autres et difficiles à changer. Si vous êtes intéressé par une approche plus propre, consultez les liens du lien wiki DI ci-dessus et lisez à propos de Clean Code: http://clean-code-developer.com/


J'ai déjà un système où les composants sont très dépendants les uns des autres. J'ai fait un usage intensif de l'injection de dépendances là-bas, et bien que je la préfère aux hiérarchies profondes, j'essaie d'en créer une nouvelle pour éviter le couplage de composants si c'était possible. Je n'appellerais rien statiquement. J'aurais un ComponentManager auquel chaque système aurait accès (chaque système devrait y avoir une référence), et le RendererSystem obtiendrait tous les composants d'animation du gestionnaire de composants et rendrait l'état actuel de chaque animation.
miviclin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.