Remarque: je programme cela en Javascript, mais cela devrait être indépendant de la langue dans la plupart des cas.
Je pense à convertir mon moteur en moteur ECS.
J'ai l'idée de base ( note: c'est faux, voir ma réponse ):
Les entités sont des objets de jeu.
Les composants sont des éléments de fonctionnalité ( reactToInput()
) ou d'état ( position
) qui peuvent être «collés» aux entités.
Les systèmes ont une liste d'entités qu'ils gèrent et mettent à jour.
Mais, je ne suis pas tout à fait sûr d'avoir la mise en œuvre et quelques détails ...
Question: un système peut-il fonctionner sur différents types d'entités? Je donne généralement l'exemple d'une classe appelée Scene
dans mon moteur, et elle servira également à cette fin maintenant. Une scène est un conteneur de tous les objets qui peuvent être rendus, mis à jour, affecter le rendu (lumières) et peut-être, à l'avenir, même des 2DSoundEmitter
objets. Il a une interface de haut niveau afin que l'utilisateur n'ait pas à se soucier du type d'objet qu'il scene.add()
ingère et de tout ce genre de choses.
Je me rends compte que cela Scene
pourrait être un système. Il prend des entités, les stocke, puis il peut appeler leurs méthodes de mise à jour et peut-être même effectuer des changements d'état. Mais, il y a un problème: comme je l'ai décrit ci-dessus, ils Scene
peuvent être alimentés en différents types d'objets! Que dois-je faire dans, disons, une situation où une scène contient à la fois des objets pouvant être rendus ("dessinables") et des lumières? Dois-je faire des vérifications de type avant d'interagir? Ou, devrais-je le résoudre à un niveau encore plus bas: créez un LightSource
composant qui peut être ajouté à n'importe quel objet, et la lumière ne serait qu'une entité avec LightSource
et des Position
composants. Est-ce acceptable?
En outre, est-ce une bonne pratique de continuer à utiliser l'héritage conventionnel et les classes traditionnelles? Par exemple, je n'arrive pas à comprendre ce que je Renderer
serais! Ce n'est pas un système, car sa seule fonction est de prendre un appareil photo et une scène, de tout rendre et d'appliquer des effets (comme des ombres). Il gère également le contexte, la largeur et la hauteur du jeu, fait des traductions ... Mais ce n'est toujours pas un système!
Edit: pourriez-vous peut-être lier des ressources que vous avez trouvées sur l'ECS? J'ai du mal à en trouver de bons.
Réponses:
Permettez-moi de voir si en essayant de comprendre en tant que développeur JS Web / UI, je peux être utile. N'allez pas trop loin dans l'agnosticisme linguistique. De nombreux modèles établis dans d'autres langues méritent d'être étudiés, mais peuvent être appliqués très différemment dans JS en raison de sa flexibilité ou ne sont vraiment pas nécessaires en raison de la nature malléable de la langue. Vous pourriez souffler quelques opportunités si vous écrivez votre code en pensant que JS a le même ensemble de frontières qu'un langage orienté OOP plus classique.
Tout d'abord, sur le facteur "ne pas utiliser OOP", rappelez-vous que les objets JavaScript sont comme de la pâte à modeler par rapport à d'autres langages et vous devez en fait faire tout votre possible pour construire un cauchemar de schéma d'héritage en cascade puisque JS n'est pas une classe et le compositing lui viennent beaucoup plus naturellement. Si vous implémentez une classe idiote ou un prototype de système manuel dans votre JS, envisagez de l'abandonner. Dans JS, nous utilisons des fermetures, des prototypes et nous transmettons des fonctions comme des bonbons. C'est dégoûtant et sale et faux mais aussi puissant, concis et c'est ainsi que nous l'aimons.
Les approches lourdes d'héritage sont en fait énoncées comme un anti-modèle dans les modèles de conception et pour une bonne raison, toute personne qui a parcouru plus de 15 niveaux de classe ou des structures de type classe pour essayer de comprendre où diable la version éclatée d'une méthode venait de peut vous le dire.
Je ne sais pas pourquoi tant de programmeurs aiment faire cela (en particulier les gars de Java qui écrivent JavaScript pour une raison quelconque), mais c'est horrible, illisible et complètement impossible à entretenir lorsqu'il est utilisé à outrance. L'héritage est correct ici et là, mais pas vraiment nécessaire dans JS. Dans les langues où c'est un raccourci plus attrayant, il devrait vraiment être réservé à des préoccupations d'architecture plus abstraites plutôt qu'à des schémas de modélisation plus littéraux comme frankensteining une implémentation de zombie via une chaîne d'héritage qui comprenait un BunnyRabbit parce que cela fonctionnait. Ce n'est pas une bonne réutilisation du code. C'est un cauchemar d'entretien.
En tant que moteur JS dev Entity / Component / System, les moteurs me semblent être un système / modèle pour découpler les problèmes de conception, puis pour composer des objets à implémenter à un niveau très granulaire. En d'autres termes, un jeu d'enfant dans un langage comme JavaScript. Mais permettez-moi de voir si je suis en train de faire ça correctement en premier.
Entité - La chose spécifique que vous concevez. Nous parlons plus dans le sens des noms propres (mais pas en fait, bien sûr). Pas «Scene», mais «IntroAreaLevelOne». IntroAreaLevelOne peut s'asseoir dans une boîte sceneEntity d'une certaine sorte, mais nous nous concentrons sur quelque chose de spécifique qui diffère d'autres choses connexes. Dans le code, une entité n'est en fait qu'un nom (ou ID) lié à un tas de choses qu'elle doit avoir implémenté ou établi (les composants) pour être utile.
Composants - types de choses dont une entité a besoin. Ce sont des noms généraux. Comme WalkingAnimation. Dans WalkingAnimation, nous pouvons être plus précis, comme "Shambling" (bon choix pour les zombies et les monstres végétaux), ou "ChickenWalker" (idéal pour les types de robots ed-209ish à articulation inversée). Remarque: Je ne sais pas comment cela pourrait se dissocier du rendu d'un modèle 3D comme celui-ci - alors peut-être un exemple de merde, mais je suis plus un JS pro qu'un développeur de jeu expérimenté. Dans JS, je mettrais le mécanisme de mappage dans la même boîte avec les composants. Les composants à part entière sont susceptibles d'être légers sur la logique et plus d'une feuille de route indiquant à vos systèmes ce qu'il faut implémenter si des systèmes sont même nécessaires (dans ma tentative d'ECS, certains composants ne sont que des collections d'ensembles de propriétés). Une fois qu'un composant est établi, il '
Systèmes - La vraie viande programmée est ici. Les systèmes d'IA sont construits et liés, le rendu est réalisé, les séquences d'animations établies, etc ... Je les supprime et les laisse principalement à l'imagination mais dans l'exemple System.AI prend un tas de propriétés et crache une fonction qui est utilisé pour ajouter des gestionnaires d'événements à l'objet qui est finalement utilisé dans l'implémentation. L'essentiel sur System.AI est qu'il couvre plusieurs types de composants. Vous pouvez trier tous les éléments de l'IA avec un seul composant, mais le faire est de mal comprendre le point de rendre les choses granulaires.
Gardez à l'esprit les objectifs: nous voulons faciliter la connexion d'une sorte d'interface graphique pour les non-concepteurs afin de modifier facilement différents types de choses en maximisant et en faisant correspondre les composants dans un paradigme qui leur convient, et nous voulons nous éloigner de des schémas de code arbitraires populaires qui sont beaucoup plus faciles à écrire qu'à modifier ou à maintenir.
Donc, dans JS, peut-être quelque chose comme ça. Les développeurs de jeu, s'il vous plaît, dites-moi si je me trompe horriblement:
Maintenant, chaque fois que vous avez besoin d'un PNJ, vous pouvez construire avec
npcBuilders.<npcName>();
Une interface graphique pourrait se connecter aux objets npcEntities et composants et permettre aux concepteurs de modifier d'anciennes entités ou de créer de nouvelles entités en mélangeant simplement et en faisant correspondre les composants (bien qu'il n'y ait pas de mécanisme pour les composants non par défaut, mais des composants spéciaux pourraient être ajoutés à la volée dans le code tant qu'il y avait un composant défini pour lui.
la source
J'ai lu sur Entity Systems dans les articles que les gens aimables ont fournis dans les commentaires, mais j'avais encore des doutes, alors j'ai posé une autre question .
Tout d'abord, mes définitions étaient fausses. Les entités et les composants ne sont que des détenteurs de données stupides, tandis que les systèmes fournissent toutes les fonctionnalités.
J'ai suffisamment appris pour couvrir la plupart de ma question ici, alors je vais y répondre.
La
Scene
classe dont je parlais ne devrait pas être un système. Il devrait cependant s'agir d'un gestionnaire central qui peut contenir toutes les entités, faciliter les messages et peut-être même gérer les systèmes. Il peut également fonctionner comme une sorte d'usine pour les entités, et j'ai décidé de l'utiliser comme ça. Il peut prendre n'importe quel type d'entité, mais il doit ensuite alimenter cette entité vers un système approprié (qui, pour des raisons de performances, ne doit effectuer aucune vérification de type, sauf s'il est au niveau du bit).Je ne devrais pas utiliser de POO lors de l'implémentation d'un ES, suggère Adam, mais je ne trouve aucune raison de ne pas avoir d'objets avec des méthodes pour les entités et les composants, pas seulement des détenteurs de données stupides.
Le
Renderer
peut simplement être implémenté en tant que système. Il conserverait une liste d'objets dessinables et appellerait ladraw()
méthode de leur composant de rendu toutes les 16 ms.la source
Introduction à la gestion des dépendances 101.
Ce cours suppose que vous avez des connaissances de base sur l'injection de dépendances et la conception de référentiels.
L'injection de dépendance n'est qu'un moyen sophistiqué pour que les objets se parlent (via des messages / signaux / délégués / quoi que ce soit) sans être directement couplés.
Il va par la phrase: "
new
est de la colle."Je vais le démontrer en C #.
Il s'agit d'un système de base pour l'entrée, le mouvement et le rendu. La
Player
classe est l'entité dans ce cas et les composants sont les interfaces. LaPlayer
classe ne sait rien sur les entrailles de la façon dont un bétonIRenderSystem
,IMovementSystem
ou leIInputSystem
travail. Cependant, les interfaces fournissent un moyenPlayer
d'envoyer des signaux (par exemple, invoquer Draw sur le système IRender) sans dépendre de la manière dont le résultat final est atteint.Par exemple, prenez mon implémentation d'IMovementSystem:
MovementSystem
peut avoir ses propres dépendances etPlayer
ne s'en soucierait même pas. En utilisant des interfaces, une machine d'état du jeu peut être créée:Et c'est le début d'un beau jeu (qui est également testable à l'unité).
Et avec quelques ajouts et un certain polymorphisme:
Nous avons maintenant un système entité / composant primitif basé sur des interfaces d'agrégation et un héritage lâche.
la source