web-dev-qa-db-fra.com

L'architecture Entity Component System est-elle orientée par définition?

L'architecture Entity Component System est-elle orientée objet, par définition? Cela me semble plus procédural ou fonctionnel. Mon avis est que cela ne vous empêche pas de l'implémenter dans une langue OO, mais il ne serait pas idiomatique de le faire de manière résolue OO façon .

Il semble que ECS sépare les données (E & C) du comportement (S). Comme preuve :

L'idée est de n'avoir aucune méthode de jeu intégrée dans l'entité.

Et :

Le composant consiste en un ensemble minimal de données nécessaires à un usage spécifique

Les systèmes sont des fonctions à usage unique qui prennent un ensemble d'entités qui ont un composant spécifique


Je pense que ce n'est pas orienté objet car une grande partie de l'orientation objet consiste à combiner vos données et votre comportement. Comme preuve :

En revanche, l'approche orientée objet encourage le programmeur à placer des données là où elles ne sont pas directement accessibles par le reste du programme. Au lieu de cela, les données sont accessibles en appelant des fonctions spécialement écrites, communément appelées méthodes, qui sont regroupées avec les données.

ECS, d'autre part, semble être tout au sujet de séparer vos données de votre comportement.

20
Daniel Kaplan

Introduction


Les systèmes à composants d'entité sont une technique architecturale orientée objet.

Il n'y a pas de consensus universel sur ce que le terme signifie, de même que la programmation orientée objet. Cependant, il est clair que les systèmes à composants d'entité sont spécifiquement conçus comme une alternative architecturale à héritage. Les hiérarchies d'héritage sont naturelles pour exprimer ce qu'est un objet est, mais dans certains types de logiciels (tels que les jeux), vous préférez exprimer ce qu'est un objet fait.

Il s'agit d'un modèle d'objet différent de celui des "classes et héritage" auquel vous êtes probablement habitué à travailler en C++ ou Java. Les entités sont aussi expressives que les classes, tout comme les prototypes comme en JavaScript ou Self - tous ces systèmes peuvent être implémentés les uns par rapport aux autres.

Exemples


Disons que Player est une entité avec les composants Position, Velocity et KeyboardControlled, qui font les choses évidentes.

entity Player:
  Position
  Velocity
  KeyboardControlled

Nous savons que Position doit être affecté par Velocity et Velocity par KeyboardControlled. La question est de savoir comment nous aimerions modéliser ces effets.

Entités, composants et systèmes


Supposons que les composants n'aient aucune référence l'un à l'autre; un système Physics externe traverse tous les composants Velocity et met à jour le Position de l'entité correspondante; un système Input traverse tous les composants KeyboardControlled et met à jour le Velocity.

          Player
         +--------------------+
         | Position           | \
         |                    |  Physics
       / | Velocity           | /
  Input  |                    |
       \ | KeyboardControlled |
         +--------------------+

Cela répond aux critères:

  • Aucune logique de jeu/métier n'est exprimée par l'entité.

  • Les composants stockent des données décrivant le comportement.

Les systèmes sont désormais responsables de la gestion des événements et enacting du comportement décrit par les composants. Ils sont également chargés de gérer les interactions entre les entités, telles que les collisions.

Entités et composants


Supposons cependant que les composants do aient des références les uns aux autres. Maintenant, l'entité est simplement un constructeur qui crée certains composants, les lie et gère leur durée de vie:

class Player:
  construct():
    this.p = Position()
    this.v = Velocity(this.p)
    this.c = KeyboardControlled(this.v)

L'entité peut maintenant envoyer des événements d'entrée et de mise à jour directement à ses composants. Velocity répondrait aux mises à jour et KeyboardControlled répondrait aux entrées. Cela répond toujours à nos critères:

  • L'entité est un conteneur "stupide" qui ne transmet que les événements aux composants.

  • Chaque composant adopte le comportement son propre.

Ici, les interactions entre composants sont explicites, non imposées de l'extérieur par un système. Les données décrivant un comportement (quelle est la quantité de vitesse?) Et le code qui le décrète (quelle est la vitesse?) Sont couplées, mais de manière naturelle. Les données peuvent être considérées comme des paramètres du comportement. Et certains composants n'agissent pas du tout - un Position est le comportement de être dans un endroit.

Les interactions peuvent être gérées au niveau de l'entité ("lorsqu'une Player entre en collision avec une Enemy…") ou au niveau des composants individuels ("lorsqu'une entité avec Life entre en collision avec une entité avec Strength… ”).

Composants


Quelle est la raison pour laquelle l'entité existe? S'il ne s'agit que d'un constructeur, nous pouvons le remplacer par une fonction renvoyant un set de composants. Si nous voulons plus tard interroger des entités par leur type, nous pouvons tout aussi bien avoir un composant Tag qui nous permet de faire exactement cela:

function Player():
  t = Tag("Player")
  p = Position()
  v = Velocity(p)
  c = KeyboardControlled(v)
  return {t, p, v, c}
  • Les entités sont aussi stupides que possible - ce ne sont que des ensembles de composants.

  • Les composants répondent directement aux événements comme précédemment.

Les interactions doit maintenant être gérées par des requêtes abstraites, découplant complètement les événements des types d'entités. Il n'y en a plus types d'entités à interroger — les données arbitraires Tag sont probablement mieux utilisées pour le débogage que la logique du jeu.

Conclusion


Les entités ne sont pas des fonctions, des règles, des acteurs ou des combinateurs de flux de données. Ce sont noms qui modélisent des phénomènes concrets - en d'autres termes, ce sont des objets. C'est comme le dit Wikipedia - les systèmes à composants d'entité sont un modèle d'architecture logicielle pour la modélisation d'objets généraux.

22
Jon Purdy

NON. Et je suis surpris du nombre de personnes qui ont voté autrement!

Paradigme

C'est orienté données aka piloté par les données parce que nous parlons de l'architecture et non de la langue dans laquelle elle est écrite. Architectures are réalisations de styles de programmation ou paradigmes , qui peuvent généralement être contournés dans un langage donné.


Fonctionnel?

Votre comparaison avec la programmation fonctionnelle / procédurale est une comparaison pertinente et significative. Notez cependant qu'un langage "fonctionnel" est différent du paradigme "procédural" . Et vous pouvez implémenter un ECS dans un langage fonctionnel comme Haskell , ce que les gens ont fait.


Là où la cohésion se produit

Votre observation est pertinente et ponctuelle :

"... [ECS] ne vous empêche pas de l'implémenter dans une langue OO, mais il ne serait pas idiomatique de le faire de manière résolue OO = chemin "


ECS/ES n'est pas EC/CE

Il existe une différence entre les architectures basées sur les composants, "Entity-Component" et "Entity-Component-System". Comme il s'agit d'un modèle de conception en évolution, j'ai vu ces définitions utilisées de manière interchangeable. Architectures "EC" ou ou "CE" ou "Entity-Component" mettre le comportement dans les composants, alors que les architectures "ES" ou "ECS" mettre le comportement dans les systèmes. Voici quelques articles ECS, qui utilisent tous deux une nomenclature trompeuse, mais donnent une idée générale:

Si vous essayez de comprendre ces termes en 2015, assurez-vous que la référence de quelqu'un à "Entity Component System" ne signifie pas "Entity-Component architecture".

18
pup

Les systèmes de composants d'entité (ECS) peuvent être programmés de manière OOP ou fonctionnelle) selon la façon dont le système est défini.

Manière OOP:

J'ai travaillé sur des jeux où une entité était un objet composé de divers composants. L'entité a une fonction de mise à jour qui modifie l'objet en place en appelant successivement update sur tous ses composants. C'est clairement OOP dans le style - le comportement est lié aux données, et les données sont modifiables. Les entités sont des objets avec des constructeurs/destructeurs/mises à jour.

Manière plus fonctionnelle:

Une alternative est que l'entité soit des données sans aucune méthode. Cette entité peut exister à part entière ou simplement être un identifiant lié à divers composants. De cette façon, il est possible (mais ce n'est pas courant) d'être pleinement fonctionnel et d'avoir des entités immuables et des systèmes purs qui génèrent de nouveaux états de composants.

Il semble (par expérience personnelle) que cette dernière voie gagne en traction et pour une bonne raison. La séparation des données d'entité du comportement entraîne un code plus flexible et réutilisable (imo). En particulier, l'utilisation de systèmes pour mettre à jour des composants/entités par lots peut être plus performante et évite complètement les complexités de la messagerie interentités qui affligent de nombreux ECS OOP).

TLDR: Vous pouvez le faire dans les deux cas, mais je dirais que les avantages de bons systèmes de composants d'entité découlent de leur nature plus fonctionnelle.

10
AGD

Je pense que ECS est fondamentalement distinct de OOP et j'ai tendance à le voir de la même manière que vous, plus proche de la nature fonctionnelle ou surtout procédurale avec une séparation très distincte des données de la fonctionnalité. Il y a aussi un semblant de programmation d'un genre traitant des bases de données centrales. Bien sûr, je suis la pire personne en ce qui concerne les définitions formelles. Je me préoccupe uniquement de la façon dont les choses ont tendance à être, pas de ce qu'elles sont définies conceptuellement.

Je suppose une sorte d'ECS où les composants regroupent les champs de données et les rendent accessibles au public/au niveau mondial, les entités agrègent les composants et les systèmes fournissent des fonctionnalités/un comportement sur ces données. Cela conduit à des caractéristiques architecturales radicalement difficiles à partir de ce que nous appellerions généralement une base de code orientée objet.

Et bien sûr, il y a un certain flou des limites dans la façon dont les gens conçoivent/implémentent un ECS, et il y a un débat sur ce qui constitue exactement un ECS en premier lieu. Pourtant, ces limites sont également floues dans le code écrit dans ce que nous appelons les langages fonctionnels ou procéduraux. Parmi tous ces flous, la constante fondamentale d'un ECS avec une séparation des données de la fonctionnalité me semble beaucoup plus proche de la programmation fonctionnelle ou procédurale que la POO.

L'une des principales raisons pour lesquelles je ne pense pas qu'il soit utile de considérer ECS comme appartenant à une classe de OOP est que la plupart des pratiques SE associées à OOP tournent autour de la stabilité de l'interface publique, avec des fonctions de modélisation des interfaces publiques , pas des données. L'idée fondamentale est que la majeure partie des dépendances publiques va vers des fonctions abstraites, pas vers des données concrètes. Et à cause de cela, OOP tend à rendre très coûteux le changement des comportements de conception fondamentaux, tout en rendant très bon marché la modification de détails concrets (comme les données et le code requis pour implémenter la fonctionnalité).

ECS est radicalement différent à cet égard, compte tenu de la façon dont les choses sont couplées alors que la majeure partie des dépendances publiques circulent vers des données concrètes: des systèmes aux composants. Par conséquent, toutes les pratiques SE associées à ECS tourneraient autour de la stabilité des données , car les interfaces (composants) les plus publiques et les plus utilisées sont en fait uniquement des données.

En conséquence, un ECS rend très facile de faire des choses comme substituer un moteur de rendu OpenGL à un DirectX, même si les deux sont implémentés avec des fonctionnalités radicalement différentes et ne partagent pas les mêmes conceptions, à condition que le DX et le GL le moteur a accès aux mêmes données stables. En attendant, cela coûterait très cher et nécessiterait de réécrire un tas de systèmes pour changer, disons, la représentation des données d'un MotionComponent.

C'est très opposé à ce que nous associons traditionnellement à la POO, au moins en termes de caractéristiques de couplage et de ce qui constitue une "interface publique" par rapport à des "détails d'implémentation privés". Bien sûr, dans les deux cas, les "détails d'implémentation" sont faciles à modifier, mais dans ECS, c'est la conception des données qui est coûteuse à modifier (les données ne sont pas un détail d'implémentation dans ECS), et dans OOP c'est la conception de fonctionnalités coûteuses à modifier (la conception des fonctions n'est pas un détail d'implémentation dans la POO). C'est donc une idée très différente des "détails de mise en œuvre", et l'un des principaux attraits pour moi d'un ECS du point de vue de la maintenance était que dans mon domaine, les données nécessaires pour faire les choses étaient plus faciles à stabiliser et à concevoir correctement une fois pour toutes. à l'avance que toutes les différentes choses que nous pourrions faire avec ces données (qui changeraient tout le temps à mesure que les clients changeaient d'avis et que de nouvelles suggestions d'utilisateurs arrivaient). En conséquence, j'ai trouvé les coûts de maintenance en chute libre lorsque nous avons commencé à orienter les dépendances des fonctions abstraites vers des données brutes et centrales (mais toujours avec soin quels systèmes accèdent à quels composants pour permettre de maintenir les invariants à un degré raisonnable malgré toutes les données conceptuellement étant accessible au niveau mondial).

Et dans mon cas au moins, le SDK ECS avec l'API et tous les composants sont réellement implémentés en C et ne ressemble en rien à la POO. J'ai trouvé C plus que suffisant pour un tel objectif étant donné le manque inhérent de OO dans les architectures ECS et le désir d'avoir une architecture de plugin qui peut être utilisée par la plus large gamme de langages et de compilateurs. Les systèmes sont toujours implémentés en C++ car C++ rend les choses très pratiques là-bas et les systèmes modélisent la majeure partie de la complexité et là je trouve utile pour beaucoup de choses qui pourraient être considérées plus proches de la POO, mais c'est pour les détails d'implémentation. La conception architecturale elle-même ressemble toujours à un C. très procédural.

Je pense donc qu'il est un peu déroutant, à tout le moins, d'essayer de dire qu'un ECS est OO par définition. À tout le moins, les principes fondamentaux font un virage complet à 180 degrés par rapport à bon nombre des principes fondamentaux généralement associés à la POO, à commencer par l'encapsulation et peut-être se terminant par ce qui serait considéré comme les caractéristiques de couplage souhaitées.

3
user204677

Les systèmes de composants d'entité orientés données peuvent coexister avec des paradigmes orientés objet: - Les systèmes de composants se prêtent au polymorphisme. - Les composants peuvent être à la fois des objets POD (plain old data) et ALSO (avec une classe et des méthodes), et le tout est toujours `` orienté données '', à condition que les méthodes de classe de composants manipulent uniquement les données appartenant à l'objet local.

Si vous choisissez ce chemin, je vous recommande d'éviter d'utiliser des méthodes virtuelles, car si vous les avez, votre composant n'est plus uniquement des données de composant, plus ces méthodes coûtent plus cher à appeler - ce n'est pas COM. Gardez vos classes de composants propres de toute référence à tout élément externe, en règle générale.

L'exemple serait vec2 ou vec3, un conteneur de données avec quelques méthodes pour toucher ces données, et rien de plus.

2
Homer