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?
la source
statecomponent
etanimationcomponent
assez 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.Réponses:
À mon humble avis, le
Movement
composant doit conserver l'état actuel (Movement.state
) et leAnimation
composant doit observer les changementsMovement.state
et 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 moyenAnimation
dépendraMovement
.Une structure alternative serait d'avoir un
State
composant générique , quiAnimation
observe etMovement
modifie, 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.
la source
Movement
ne contrôleState
(pas observer). Dernier cas: Ouais leMovement
feraitentity.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.À 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:
la source
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/
la source