Après avoir posé deux questions sur les systèmes d'entités ( 1 , 2 ), et lu quelques articles à leur sujet, je pense que je les comprends beaucoup mieux qu'avant. J'ai encore quelques incertitudes, principalement sur la construction d'un émetteur de particules, d'un système d'entrée et d'une caméra. J'ai évidemment encore des problèmes pour comprendre les systèmes d'entités, et ils peuvent s'appliquer à une toute autre gamme d'objets, mais j'ai choisi ces trois parce qu'ils sont des concepts très différents, devraient couvrir un terrain assez large et m'aider à comprendre les systèmes d'entités et comment gérer des problèmes comme ceux-ci, moi-même, au fur et à mesure qu'ils surviennent.
Je construis un moteur en JavaScript, et j'ai implémenté la plupart des fonctionnalités de base, qui comprennent: la gestion des entrées, un système d'animation flexible, un émetteur de particules, des classes et des fonctions mathématiques, la gestion des scènes, une caméra et un rendu, et tout un tas d'autres choses que les moteurs prennent généralement en charge. J'ai lu la réponse de Byte56, qui m'a intéressé à faire du moteur un système d'entités. Il resterait toujours un moteur de jeu HTML5, avec la philosophie de base de la scène, mais il devrait prendre en charge la création dynamique d'entités à partir de composants.
Le problème que j'ai, maintenant, est d'adapter mon ancien concept de moteur à ce nouveau paradigme de programmation. Voici quelques définitions des questions précédentes, mises à jour:
Une entité est un identifiant. Il n'a pas de données, ce n'est pas un objet, c'est un simple identifiant qui représente un index dans la liste des scènes de toutes les entités (que j'ai l'intention de mettre en œuvre en tant que matrice de composants).
Un composant est un détenteur de données, mais avec des méthodes qui peuvent fonctionner sur ces données. Le meilleur exemple est un
Vector2D
ou un composant "Position". Il a des données:x
ety
, mais aussi des méthodes qui permettent de manipuler les données sur un peu plus facile:add()
,normalize()
et ainsi de suite.Un système est quelque chose qui peut fonctionner sur un ensemble d'entités qui répondent à certaines exigences; généralement, les entités doivent avoir un ensemble spécifié de composants, pour être exploitées. Le système est la partie "logique", la partie "algorithme", toutes les fonctionnalités fournies par les composants sont purement pour une gestion des données plus simple.
Caméra
La caméra possède une Vector2D
propriété de position, une propriété de rotation et quelques méthodes pour la centrer autour d'un point. Chaque image, elle est alimentée dans un moteur de rendu, avec une scène, et tous les objets sont traduits en fonction de sa position. La scène est ensuite rendue.
Comment pourrais-je représenter ce type d'objet dans un système d'entités? La caméra serait-elle une entité, un composant ou une combinaison (selon ma réponse )?
Emetteur de particules
Le problème que j'ai avec mon émetteur de particules est, encore une fois, ce qui devrait être quoi. Je suis à peu près sûr que les particules elles-mêmes ne devraient pas être des entités, car je veux en prendre en charge plus de 10 000, et je pense que créer autant d'entités serait un coup dur pour mes performances.
Comment pourrais-je représenter ce type d'objet dans un système d'entités?
Gestionnaire d'entrées
Le dernier dont je veux parler est de savoir comment gérer les entrées. Dans ma version actuelle du moteur, il existe une classe appelée Input
. Il s'agit d'un gestionnaire qui s'abonne aux événements du navigateur, tels que les pressions sur les touches et les changements de position de la souris, et maintient également un état interne. Ensuite, la classe player possède une react()
méthode, qui accepte un objet d'entrée comme argument. L'avantage de ceci est que l'objet d'entrée pourrait être sérialisé en .JSON, puis partagé sur le réseau, permettant des simulations multijoueurs fluides.
Comment cela se traduit-il en un système d'entités?
Voici comment je l'ai abordé:
Caméra
Mon appareil photo est une entité comme les autres, qui a des composants attachés:
Transform
aTranslation
,Rotation
et desScale
propriétés, en plus d'autres pour la vitesse, etc.Pov
(Point de vue) aFieldOfView
,AspectRatio
,Near
,Far
, et toute autre chose nécessaire pour produire une matrice de projection, en plus d'unIsOrtho
drapeau utilisé pour basculer entre perspective et projections orthogonales.Pov
fournit également uneProjectionMatrix
propriété de chargement différé utilisée par le système de rendu qui est calculée en interne lors de la lecture et mise en cache jusqu'à ce que les autres propriétés soient modifiées.Il n'y a pas de système de caméra dédié. Le système de rendu conserve une liste de
Pov
fichiers et contient une logique pour déterminer celui à utiliser lors du rendu.Contribution
Un
InputReceiver
composant peut être attaché à n'importe quelle entité. Il dispose d'un gestionnaire d'événements attaché (ou lambda si votre langue le prend en charge) qui est utilisé pour maintenir le traitement d'entrée spécifique à l'entité, qui prend les paramètres pour l'état actuel et précédent de la clé, l'emplacement actuel et précédent de la souris et l'état du bouton, etc. (En fait, il existe des gestionnaires distincts pour la souris et le clavier).Par exemple, dans un jeu de test de type Asteroids que j'ai créé en m'habituant à Entity / Component, j'ai deux méthodes d'entrée lambda. L'un gère la navigation du navire en traitant les touches fléchées et la barre d'espace (pour le tir). L'autre gère la saisie générale du clavier - touches de sortie, pause, etc., niveau de redémarrage, etc. Je crée deux composants, attache chaque lambda à son propre composant, puis attribue le composant récepteur de navigation à l'entité navire, l'autre à un entité de processeur de commande non visible.
Voici le gestionnaire d'événements pour gérer les clés qui sont maintenues entre les cadres qui sont attachés au
InputReceiver
composant du navire (C #):Si votre appareil photo est mobile, lui donner son propre
InputReceiver
etTransform
composant, joindre un lambda ou gestionnaire qui implémente tout type de contrôle que vous voulez, et vous avez terminé.C'est plutôt bien en ce sens que vous pouvez déplacer le
InputReceiver
composant avec le gestionnaire de navigation attaché du vaisseau à un astéroïde, ou autre chose d'ailleurs, et le faire voler à la place. Ou, en affectant unPov
composant à tout autre élément de votre scène - un astéroïde, un lampadaire, etc. - vous pouvez visualiser votre scène du point de vue de cette entité.Une
InputSystem
classe qui conserve un état interne pour le clavier, la souris, etc.InputSystem
filtre sa collection d'entités internes en entités qui ont unInputReceiver
composant. Dans saUpdate()
méthode, il parcourt cette collection et appelle les gestionnaires d'entrée attachés à chacun de ces composants de la même manière que le système de rendu dessine chaque entité avec unRenderable
composant.Particules
Cela dépend vraiment de la façon dont vous prévoyez d'interagir avec les particules. Si vous avez juste besoin d'un système de particules qui se comporte comme un objet - disons, un feu d'artifice montre que le joueur ne peut pas toucher ou frapper - alors je créerais une seule entité et un
ParticleRenderGroup
composant qui contient toutes les informations dont vous avez besoin pour les particules - désintégration, etc. - qui n'est pas couvert par votreRenderable
composant. Lors du rendu, le système de rendu verrait si une entité a l'RenderParticleGroup
attaché et le gérerait en conséquence.Si vous avez besoin de particules individuelles pour participer à la détection des collisions, répondre aux entrées, etc., mais que vous souhaitez simplement les rendre sous forme de lot, je créerais un
Particle
composant qui contient ces informations par particule, et les créer comme entités distinctes. Le système de rendu peut toujours les regrouper, mais ils seront traités comme des objets séparés par les autres systèmes. (Cela fonctionne très bien avec l'instanciation.)Ensuite, soit dans votre
MotionSystem
(ou quelle que soit votre utilisation qui gère la mise à jour de la position de l'entité, etc.) ou dans dédiéParticleSystem
, effectuez le traitement requis pour chaque particule par image. LeRenderSystem
serait responsable de la création / mise en lots et de la mise en cache des collections de particules au fur et à mesure qu'elles sont créées et détruites, et les rendrait selon les besoins.Une bonne chose à propos de cette approche est que vous n'avez pas à avoir de cas particuliers pour les collisions, l'abattage, etc. pour les particules; ils codent que vous écrivez pour tout autre type d'entité peut toujours être utilisé.
Conclusion
Si vous envisagez de passer à plusieurs plates-formes - non super-applicables à JavaScript - tout votre code spécifique à la plate-forme (à savoir, le rendu et la saisie) est isolé dans deux systèmes. Votre logique de jeu reste dans les classes agnositiques à la plateforme (mouvement, collision, etc.), vous ne devriez donc pas avoir à les toucher lors du portage.
Je comprends et suis d'accord avec la position de Sean selon laquelle les éléments de taille de chaussures dans un modèle afin d'adhérer strictement au modèle, plutôt que de modifier le modèle pour répondre aux besoins de votre application, sont mauvais. Je ne vois rien dans Input, Camera ou Particles qui nécessite ce type de traitement.
la source
La logique d'entrée et de jeu sera probablement gérée dans un morceau de code dédié en dehors du système de composants d'entité. Il est techniquement possible de l'intégrer dans la conception, mais il y a peu d'avantages - la logique du jeu et l'interface utilisateur sont hacky et pleins d'abstractions qui fuient quoi que vous fassiez, et essayer de forcer la cheville carrée dans un trou rond juste pour la pureté architecturale est un gaspillage de temps.
De même, les émetteurs de particules sont des bêtes spéciales, surtout si vous vous souciez de la performance. Un composant émetteur est logique, mais les graphiques vont faire de la magie spéciale avec ces composants, mélangés avec la magie pour le reste du rendu.
En ce qui concerne votre caméra, donnez simplement aux caméras un drapeau actif et peut-être un index de "profondeur", et laissez le système graphique rendre toutes celles qui sont activées. C'est en fait pratique pour de nombreuses astuces, y compris les interfaces graphiques (vous voulez que votre interface graphique soit rendue en mode orthographique au-dessus du monde du jeu? Pas de problème, ce ne sont que deux caméras avec des masques d'objets différents et une interface graphique définie sur une couche supérieure). Il est également utile pour les calques d'effets spéciaux et autres.
la source
Je ne sais pas vraiment ce que cette question demande. Étant donné que les seules choses que vous avez dans le jeu sont des entités, les caméras doivent être des entités. La fonctionnalité de la caméra est implémentée via une sorte de composant de caméra. Ne pas avoir de composants "Position" et "Rotation" séparés - c'est un niveau beaucoup trop bas. Ils devraient être combinés en une sorte de composant WorldPosition qui s'appliquerait à toute entité située dans le monde. Quant à savoir lequel utiliser ... vous devez d'une manière ou d'une autre intégrer la logique dans le système. Soit vous le codez en dur dans votre système de gestion de caméra, soit vous joignez des scripts, ou quelque chose. Vous pouvez avoir un indicateur activé / désactivé sur un composant de caméra si cela vous aide.
Moi aussi. Un émetteur de particules serait une entité et le système de particules suivrait les particules associées à une entité donnée. Des choses comme celle-ci sont où vous réalisez que "tout est une entité" est absurdement impossible. En pratique, les seules choses qui sont des entités sont des objets relativement complexes qui bénéficient de combinaisons de composants.
Quant à Input: l'entrée n'existe pas dans le monde du jeu en tant que tel, elle est donc gérée par un système. Pas nécessairement un «système de composants» car tout dans votre jeu ne tourne pas autour des composants. Mais il y aura un système d'entrée. Vous voudrez peut-être marquer l'entité qui répond à l'entrée avec une sorte de composant Player, mais l'entrée va être complexe et complètement spécifique au jeu, il est donc inutile d'essayer de créer des composants pour cela.
la source
Voici quelques-unes de mes idées pour résoudre ces problèmes. Ils auront probablement quelque chose qui ne va pas avec eux, et il y aura probablement une meilleure approche, alors s'il vous plaît, dirigez-moi vers ceux dans votre réponse!
Appareil photo :
Il existe un composant "Caméra", qui peut être ajouté à n'importe quelle entité. Je ne peux pas vraiment comprendre quelles données dois-je mettre dans ce composant, cependant: je pourrais avoir des composants séparés "Position" et "Rotation"! La
follow
méthode n'a pas besoin d'être implémentée, car elle suit déjà l'entité à laquelle elle est attachée! Et je suis libre de le déplacer. Le problème avec ce système serait de nombreux objets de caméra différents: commentRendererSystem
savoir lesquels utiliser? Et aussi, je passais juste l'objet caméra autour, mais maintenant il semble que leRendererSystem
besoin de répéter deux fois sur toutes les entités: d'abord pour trouver celles agissant comme des caméras, et deuxièmement, pour réellement tout rendre.Émetteur de particules :
Il y aurait un
ParticleSystem
qui mettrait à jour toutes les entités qui avaient un composant "Emitter". Les particules sont des objets muets dans un espace de coordonnées relatif, à l'intérieur de ce composant. Il y a un problème de rendu ici: il me faudrait soit faire unParticleRenderer
système, soit étendre les fonctionnalités de l'existant.Système d'entrée :
La principale préoccupation pour moi ici était la logique ou la
react()
méthode. La seule solution que j'ai trouvée est un système séparé pour cela, et un composant pour chaque système, qui indiquerait lequel utiliser. Cela semble vraiment trop hacky, et je ne sais pas comment le gérer correctement. Une chose est que, tant que je suis concerné, leInput
peut rester implémenté en tant que classe, mais je ne vois pas comment pourrais-je l'intégrer au reste du jeu.la source