Actuellement, je suis confronté au problème suivant:
J'essaie d'écrire un clone de pong en utilisant un système de composants d'entité (ECS). J'ai écrit le "framework" tout seul. Il existe donc une classe qui gère les entités avec tous les composants. Ensuite, il y a les classes de composants elles-mêmes. Et enfin, il y a mes systèmes qui obtiennent juste toutes les entités qui ont des composants dont le système a besoin.
Ainsi, par exemple, mon système de mouvement recherche toutes les entités qui ont un composant de position et un composant de mouvement. Le composant de position tient juste la position et le composant de mouvement tient la vitesse.
Mais le vrai problème est mon système de collision. Cette classe est comme un blob logique. J'ai tellement de cas particuliers dans cette classe.
Par exemple: Mes pagaies peuvent entrer en collision avec les bordures. Si cela se produit, leur vitesse est réglée sur zéro. Ma balle peut aussi bien entrer en collision avec les bordures. Mais dans ce cas, sa vitesse est simplement reflétée à la normale de la frontière afin qu'elle se reflète. Pour ce faire, j'ai donné au ballon une composante physique supplémentaire qui dit simplement: "Hé, cette chose ne s'arrête pas, elle réfléchit." Donc, en fait, le composant physique n'a pas de données réelles. Il s'agit d'une classe vide qui est juste là pour indiquer au système si un objet se reflète ou s'arrête.
Ensuite, il y a ceci: je veux rendre quelques particules lorsque la balle entre en collision avec les pagaies ou les bordures. Je pense donc que la balle doit obtenir un autre composant qui indique au système de collision de créer des particules lors de la collision.
Ensuite, je veux avoir des power-ups qui peuvent entrer en collision avec les pagaies mais pas avec les bordures. Si cela se produit, les power-ups doivent disparaître. J'aurais donc besoin de beaucoup plus de boîtiers et de composants (pour dire au système que certaines entités ne peuvent entrer en collision qu'avec certaines autres, pas avec tous même si d'autres sont réellement capables de se heurter, en outre, le système de collision a dû appliquer les power-ups à les pagaies, etc., etc., etc.).
Je vois que le système de composants d'entité est une bonne chose car il est flexible et vous n'avez pas de problèmes d'héritage. Mais je suis totalement bloqué actuellement.
Suis-je trop compliqué? Comment dois-je faire face à ce problème?
Bien sûr, je dois créer des systèmes qui sont en fait responsables de la "post-collision", donc le système de collision ne dit que "Oui, nous avons une collision dans la dernière image" et puis il y a un tas de systèmes "post-collision" qui tous nécessitent des composants (combinaisons de) différents, puis changent les composants. Par exemple, il y aurait un système de mouvement post-collision qui arrête les choses qui doivent s'arrêter lorsque la collision se produit. Puis un système physique post-collision qui reflète les choses, etc.
Mais cela ne me semble pas non plus être une bonne solution, car par exemple:
- Mon système de post-collision de mouvement aurait besoin d'entités qui ont un composant de position, un composant de mouvement et un composant de collision. Ensuite, il mettrait la vitesse de l'entité à zéro.
- Le système physique post-collision aurait besoin d'entités qui ont un composant position, un composant mouvement, un composant collision et un composant physique. Il refléterait alors le vecteur vitesse.
Le problème est évident: le mouvement post-collision a besoin d'entités qui sont un sous-ensemble des entités du système physique post-collision. Ainsi, deux systèmes post-collision fonctionneraient sur les mêmes données, l'effet étant: Bien qu'une entité ait une composante physique, sa vitesse serait nulle après une collision.
Comment ces problèmes sont-ils résolus en général dans un système de composants d'entité? Ces problèmes sont-ils même habituels ou est-ce que je fais quelque chose de mal? Si oui, que faut-il faire et comment?
la source
Vous compliquez trop les choses. J'irais jusqu'à dire que même l'utilisation d'une conception basée sur les composants est juste exagérée pour un jeu aussi simple. Faites les choses de la manière qui rend votre jeu rapide et facile à développer. Les composants aident à l'itération dans des projets plus grands avec une grande variété de comportements et de configurations d'objets de jeu, mais leur avantage pour un jeu aussi simple et bien défini est plus discutable. J'ai fait un discours l'année dernière à ce sujet: vous pouvez créer de petits jeux amusants en quelques heures si vous vous concentrez sur la création d'un jeu au lieu d' adhérer à une architecture . L'héritage se décompose lorsque vous avez 100 ou même 20 types d'objets différents, mais cela fonctionne très bien si vous n'en avez qu'une poignée.
En supposant que vous souhaitez continuer à utiliser des composants à des fins d'apprentissage, votre approche présente des problèmes évidents.
Tout d'abord, ne faites pas vos composants si petits. Il n'y a aucune raison d'avoir des composants à grain fin comme le «mouvement». Il n'y a pas de mouvement générique dans votre jeu. Vous avez des palettes, dont le mouvement est étroitement lié à l'entrée ou à l'IA (et n'utilisez pas vraiment la vitesse, l'accélération, la restitution, etc.), et vous avez la balle, qui a un algorithme de mouvement bien défini. Il suffit d'avoir un composant PaddleController et un composant BouncingBall ou quelque chose du genre. Si / lorsque vous obtenez un jeu plus compliqué, vous pouvez vous inquiéter d'avoir un composant PhysicsBody plus générique (qui, dans les moteurs «réels», est simplement un lien entre l'objet de jeu et tout objet API interne utilisé par Havok / PhysX / Bullet / Box2D / etc.) Qui gère une plus grande variété de situations.
Même une composante «position» est discutable, mais certainement pas rare. Les moteurs physiques ont généralement leur propre idée interne de l'endroit où se trouve un objet, les graphiques peuvent avoir une représentation interpolée et l'IA peut avoir encore une autre représentation des mêmes données dans un état différent. Il peut être avantageux de donner à chaque système la possibilité de gérer sa propre idée de la transformation des composants du système, puis de garantir une communication fluide entre les systèmes. Voir le billet de blog BitSquid sur les flux d'événements .
Pour les moteurs de physique personnalisés, n'oubliez pas que vous êtes autorisé à avoir des données sur vos composants. Peut-être qu'un composant physique générique de Pong a des données indiquant sur quels axes il peut se déplacer (par exemple,
vec2(0,1)
comme un multiplicateur pour les pagaies qui ne peuvent se déplacer que sur l'axe Y etvec2(1,1)
pour la balle indiquant qu'il peut se déplacer cependant), un drapeau ou un flotteur indiquant le rebond (le balle serait généralement à1.0
et pagaies à0.0
), les caractéristiques d'accélération, la vitesse, etc. Essayer de diviser cela en un milliard de micro-composants différents pour chaque élément de données hautement liées est contraire à ce que ECS était censé faire à l'origine. Conservez les éléments qui sont utilisés ensemble dans le même composant lorsque cela est possible et divisez-les uniquement lorsqu'il existe une grande différence dans la façon dont chaque objet de jeu utilise ces données. Il y a un argument pour dire que pour Pong, la physique entre le ballon et les palettes est suffisamment différente pour être des composants séparés, mais pour un jeu plus grand, il n'y a pas de raison d'essayer de faire 20 composants pour faire ce qui fonctionne très bien en 1-3.N'oubliez pas, si / quand votre version d'ECS vous gêne, faites ce dont vous avez besoin pour créer votre jeu et oubliez l'adhésion obstinée à un modèle / architecture de conception.
la source
Ball
qui contient toute la logique de la balle comme le mouvement, le rebond, etc. et unPaddle
composant qui prend en entrée, mais c'est moi. Tout ce qui a le plus de sens pour vous, vous échappe et vous permet de créer le jeu est la "bonne façon" de faire les choses.À mon avis *), votre plus gros problème avec les composants est le suivant: les composants ne sont PAS là pour dire à quelqu'un d'autre quoi faire. Les composants sont là pour FAIRE des choses. Vous n'avez pas de composant juste pour conserver la mémoire de quelque chose et ensuite faire fonctionner d'autres composants. Vous voulez des composants qui FONT des trucs avec les données qu'ils ont obtenues.
Si vous vous voyez tester la présence d'autres composants (puis appeler des fonctions là-bas), alors c'est un signe clair, que l'une des deux choses est vraie:
Invalidate()
ouSetDirty()
à d'autres composants.Soit dit en passant, cela s'applique à toutes sortes de systèmes, non seulement aux systèmes Entité / Composant, mais aussi à l'héritage classique avec de simples "GameObject" ou même des fonctions de bibliothèque.
*) Vraiment seulement le mien . Les opinions varient considérablement sur Whats Da Real Component Systemz (TM)
la source