L'architettura di Entity Component System è orientata per definizione?


20

È l' architettura di Entity Sistema componenti object oriented, per definizione? Mi sembra più procedurale o funzionale. La mia opinione è che non ti impedisce di implementarlo in un linguaggio OO, ma non sarebbe idiomatico farlo in modo fermamente OO.

Sembra che ECS separa i dati (E & C) dal comportamento (S). Come prova :

L'idea è di non avere metodi di gioco incorporati nell'entità.

E :

Il componente è costituito da un set minimo di dati necessari per uno scopo specifico

I sistemi sono funzioni monouso che accettano un insieme di entità che hanno un componente specifico


Penso che questo non sia orientato agli oggetti perché una grande parte dell'essere orientati agli oggetti sta combinando insieme i tuoi dati e il tuo comportamento. Come prova :

Al contrario, l'approccio orientato agli oggetti incoraggia il programmatore a posizionare i dati dove non sono direttamente accessibili dal resto del programma. Al contrario, si accede ai dati chiamando funzioni appositamente scritte, comunemente chiamate metodi, che sono raggruppati con i dati.

ECS, d'altra parte, sembra essere tutto sulla separazione dei dati dal tuo comportamento.

Risposte:


21

introduzione


I sistemi entità-componente sono una tecnica architettonica orientata agli oggetti.

Non esiste un consenso universale sul significato del termine, lo stesso della programmazione orientata agli oggetti. Tuttavia, è chiaro che i sistemi entità-componente sono specificamente intesi come un'alternativa architettonica all'ereditarietà . Gerarchie di ereditarietà sono naturali per esprimere quello che un oggetto è , ma in certi tipi di software (come i giochi), si preferisce esprimere ciò che un oggetto fa .

È un modello a oggetti diverso da quello di "classi ed ereditarietà" a cui probabilmente sei abituato a lavorare in C ++ o Java. Le entità sono espressive come le classi, proprio come i prototipi come in JavaScript o Self: tutti questi sistemi possono essere implementati l'uno nell'altro.

 

Esempi


Diciamo di lasciare che Playersia un soggetto con Position, Velocitye KeyboardControlledcomponenti, che fanno le cose ovvie.

entity Player:
  Position
  Velocity
  KeyboardControlled

Sappiamo che Positiondevono essere influenzati da Velocitye Velocityda KeyboardControlled. La domanda è: come vorremmo modellare quegli effetti.

 

Entità, componenti e sistemi


Supponiamo che i componenti non abbiano riferimenti reciproci; un Physicssistema esterno attraversa tutti i Velocitycomponenti e aggiorna l' Positionentità corrispondente; un Inputsistema attraversa tutti i KeyboardControlledcomponenti e aggiorna il file Velocity.

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

Questo soddisfa i criteri:

  • Nessuna logica di gioco / business è espressa dall'entità.

  • I componenti memorizzano i dati che descrivono il comportamento.

I sistemi sono ora responsabili della gestione degli eventi e della messa in atto del comportamento descritto dai componenti. Sono anche responsabili della gestione delle interazioni tra entità, come le collisioni.

 

Entità e componenti


Tuttavia, supponiamo che i componenti che fare hanno riferimenti a uno con l'altro. Ora l'entità è semplicemente un costruttore che crea alcuni componenti, li lega insieme e gestisce le loro vite:

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

L'entità potrebbe ora inviare l'input e aggiornare gli eventi direttamente ai suoi componenti. Velocityrisponderebbe agli aggiornamenti e KeyboardControlledrisponderebbe all'input. Questo soddisfa ancora i nostri criteri:

  • L'entità è un contenitore "stupido" che inoltra eventi solo ai componenti.

  • Ogni componente mette in atto il proprio comportamento.

Qui le interazioni tra componenti sono esplicite, non imposte dall'esterno da un sistema. I dati che descrivono un comportamento (qual è la quantità di velocità?) E il codice che lo mette in atto (che cos'è la velocità?) Sono accoppiati, ma in modo naturale. I dati possono essere visualizzati come parametri del comportamento. E alcuni componenti non agiscono affatto: a Positionè il comportamento di trovarsi in un luogo .

Le interazioni possono essere gestite a livello dell'entità ("quando un'entità si Playerscontra con un Enemy...") o a livello di singoli componenti ("quando un'entità con si Lifescontra con un'entità con Strength...").

 

componenti


Qual è il motivo dell'esistenza dell'entità? Se è semplicemente un costruttore, possiamo sostituirlo con una funzione che restituisce un set di componenti. Se in seguito vogliamo interrogare le entità in base al loro tipo, possiamo anche avere un Tagcomponente che ci consente di fare proprio questo:

function Player():
  t = Tag("Player")
  p = Position()
  v = Velocity(p)
  c = KeyboardControlled(v)
  return {t, p, v, c}
  • Le entità sono stupide come possono essere: sono solo insiemi di componenti.

  • I componenti rispondono direttamente agli eventi come prima.

Le interazioni ora devono essere gestite da query astratte, disaccoppiando completamente gli eventi dai tipi di entità. Non ci sono più tipi di entità da interrogare: i Tagdati arbitrari sono probabilmente meglio usati per il debug della logica di gioco.

 

Conclusione


Le entità non sono funzioni, regole, attori o combinatori di flussi di dati. Sono nomi che modellano fenomeni concreti - in altre parole, sono oggetti. È come dice Wikipedia: i sistemi entità-componente sono un modello di architettura software per la modellazione di oggetti generali.


2
La principale alternativa all'OO di classe, OO basata su prototipo, sembra anche abbinare dati e comportamento. In realtà, sembra differire da ECS tanto quanto OO di classe. Quindi potresti elaborare cosa intendi per OO?

Per aggiungere alla domanda di @ delnan, non sei d'accordo con lo snippet dell'articolo di OO di Wikipedia che ho citato?
Daniel Kaplan,

@tieTYT: la citazione di Wikipedia parla di incapsulamento e occultamento delle informazioni. Non credo sia la prova che sia necessario un accoppiamento dati-comportamento, solo che è comune.
Jon Purdy,

@delnan: non intendo nulla per OO. La programmazione orientata agli oggetti, per me, è esattamente ciò che dice sulla carta: programmare con "oggetti" (al contrario di funzioni, regole, attori, combinatori di flussi di dati, ecc.) In cui la definizione particolare di oggetto è definita dall'implementazione.
Jon Purdy,

1
@tieTYT: stavo solo descrivendo implementazioni che ho visto in natura, al fine di comunicare che è un termine ampio, non contraddittorio, ma sicuramente più ampio della descrizione di Wikipedia.
Jon Purdy,

20

NO. E sono sorpreso da quante persone hanno votato diversamente!

Paradigma

È orientato ai dati, noto anche come Data-Driven perché stiamo parlando dell'architettura e non del linguaggio in cui è scritta. Le architetture sono realizzazioni di stili di programmazione o paradigmi , che di solito possono essere sconsigliati in un determinato linguaggio.


Funzionale?

Il confronto con la programmazione funzionale / procedurale è un confronto rilevante e significativo. Si noti, tuttavia, che un linguaggio "funzionale" è diverso dal paradigma "procedurale" . E puoi implementare un ECS in un linguaggio funzionale come Haskell , cosa che le persone hanno fatto.


Dove avviene la coesione

La tua osservazione è pertinente e precisa :

"... [ECS] non ti impedisce di implementarlo in un linguaggio OO, ma non sarebbe idiomatico farlo in modo fermamente OO"


ECS / ES non è EC / CE

Esiste una differenza tra le architetture basate su componenti, "Entity-Component" e "Entity-Component-System". Poiché si tratta di un modello di progettazione in evoluzione, ho visto queste definizioni usate in modo intercambiabile. Le architetture "EC" o "CE" o "Entity-Component" inseriscono il comportamento nei componenti , mentre le architetture "ES" o "ECS" inseriscono il comportamento nei sistemi . Ecco alcuni articoli ECS, entrambi i quali utilizzano una nomenclatura fuorviante, ma fanno capire l'idea generale:

Se stai cercando di comprendere questi termini nel 2015, assicurati che il riferimento di qualcuno a "Sistema di componenti entità" non significhi "Architettura entità-componenti".


1
Questa è la risposta corretta ECS non si adatta molto bene ai paradigmi OOP, perché ECS si basa sulla separazione di dati e comportamento, mentre OOP è all'opposto.
Nax 'vi-vim-nvim'

"mentre OOP è all'opposto" Non esiste una definizione accettata di OOP, a meno che definizioni accademiche inutili come SmallTalk che non vengano mai utilizzate nella pratica.
Jean-Michaël Celerier,

10

I sistemi di componenti entità (ECS) possono essere programmati in modo OOP o funzionale a seconda della definizione del sistema.

Modo OOP:

Ho lavorato su giochi in cui un'entità era un oggetto composto da vari componenti. L'entità ha una funzione di aggiornamento che modifica l'oggetto in atto chiamando a sua volta l'aggiornamento su tutti i suoi componenti. Questo è chiaramente OOP nello stile: il comportamento è collegato ai dati e i dati sono mutabili. Le entità sono oggetti con costruttori / distruttori / aggiornamenti.

Modo più funzionale:

Un'alternativa è che l'entità sia dati senza alcun metodo. Questa entità può esistere a sé stante o essere semplicemente un ID collegato a vari componenti. In questo modo è possibile (ma non fatto comunemente) essere pienamente funzionale e avere entità immutabili e sistemi puri che generano nuovi stati componenti.

Sembra (per esperienza personale) che quest'ultima strada stia guadagnando più trazione e per buoni motivi. La separazione dei dati delle entità dal comportamento comporta un codice più flessibile e riutilizzabile (imo). In particolare, l'uso di sistemi per aggiornare componenti / entità in batch può essere più efficace ed evitare completamente le complessità della messaggistica tra entità che affligge molti ECS OOP.

TLDR: Puoi farlo in entrambi i modi, ma direi che i benefici di buoni sistemi di componenti di entità derivano dalla loro natura più funzionale.


Più trazione, soprattutto perché l'intero punto dei componenti era allontanarsi dalle gerarchie OOP intrattabili, una buona descrizione del vantaggio.
Patrick Hughes,

2

I sistemi di componenti di entità orientati ai dati possono coesistere con paradigmi orientati agli oggetti: - I sistemi di componenti si prestano al polimorfismo. - I componenti possono essere sia POD (semplici vecchi dati) sia ANCHE oggetti (con una classe e metodi) e il tutto è ancora "orientato ai dati", a condizione che i metodi delle classi di componenti manipolino solo i dati di proprietà dell'oggetto locale.

Se scegli questo percorso, ti consiglio di evitare di usare i metodi virtuali, perché se li hai, il tuo componente non è più puramente dati del componente, inoltre quei metodi costano di più da chiamare - questo non è COM. Mantieni le tue classi di componenti pulite da qualsiasi riferimento a qualsiasi cosa esterna, di regola.

Esempio sarebbe vec2 o vec3, un contenitore di dati con alcuni metodi per toccare quei dati e niente di più.


2
questo post è piuttosto difficile da leggere (wall of text). Ti dispiacerebbe modificarlo in una forma migliore? Inoltre, sarebbe utile se spiegassi ai lettori perché potrebbero trovare l'articolo del blog collegato utile e pertinente alla domanda posta ...
moscerino

... nel caso in cui tu sia in qualche modo legato a quel blog (vero?), sarebbe anche auspicabile rivelare l'affiliazione
moscerino

Sì, questo è il mio blog, sono strettamente associato al mio blog pubblico, che rivela i dettagli di un sistema di componenti di entità orientato agli oggetti basato su principi di progettazione orientati ai dati, che ritengo pertinenti e possibilmente utili, ho comunque rimosso il link a rimuovere qualsiasi pregiudizio.
Omero,

2

Penso a ECS come fondamentalmente distinto da OOP e tendo a vederlo nello stesso modo in cui lo fai, più vicino alla natura funzionale o soprattutto procedurale con una separazione molto distinta di dati dalla funzionalità. C'è anche qualche parvenza di programmazione di un tipo che si occupa di database centrali. Ovviamente sono la persona peggiore quando si tratta di definizioni formali. Mi preoccupo solo di come le cose tendono ad essere, non di cosa sono concettualmente definite.

Sto assumendo una specie di ECS in cui i componenti aggregano i campi dati e li rendono accessibili pubblicamente / globalmente, le entità aggregano componenti e sistemi forniscono funzionalità / comportamento su tali dati. Ciò porta a caratteristiche architettoniche radicalmente difficili da ciò che normalmente chiameremmo una base di codice orientata agli oggetti.

E ovviamente c'è un po 'di confusione dei confini nel modo in cui le persone progettano / implementano un ECS, e c'è un dibattito su cosa costituisce esattamente un ECS in primo luogo. Eppure tali confini sono anche confusi nel codice scritto in quelli che chiamiamo linguaggi funzionali o procedurali. Tra tutte queste confusioni, la costante fondamentale di un ECS con una separazione dei dati dalla funzionalità mi sembra molto più vicina alla programmazione funzionale o procedurale rispetto a OOP.

Uno dei motivi principali per cui non credo sia utile considerare ECS come appartenente a una classe di OOP è che la maggior parte delle pratiche SE associate all'OPP ruotano attorno alla stabilità dell'interfaccia pubblica, con funzioni di modellazione delle interfacce pubbliche , non dati. L'idea fondamentale è che la maggior parte delle dipendenze pubbliche fluisce verso funzioni astratte, non verso dati concreti. E per questo motivo, OOP tende a rendere molto costoso modificare i comportamenti di progettazione fondamentali, rendendo al contempo molto economico modificare dettagli concreti (come dati e codice necessari per implementare la funzionalità).

ECS è radicalmente diverso in questo senso considerando come le cose sono accoppiate mentre la maggior parte delle dipendenze pubbliche fluisce verso dati concreti: dai sistemi ai componenti. Di conseguenza, tutte le pratiche SE associate all'ECS ruoterebbero attorno alla stabilità dei dati , poiché le interfacce (componenti) più pubbliche e ampiamente utilizzate sono in realtà solo dati.

Di conseguenza un ECS rende molto facile fare cose come sostituire un motore di rendering OpenGL con uno DirectX, anche se i due sono implementati con funzionalità radicalmente diverse e non condividono gli stessi progetti, purché sia ​​il motore DX che GL avere accesso agli stessi dati stabili. Nel frattempo sarebbe molto costoso e richiederebbe riscrivere un sacco di sistemi per cambiare, diciamo, la rappresentazione dei dati di a MotionComponent.

Questo è molto contrario a ciò che tradizionalmente associamo a OOP, almeno in termini di caratteristiche di accoppiamento e cosa costituisce "interfaccia pubblica" rispetto a "dettagli dell'implementazione privata". Naturalmente in entrambi i casi i "dettagli di implementazione" sono facili da modificare, ma in ECS è la progettazione di dati che è costosa da cambiare (i dati non sono un dettaglio di implementazione in ECS), e in OOP è la progettazione di funzionalità che è costosa da cambiare (la progettazione di funzioni non è un dettaglio di implementazione in OOP). Quindi questa è un'idea molto diversa di "dettagli di implementazione", e uno dei principali appelli a me di un ECS dal punto di vista della manutenzione era che nel mio dominio, i dati necessari per fare le cose erano più facili da stabilizzare e progettare correttamente una volta per tutte in anticipo rispetto a tutte le varie cose che potevamo fare con quei dati (il che cambierebbe continuamente quando i clienti cambiano idea e i nuovi suggerimenti degli utenti vengono inondati). Di conseguenza, ho scoperto che i costi di manutenzione sono precipitati quando abbiamo iniziato a dirigere le dipendenze da funzioni astratte verso dati grezzi e centrali (ma ancora con attenzione a quali sistemi accedono a quali componenti per consentire di mantenere invariati gli invarianti nonostante tutti i dati siano concettualmente accessibile a livello globale).

E almeno nel mio caso, l'SDK ECS con l'API e tutti i componenti sono effettivamente implementati in C e non ha alcuna somiglianza con OOP. Ho trovato C più che adeguato a tale scopo data la mancanza intrinseca di OO nelle architetture ECS e il desiderio di avere un'architettura plug-in che possa essere utilizzata dalla più ampia gamma di linguaggi e compilatori. I sistemi sono ancora implementati in C ++ poiché C ++ rende le cose molto convenienti lì e i sistemi modellano la maggior parte della complessità e lì trovo l'uso per molte cose che potrebbero essere considerate più vicine a OOP, ma questo è per i dettagli di implementazione. Il disegno architettonico stesso ricorda ancora molto procedurale C.

Quindi penso che sia un po 'confuso, almeno, cercare di dire che un ECS è OO per definizione. Per lo meno i fondamenti fanno cose che sono una completa svolta di 180 gradi rispetto a molti dei principi fondamentali generalmente associati con OOP, a partire dall'incapsulamento e forse finendo con quelle che sarebbero considerate le caratteristiche di accoppiamento desiderate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.