Come evitare i "sistemi BLOB" in un sistema di componenti di entità?


10

Attualmente sto affrontando il seguente problema:

Sto cercando di scrivere un clone pong utilizzando un sistema di componenti entità (ECS). Ho scritto il "framework" da solo. Quindi esiste una classe che gestisce le entità con tutti i componenti. Quindi ci sono le classi componenti stesse. E infine ci sono i miei sistemi che ottengono solo tutte le entità che hanno componenti di cui il sistema ha bisogno.

Quindi, ad esempio, il mio sistema di movimento cerca tutte le entità che hanno una componente di posizione e una componente di movimento. Il componente posizione mantiene solo la posizione e il componente movimento mantiene la velocità.

Ma il vero problema è il mio sistema di collisione. Questa classe è come un BLOB logico. Ho così tanti casi speciali in questa classe.

Ad esempio: I miei paddle possono scontrarsi con i bordi. Se ciò accade, la loro velocità è impostata su zero. La mia palla può anche scontrarsi con i bordi. Ma in questo caso la sua velocità è solo speculare alla normale del bordo, quindi viene riflessa. Per fare questo ho dato alla palla un componente fisico aggiuntivo che dice semplicemente: "Ehi, questa cosa non si ferma, riflette". Quindi, in realtà, il componente fisico non ha dati reali. È una classe vuota che è proprio lì per dire al sistema se un oggetto si riflette o si ferma.

Poi arriva questo: voglio rendere alcune particelle quando la palla si scontra con le pagaie o i bordi. Quindi penso che la palla debba ottenere un altro componente che dice al sistema di collisione di creare particelle sulla collisione.
Quindi voglio avere potenziamenti che possono scontrarsi con i paddle ma non con i bordi. Se ciò accade, i power-up devono scomparire. Quindi avrei bisogno di molti più casi e componenti (per dire al sistema che alcune entità possono scontrarsi solo con alcuni altri, il bot non con tutti anche se alcuni altri sono effettivamente in grado di scontrarsi, inoltre il sistema di collisione ha dovuto applicare i power-up a paddle, ecc. ecc. ecc.).

Vedo che il sistema di componenti di entità è una buona cosa perché è flessibile e non si hanno problemi con l'ereditarietà. Ma al momento sono totalmente bloccato.

Sto pensando troppo complicato? Come devo affrontare questo problema?

Certo, devo creare sistemi che sono effettivamente responsabili della "post-collisione", quindi il sistema di collisione dice solo "Sì, abbiamo una collisione nell'ultimo frame" e poi ci sono un sacco di sistemi "post-collisione" che tutti richiedono componenti diversi (combinazioni di) e quindi cambiano i componenti. Ad esempio ci sarebbe un sistema di post-collisione del movimento che blocca le cose che devono fermarsi quando si verifica la collisione. Quindi un sistema di post-collisione fisico che riflette cose, ecc.

Ma questa non sembra nemmeno essere una soluzione adeguata per me, perché ad esempio:

  1. Il mio sistema di post-collisione del movimento avrebbe bisogno di entità che hanno una componente di posizione, una componente di movimento e una componente di collisione. Quindi imposta la velocità dell'entità su zero.
  2. Il sistema fisico post-collisione avrebbe bisogno di entità che hanno un componente di posizione, un componente di movimento, un componente di collisione e un componente di fisica. Quindi rifletterebbe il vettore di velocità.

Il problema è ovvio: il movimento post-collisione ha bisogno di entità che sono un sottoinsieme delle entità nel sistema post-collisione di fisica. Quindi due sistemi post-collisione opererebbero sugli stessi dati, con l'effetto: sebbene un'entità abbia una componente fisica, la velocità sarebbe zero dopo una collisione.

Come vengono risolti questi problemi in generale in un sistema di componenti di entità? Quei problemi sono al solito o sto facendo qualcosa di sbagliato? Se sì, cosa e come dovrebbe essere fatto invece?

Risposte:


11

Sì, stai pensando troppo complicato.

Sembra che molti dei tuoi problemi potrebbero essere risolti con un sistema di messaggistica e alcuni attributi aggiuntivi che ti consentono di specificare alcuni filtri e, infine, non preoccuparti di essere così severo con entità / componenti.

La messaggistica ti aiuterà con alcuni aspetti come l'innesco di particelle, potenziamenti e così via. Ad esempio, potresti avere un oggetto mondo che si iscrive agli eventi delle particelle e crea particelle nella posizione descritta nell'evento.

I filtri ti aiuteranno molto nelle collisioni. I filtri possono definire se un oggetto si scontra con un altro e quale risposta avrà. Aggiungete alcuni attributi al vostro componente fisico che definisce quale tipo di corpo fisico è, con quali altri tipi di corpi fisici si scontra e quale dovrebbe essere la risposta. Ad esempio, un oggetto fisico a sfera si scontra con un oggetto fisico a paletta e risponde con riflessi e particelle.

Infine, non essere così severo sulla tua implementazione. Se riesci a trovare un modo per farlo funzionare, ma non è proprio un sistema EC, fallo. Come nel mio esempio sopra, le particelle non devono essere gestite da un sistema o parte del sistema CE. È più importante finire il gioco piuttosto che seguire rigorosamente un metodo che è già abbastanza mal definito.


Grazie mille. Volevo costruire un gioco usando un ECS solo per vedere come si ridimensiona e se è davvero così bello da usare mentre leggo in articoli e tutorial. Il mio problema era che pensavo: "Ora ho un ECS e tutto deve essere gestito da questo". Quindi ho anche pianificato di scrivere il sistema di particelle in connessione con l'ECS. Inoltre, ho letto in alcuni articoli che ogni componente dovrebbe davvero avere solo alcuni dati di base e niente di più. Questo è spesso il mio problema ... Penso che sia troppo complicato.
M0rgenstern,

Volevo costruire un gioco usando un ECS solo per vedere come si ridimensiona e se è davvero così bello da usare mentre leggo in articoli e tutorial . Se questo è il tuo obiettivo, ti consiglio di guardare i sistemi Component / Entity esistenti invece di costruirne uno tuo. Scarica Unity3D, che probabilmente è "Componenti puri come può" e gioca lì. Approfondimenti molto più veloci, IMHO.
Imi,

3
@lmi: Unity non è un sistema di componenti di entità sebbene sia basato su componenti. ECS ha alcune linee guida piuttosto rigorose ( mai pensare a uno schema come regole) che semplicemente avere e usare componenti di oggetti di gioco. A causa di una serie di articoli ECS è popolare in alcuni segmenti di sviluppatori di giochi in questo momento, quindi ci sono molte domande su ECS in particolare piuttosto che sulla progettazione basata su componenti in generale.
Sean Middleditch,

12

Stai complicando troppo le cose. Vorrei arrivare al punto di dire che anche l'uso del design basato su componenti è eccessivo per un gioco così semplice. Fai le cose nel modo che rende il tuo gioco veloce e facile da sviluppare. I componenti aiutano con l'iterazione in progetti più grandi con una grande varietà di comportamenti e configurazioni di oggetti di gioco, ma i loro vantaggi in un gioco così semplice e ben definito sono più discutibili. Ne ho parlato l'anno scorso: puoi costruire piccoli giochi divertenti in poche ore se ti concentri sulla creazione di un gioco invece di aderire a un'architettura . L'ereditarietà si interrompe quando hai 100 o anche 20 diversi tipi di oggetti, ma funziona bene se ne hai solo una manciata.

Supponendo che tu voglia continuare a utilizzare i componenti a fini di apprendimento, ci sono alcuni ovvi problemi con il tuo approccio che si distinguono.

Innanzitutto, non rendere i componenti così piccoli. Non c'è motivo di avere componenti a grana fine come il "movimento". Non ci sono movimenti generici nel tuo gioco. Hai paddle, il cui movimento è strettamente legato all'input o all'intelligenza artificiale (e non usa realmente velocità, accelerazione, restituzione, ecc.), E hai la palla, che ha un algoritmo di movimento ben definito. Basta avere un componente PaddleController e un componente BouncingBall o qualcosa del genere. Se / quando ottieni un gioco più complicato, puoi preoccuparti di avere un componente PhysicsBody più generico (che nei motori "reali" è fondamentalmente solo un collegamento tra l'oggetto di gioco e qualsiasi oggetto API interno utilizzato da Havok / PhysX / Bullet / Box2D / ecc.) Che gestisce una più ampia varietà di situazioni.

Anche un componente di "posizione" è discutibile, sebbene certamente non insolito. I motori fisici in genere hanno la propria idea interna di dove si trova un oggetto, la grafica potrebbe avere una rappresentazione interpolata e l'IA potrebbe avere ancora un'altra rappresentazione degli stessi dati in uno stato diverso. Può essere vantaggioso lasciare che ogni sistema gestisca la propria idea della trasformazione nei propri componenti e quindi garantire una comunicazione regolare tra il sistema. Vedi il post sul blog BitSquid sui flussi di eventi .

Per i motori di fisica personalizzati ricorda che ti è permesso avere dati sui tuoi componenti. Forse un componente di fisica Pong generico ha dati che indicano su quali assi può muoversi (diciamo, vec2(0,1)come un moltiplicatore per i paddle che possono muoversi solo sull'asse Y e vec2(1,1)per la palla che indica che può muoversi comunque), una bandiera o galleggiante che indica rimbalzo (il palla sarebbe tipicamente a 1.0e pagaie a0.0), caratteristiche di accelerazione, velocità e così via. Cercare di dividere questo in un bazillion di microcomponenti diversi per ogni pezzo di dati altamente correlati è contrario a ciò che ECS doveva originariamente fare. Se possibile, mantieni le cose usate insieme nello stesso componente e suddividile solo quando c'è una grande differenza nel modo in cui ciascun oggetto di gioco utilizza quei dati. C'è un argomento per affermare che per Pong la fisica tra la palla e le pagaie è abbastanza diversa da essere componenti separati, ma per un gioco più grande, ci sono pochi motivi per provare a fare 20 componenti per fare ciò che funziona bene in 1-3.

Ricorda, se / quando la tua versione di ECS si mette in mezzo, fai quello che ti serve per realizzare il tuo gioco e dimenticare l'adesione ostinata a un modello / architettura di design.


1
Grazie davvero! So che un ECS non si adatta molto bene a un gioco piccolo come il pong. Ma l'ho usato solo per vedere come una cosa del genere è effettivamente implementata e come funziona. Ho reso i componenti così piccoli, perché è quello che ho letto principalmente in alcuni articoli. Avere molti componenti e ogni componente contiene solo dati elementari. Quindi, ti capisco bene, che suggerisci di usare un mix tra eredità ed ECS? Come dici tu "la palla e le pagaie sono abbastanza diverse da essere componenti separati". Quindi, per esempio, do loro sia un componente Movimento / Posizione (forse come un componente) sia
M0rgenstern,

1
Basta che funzioni. Concentrati sulla realizzazione del gioco. Avrei letteralmente solo un componente chiamato Ballche contiene tutta la logica della palla come movimento, rimbalzo, ecc. E un Paddlecomponente che accetta input, ma sono io. Qualunque cosa abbia più senso per te, ti toglie di mezzo e ti permetta di creare il gioco è il "modo corretto" di fare le cose.
Sean Middleditch il

3
Avrei letteralmente solo un componente chiamato Ball che contiene tutta la logica della palla come movimento, rimbalzo, ecc. E un componente Paddle che accetta input, ma sono io. Ed è per questo che esiste un'opinione per ogni programmatore su "cos'è un sistema componente". Consiglio di NON farlo come questo suggerimento, tranne per il fatto che stai pensando totalmente nei sistemi Entity classici e sei costretto a usare un sistema Component ma non vuoi vedere quali sono le differenze.
Imi,

2
@lmi: avendo lavorato su alcuni grandi giochi / motori con componenti e visto di persona il motivo per cui usiamo i componenti, no, i componenti eccessivamente granulari sono solo più problemi di quanto valgano. I componenti non sono un proiettile magico; sono uno dei tanti strumenti nella cassetta degli attrezzi di uno sviluppatore di giochi. Usali in un modo e in un luogo che aiutano e non in un modo in cui aggiungono semplicemente più sovraccarico mentale e di runtime al sistema. Se l'unica cosa che ha la fisica della palla è la palla, non vi è alcun vantaggio nel separarla dalle altre proprietà della palla. Se e quando cambia, dividerlo allora e poi solo.
Sean Middleditch,

1
Concordo sul principio di essere pragmatico e di non lasciare che un particolare scalpiccio si metta in mezzo. MA se ECS non è in grado di gestire questo gioco banale senza deviazione, quale speranza c'è per un gioco di grandi dimensioni. Anch'io sto cercando di imparare a usare l'ECS in modo efficace, ma sto cercando di stare il più vicino possibile alla filosofia ECS, altrimenti, una volta che inizierò a fare eccezioni e casi speciali, so che finirò con un pasticcio non mantenibile.
Ken

-2

Secondo me *), il tuo problema più grande con i componenti è questo: i componenti NON sono qui per dire a chiunque altro cosa fare. I componenti sono qui per FARE cose. Non hai un componente solo per contenere la memoria di qualcosa e quindi altri componenti operano su questo. Volete componenti che FANNO cose con i dati che hanno.

Se ti vedi testare la presenza di altri componenti (e quindi chiamare funzioni lì), allora questo è un chiaro segno che una delle due cose è vera:

  • In realtà vuoi invertire la dipendenza: l'altro componente dovrebbe ascoltare eventi / messaggi / trasmissioni / hook / howeveryounamethem per eseguire la loro logica in risposta al tuo componente corrente. Il componente corrente non deve nemmeno sapere che esiste un "altro" componente. Questo è molto spesso il caso, se ti ritrovi a chiamare diversi componenti (anche in diversi altri / blocchi di casi) con funzionalità che non sono realmente connesse al tuo metodo attuale. Pensa a tutti questi Invalidate()o SetDirty()chiama ad altri componenti.
  • Potresti avere troppi componenti. Se due componenti non riescono a vivere l'uno senza l'altro e devono costantemente recuperare i dati e chiamare metodi l'un l'altro, quindi unirli. Ovviamente, le funzionalità che forniscono sono così intrecciate, che in realtà è solo una cosa.

A proposito, questi si applicano a tutti i tipi di sistemi, non solo ai sistemi Entity / Component, ma anche alla classica eredità con semplici "GameObject" o persino funzioni di libreria.

*) Davvero solo il mio . Le opinioni variano molto su Whats Da Real Component Systemz (TM)

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.