Okey, ce que je sais jusqu'à présent; L'entité contient un composant (stockage de données) qui contient des informations telles que; - Texture / sprite - Shader - etc
Et puis j'ai un système de rendu qui dessine tout cela. Mais ce que je ne comprends pas, c'est comment le rendu doit être conçu. Dois-je avoir un composant pour chaque "type visuel". Un composant sans shader, un avec shader, etc?
Vous avez juste besoin de quelques informations sur la "bonne façon" de le faire. Conseils et pièges à surveiller.
Réponses:
Il est difficile de répondre à cette question car chacun a sa propre idée de la manière dont un système de composants d'entité doit être structuré. Le mieux que je puisse faire est de partager avec vous certaines des choses que j'ai trouvées les plus utiles pour moi.
Entité
J'adopte l'approche ECS pour les matières grasses, probablement parce que je trouve que les méthodes de programmation extrêmes sont très inefficaces (en termes de productivité humaine). À cette fin, une entité est pour moi une classe abstraite à hériter de classes plus spécialisées. L'entité a un certain nombre de propriétés virtuelles et un simple indicateur qui me dit si cette entité doit exister ou non. Donc, par rapport à votre question sur un système de rendu, voici à quoi
Entity
ressemble:Composants
Les composants sont "stupides" en ce qu'ils ne font rien ou ne savent rien. Ils n'ont aucune référence à d'autres composants, et ils n'ont généralement pas de fonctions (je travaille en C #, donc j'utilise des propriétés pour gérer les getters / setters - s'ils ont des fonctions, ils sont basés sur la récupération des données qu'ils contiennent).
Les systèmes
Les systèmes sont moins "stupides", mais restent des automates stupides. Ils n'ont aucun contexte du système global, n'ont aucune référence à d'autres systèmes et ne contiennent aucune donnée à l'exception de quelques tampons dont ils peuvent avoir besoin pour effectuer leur traitement individuel. Selon le système, il peut avoir une méthode
Update
ou uneDraw
méthode spécialisée , ou dans certains cas, les deux.Interfaces
Les interfaces sont une structure clé de mon système. Ils sont utilisés pour définir ce qu'un
System
processus peut traiter et ce dont unEntity
est capable. Les interfaces pertinentes pour le rendu sont:IRenderable
etIAnimatable
.Les interfaces indiquent simplement au système quels composants sont disponibles. Par exemple, le système de rendu doit connaître le cadre de sélection de l'entité et l'image à dessiner. Dans mon cas, ce serait le
SpatialComponent
et leImageComponent
. Il ressemble donc à ceci:Le système de rendu
Alors, comment le système de rendu dessine-t-il une entité? C'est en fait assez simple, donc je vais juste vous montrer la classe dépouillée pour vous donner une idée:
En regardant la classe, le système de rendu ne sait même pas ce qu'est un
Entity
. Tout ce qu'il sait, c'estIRenderable
et on lui donne simplement une liste à dessiner.Comment tout cela fonctionne
Il peut également être utile de comprendre comment je crée de nouveaux objets de jeu et comment je les alimente dans les systèmes.
Création d'entités
Tous les objets de jeu héritent de l'entité et de toutes les interfaces applicables qui décrivent ce que cet objet de jeu peut faire. À peu près tout ce qui est animé à l'écran ressemble à ceci:
Nourrir les systèmes
Je garde une liste de toutes les entités qui existent dans le monde du jeu dans une seule liste appelée
List<Entity> gameObjects
. Chaque cadre, je passe ensuite au crible cette liste et copie les références d'objet vers d'autres listes basées sur le type d'interface, telles queList<IRenderable> renderableObjects
, etList<IAnimatable> animatableObjects
. De cette façon, si différents systèmes doivent traiter la même entité, ils le peuvent. Ensuite, je remets simplement ces listes à chacun des systèmesUpdate
ouDraw
méthodes et je laisse les systèmes faire leur travail.Animation
Vous pourriez être curieux de savoir comment fonctionne le système d'animation. Dans mon cas, vous voudrez peut-être voir l'interface IAnimatable:
L'élément clé à noter ici est que l'
ImageComponent
aspect de l'IAnimatable
interface n'est pas en lecture seule; il a un setter .Comme vous l'avez peut-être deviné, le composant d'animation ne contient que des données sur l'animation; une liste d'images (qui sont des composants d'image), l'image actuelle, le nombre d'images par seconde à dessiner, le temps écoulé depuis le dernier incrément d'image et d'autres options.
Le système d'animation tire parti du système de rendu et de la relation entre les composants d'image. Il modifie simplement le composant d'image de l'entité en incrémentant l'image de l'animation. De cette façon, l'animation est rendue indirectement par le système de rendu.
la source
Voir cette réponse pour voir le type de système dont je parle.
Le composant doit contenir les détails de ce qu'il faut dessiner et comment le dessiner. Le système de rendu prendra ces détails et dessinera l'entité de la manière spécifiée par le composant. Ce n'est que si vous utilisiez des technologies de dessin sensiblement différentes que vous disposeriez de composants distincts pour des styles distincts.
la source
La principale raison de séparer la logique en composants est de créer un ensemble de données qui, lorsqu'elles sont combinées dans une entité, produisent un comportement utile et réutilisable. Par exemple, séparer une entité en un composant physique et un composant rendu est logique car il est probable que toutes les entités n'auront pas la physique et certaines entités pourraient ne pas avoir Sprite.
Afin de répondre à votre question, vous devez regarder votre architecture et vous poser deux questions:
Lorsque vous divisez un composant, il est important de poser cette question, si la réponse à 1. est oui, vous avez probablement un bon candidat pour créer deux composants distincts, l'un avec un shader et l'autre avec la texture. La réponse à 2. est généralement oui pour des composants comme Position où plusieurs composants peuvent utiliser position.
Par exemple, la physique et l'audio peuvent utiliser la même position, plutôt que les deux composants stockant des positions en double, vous les refactorisez en un seul PositionComponent et exigez que les entités qui utilisent PhysicsComponent / AudioComponent aient également un PositionComponent.
Sur la base des informations que vous nous avez données, il ne semble pas que votre RenderComponent soit un bon candidat pour se diviser en TextureComponent et ShaderComponent car les shader dépendent entièrement de Texture et de rien d'autre.
En supposant que vous utilisez quelque chose de similaire à T-Machine: Entity Systems, un exemple d'implémentation d'un RenderComponent & RenderSystem en C ++ ressemblerait à ceci:
la source
Piège # 1: code sur-conçu. Réfléchissez si vous avez vraiment besoin de tout ce que vous implémentez, car vous devrez vivre avec pendant un certain temps.
Piège n ° 2: trop d'objets. Je n'utiliserais pas un système avec trop d'objets (un pour chaque type, sous-type et autres) car cela rend le traitement automatisé plus difficile. À mon avis, il est beaucoup plus agréable que chaque objet contrôle un certain ensemble de fonctionnalités (par opposition à une fonctionnalité). Par exemple, la création de composants pour chaque bit de données inclus dans le rendu (composant de texture, composant de shader) est trop divisée - vous auriez généralement besoin d'avoir tous ces composants ensemble de toute façon, n'êtes-vous pas d'accord?
Piège n ° 3: contrôle externe trop strict. Préférez changer les noms en objets shader / texture car les objets peuvent changer avec le rendu / type de texture / format shader / peu importe. Les noms sont de simples identificateurs - c'est au moteur de rendu de décider quoi faire de ceux-ci. Un jour, vous voudrez peut-être avoir des matériaux au lieu de simples shaders (ajoutez des shaders, des textures et des modes de mélange à partir de données, par exemple). Avec une interface textuelle, il est beaucoup plus facile de l'implémenter.
Quant au rendu, il peut s'agir d'une interface simple qui crée / détruit / maintient / rend des objets créés par des composants. La représentation la plus primitive pourrait être quelque chose comme ceci:
Cela vous permettrait de gérer ces objets à partir de vos composants et de les garder suffisamment loin pour vous permettre de les rendre comme vous le souhaitez.
la source