Je travaille sur des projets de loisirs depuis 3 ou 4 ans. Des jeux 2D et 3D simples. Mais dernièrement, j'ai commencé un plus grand projet. Au cours des deux derniers mois, j'ai essayé de concevoir une classe d'objets de jeu qui peut être la base de tous mes objets de jeu. Donc, après de nombreux essais, je me suis tourné vers Google, ce qui m'a rapidement indiqué certains PDF GDC et PowerPoints. Et maintenant, j'essaie de saisir les objets de jeu basés sur des composants.
Je comprends que le moteur crée un objet de jeu et attache ensuite différents composants qui gèrent des choses comme la santé, la physique, le réseautage et tout ce que vous leur faites faire. Mais ce que je ne comprends pas, c'est comment le composant X sait si Y a changé l'état de l'objet. Comme comment le Composant Physique sait-il si le joueur est vivant, parce que la santé est contrôlée par le Composant Santé ..? Et comment le HealthComponent joue-t-il "l'animation du joueur décédé"?
J'avais l'impression que c'était quelque chose comme ça (dans le HealthComponent):
if(Health < 0) {
AnimationComponent.PlayAnimation("played-died-animation")
}
Mais là encore, comment le HealthComponent sait-il que l'objet de jeu auquel il est attaché a un AnimationComponent attaché? La seule solution que je vois ici est
Vérifiez si un composant d'animation est attaché ou non (à l'intérieur du code du composant ou du côté moteur)
Avoir des composants nécessite d'autres composants, mais cela semble combattre toute la conception des composants.
Écrivez comme, HealthWithAnimationComponent, HealthNoAnimationComponent, et ainsi de suite, ce qui semble encore une fois lutter contre toute l'idée de conception des composants.
Réponses:
Dans tous vos exemples, il y a un terrible problème. Le composant de santé doit connaître chaque type de composant qui pourrait avoir besoin de répondre à la mort de l'entité. Par conséquent, aucun de vos scénarios n'est approprié. Votre entité a une composante santé. Il a un composant d'animation. Ni dépendre ni connaître l'autre. Ils communiquent via un système de messagerie.
Lorsque le composant de santé détecte que l'entité est «morte», il envoie un message «Je suis mort». Il est de la responsabilité du composant d'animation de répondre à ce message en jouant l'animation appropriée.
Le composant d'intégrité n'envoie pas le message directement au composant d'animation. Peut-être qu'il les diffuse à tous les composants de cette entité, peut-être à l'ensemble du système; peut-être que le composant d'animation doit faire savoir au système de messagerie qu'il est intéressé par les messages «Je suis mort». Il existe de nombreuses façons d'implémenter le système de messagerie. Quelle que soit l'implémentation, le fait est que le composant d'intégrité et le composant d'animation n'ont jamais besoin de savoir ou de se soucier si l'autre est présent, et l'ajout de nouveaux composants ne nécessitera jamais de modifier ceux qui existent pour leur envoyer les messages appropriés.
la source
La façon dont Artemis résout le problème est de ne pas effectuer de traitement dans les composants. Les composants contiennent uniquement les données dont ils ont besoin. Les systèmes lisent plusieurs types de composants et effectuent le traitement nécessaire.
Donc, dans votre cas, vous pouvez avoir un RenderSystem qui lit dans le HealthComponent (et autres) et lit les files d'attente des animations appropriées. La séparation des données des fonctions de cette façon facilite la bonne gestion des dépendances.
la source
Dans votre code, il existe des moyens (je les ai utilisés, peut-être d'autres moyens existent) de savoir si l'objet a changé d'état:
Pour cela, j'ai utilisé, 1. la fonction HasComponent de GameObject, ou 2. lorsque vous attachez un composant, vous pouvez vérifier les dépendances dans une fonction de construction, ou 3. Si je sais avec certitude que l'objet a ce composant, je l'utilise simplement.
Dans certains articles que j'ai lus, les composants du système Idéal ne dépendent pas les uns des autres, mais dans la vraie vie, ce n'est pas le cas.
C'est une mauvaise idée d'écrire de tels composants. Dans mon application, j'ai créé le composant Santé le plus indépendant. Maintenant, je pense à un modèle Observer qui informe les abonnés de certains événements spécifiques (par exemple, "hit", "heal", etc.). Donc AnimationComponent doit décider par lui-même quand jouer l'animation.
Mais quand j'ai lu un article sur CBES, cela m'a impressionné, donc je suis très heureux maintenant quand j'utilise CBES et en découvre de nouvelles possibilités.
la source
C'est comme Michael, Patrick Hughes et Blecki le disent. La solution pour éviter de simplement déplacer le problème est d'abandonner l'idéologie qui cause le problème en premier lieu.
C'est moins OOD et plus comme la programmation fonctionnelle. Lorsque j'ai commencé à expérimenter la conception basée sur les composants, j'ai repéré ce problème en cours de route. J'ai googlé un peu plus, et j'ai trouvé que "la programmation réactive Functive" était la solution.
Maintenant, mes composants ne sont rien d'autre qu'une collection de variables et de champs qui décrivent son état actuel. Ensuite, j'ai un tas de classes "System" qui mettent à jour tous les composants qui leur sont pertinents. La partie réactive est obtenue en exécutant les systèmes dans un ordre bien défini. Cela garantit que quel que soit le système en ligne pour effectuer son traitement et sa mise à jour, et quels que soient les composants et entités qu'il a l'intention de lire et de mettre à jour, il travaille toujours sur des données à jour.
Cependant, d'une certaine manière, vous pouvez toujours affirmer que le problème a encore évolué. Et si ce n'est pas directement l'ordre dans lequel vos systèmes doivent fonctionner? Et s'il y a des relations cycliques et que ce n'est qu'une question de temps avant de regarder un désordre d'instructions if-else et switch? C'est une forme implicite de messagerie, non? À première vue, je pense que c'est un petit risque. Habituellement, les choses sont traitées dans l'ordre. Quelque chose comme: Entrée du joueur -> Positions d'entité -> Détection de collision -> Logique de jeu -> Rendu -> Recommencer. Dans ce cas, vous auriez un système pour chacun, fournir à chaque système une méthode update (), puis les exécuter en séquence dans votre gameloop.
la source