Skip to content

Instantly share code, notes, and snippets.

@andijcr
Last active July 17, 2018 13:55
Show Gist options
  • Save andijcr/c7eb06092ae3ff460973f2e6c3428157 to your computer and use it in GitHub Desktop.
Save andijcr/c7eb06092ae3ff460973f2e6c3428157 to your computer and use it in GitHub Desktop.
aperitalk scratchpad
# chi sono
[contatti]
ciao sono andrea, lavoro qui a 2hire come sviluppatore embedded e smontacose.
Visto che chi non sa fare insegna, oggi voglio parlarvi di entity component system e perché penso sia veramente bello
e perché non l'ho mai usato
# survey
vedo un po' di facce conosciute.
quanti di voi sono programmatori puri?
quanti sono programmatori per necessità di videogioco?
quanti conoscono il pattern Entity Component system?
quanti usano un engine per i propri lavori?
# obiettivo
perché chiedo questo?
ecs è alla base di tantissimi prodotti in ambito videogiochi, e credo che conoscere un po' i meccanismi che fanno funzionare queste macchine, ci renda persone migliori e possa risolvere la fame nel mondo.
Per chi prima ha detto di essere un programmatore puro, che non usa engine per i suoi giochi, e che non conosce ecs, spero di riuscirvi a mostrare qualcosa di nuovo.
# antefatto [warning: fictonalization]
2 gamejam fà mi sono fatto convincere da Andrea a partecipare, a suon di parolacce
insieme a sarah, bruno, tsuneo e massimo ci eravamo dati appuntamento al pub per condividere un po' di idee e decidere quale tecnologia avremmo usato
io per l'occasione io avevo deciso finalmente di imparare love2d e ecs, ed ero molto deciso a condividerli con gli altri
Fu una bella sessione di brainstorming, si buttarono giù molte idee e avevamo anche una bozza di un possibile gameplay (lo so, non si fa)
# un problema [warning: informatica]
Problema: vogliamo modellare un sistema dove molteplici agenti interagiscono fra di loro contemporaneamente, e con un certo grado di autonomia
per esempio: in un videogioco abbiamo il personaggio di un giocatore, e dei nemici che si muovono a random
# proposta 1
ecco come avrei affrontato io il problema, un po' di tempo fa
- è la mia prospettiva da programmatore che non vuole usare engine
modelliamo una classe player, nel suo metodo update leggiamo gli input per muoverlo. modelliamo una classe npc, e nel suo metodo lo facciamo muovere a random
# aggiunta
se il giocatore tocca un nemico, perde
# proposta 1.1
nel loop del gioco facciamo if(player touches enemy) die()
# aggiunta
i nemici non si muovono a random, ma vanno verso il giocatore
# proposta 1.2
la classe npc riceve il giocatore, ad ogni update si muove verso la sua posizione
^ pessimo design
# aggiunta
quando il giocatore attiva la sua arma, i nemici dietro di lui esplodono
# proposta 1.3
nel loop del gioco mettiamo un if(x is pressed){for( e in enemies){if(e is behind player) die(e)}}
# cosa abbiamo finora
come potete immaginare questo è un design che diventa sempre più ingarbugliato, con logica sparsa un po' ovunque, oggetti che non si sa a chi appartengono, bug che compaiono in maniera imbarazzante.
Quale è il problema? aggiungere un comportamento diventa sempre più difficile, i dati sono inutilmente ridondanti.
questo design va bene quando abbiamo un solo giocatore, 2/3 nemici, ed un tutorial per qualche engine js/html5, ma possiamo allargare il nostro mondo solo spendendo molte bestemmie
# il problema
il problema di fondo del design di prima è che un comportamento (muoversi verso un obiettivo, esplodere) è mezzo mischiato con i dati su cui opera (la propria posizione, l'obiettivo, un evento) e questi piccoli pezzi di codice sono replicati un po' di volte.
# ecs
(non ho controllato wikipedia, garantito sto dicendo delle cazzate)
ecs dice:
il mondo è popolato da esseri, che si chiamano entità. essi hanno una identità e poco più.
nel mondo ci stanno anche leggi, chiamate sistemi, che governano come le entità si evolvono da un istante di tempo all'altro.
ad esempio: un sistema dice che se ho un obiettivo, devo "spostarmi verso di esso",
un'altra dice che "spostarmi" significa modificare la mia "posizione" con un vettore,
una terza dice che se c'è una esplosione vicino a me, allora vengo distrutto
# ecs -cont
i Sistemi interagiscono con le entità? No! ogni sistema interagisce con il suo tipo di dato, che è chiamato Componente.
Dove stanno questi Componenti? ogni Entità è un contenitore di tutti i Componenti che ne devono determinare il comportamento
In pratica cerchiamo di disaccoppiare completamente i dati dal codice, ed eliminiamo le dipendenze fra i vari pezzi di codice
# esempio
Modelliamo il nostro gioco con Entità{posizione, grafica, vita, input, nome="player"}, e tanti nemici modellati come Entità{posizione, obiettivo, grafica, nome="npc..."}
dove posizione, grafica, vita, input, obiettivo sono i nostri Componenti
poi creiamo un Sistema<posizione, input> che modifica la posizione a seconda dell'input, per permettere all'utente di controllare Qualcosa
un Sistema<posizione, obiettivo> che avvicina la posizione all'obiettivo, per far muovere Qualcosa
questi sono sistemi semplici, delle funzioni, che fanno evolvere le entità singolarmente
# esempio 2
e per modellare le interazioni fra entità?
una soluzione (una delle tante) è avere un sistema per verificare se una interazione sta avvenendo, e se si creare al volo una entità che descrive questa interazione. Sui componenti di questa nuova Entità "evento" possiamo triggerare tutti i sistemi che ci pare.
per esempio: potremmo avere un Sistema<posizione, collidibile> che verifica quali entità collidono e genera una Entità{collisione, entità_a, entità_b} per ogni nuova collisione, un Sistema<collisione> che cambia il colore delle entità che collidono, un Sistema<collisione> che azzera la vita delle entità, un Sistema<vita> che uccide l'entità con vita zero, e un Sistema<collisione> che distrugge l'entità "evento", in modo da consumare gli eventi ad ogni turno
# sembra complicato?
si, è un po' complicato. sopratutto perché l'istinto naturale del "prima programma e poi chiedi scusa" qui viene sovvertito: prima di programmare c'è del lavoro da fare per individuare entità, componenti e sistemi, e cercare di evitare l'errore di nascondere dati dentro i sistemi, e comportamenti dentro le entità
# ma il mondo stesso è complicato
in realtà però il problema di architettura, il problema di spendere del tempo a sistemare il codice, è un problema che ad un certo punto qualsiasi progetto deve affrontare.
Chi ha già avuto un progetto personale, ad uno stato medio/avanzato sà quanto è doloroso il debito tecnico di copiare e incollare il codice in giro
# cosa non sto dicendo
ecs, come lo ho illustrato qui, è un meccanismo a basso livello per organizzare lo sviluppo, ma non è ancora chiaro come farlo funzionare nella pratica.
un approccio (semplicistico e poco ottimale) è:
nel setup, creo le Entità con i loro Componenti, e li salvo in una lista master.
nel loop, per ogni Sistema itero la lista, ed eseguo il sistema sulla entità se questa soddisfa il suo filtro (per esempio il Sistema<posizione, texture> si attiva solo per le entità con questi due componenti, e disegna a schermo una texture alla posizione indicata)
ovviamente c'è un discorso dell'ordine con cui attivo i sistemi, e un possibile ordinamento delle entità, che sto accuratamente evitando.
# un approccio più sensato
usate un motore di entity component system che vi renda la vita semplice.
La mia esperiza è limitata, ho provato love2d in congiunzione con tiny.
per chi non lo conoscesse love2d è un motore molto minimale che si programma in Lua, impone pochissime restrizioni ed ha delle performance ottime per videogiochi 2d
tiny-ecs invece è una piccola libreria http://bakpakin.github.io/tiny-ecs/doc/ per ecs con delle feature che considero essenziali per chi si vuole divertire ad usare questo pattern manualmente:
(premessa: in lua esiste solo la table, che è un mix fra dizionario e oggetto, con capacità avanzate)
le entità sono delle semplici table, e i componenti sono valori all'interno di questa table con un loro nome
i sistemi sono delle tabelle con ua interfaccia semplice, e tiny include delle utility per scrivere i filtri con cui i sistemi selezionano le entità a cui sono interessati
l'ordine con cui i sistemi sono aggiunti al motore serve ad indicare l'ordine con cui vengono eseguiti - per esempio, i system responsabili per eliminare entità devono essere aggiunti alla fine dello stack, per fare cleanup ad ogni update
infine, per far girare il tutto basta chiamare update(dt) ad ogni frame
molto semplice, poco minaccioso
# chi altro usa ecs
ecs come abbiamo visto può essere molto macchinoso da setuppare (scusate il verbo), e penso che sia per questo un paio di anni fa non trovai molte librerie open che fossero mantenute, utilizzabili e non antagoniste.
i motori come love2d o processing, che sono nati per creative coding sono diretti ad una audience che probabilmente si avvicina alla programmazione, e ha già troppo a cui pensare
mentre i motori veri e propri, come unity o i vari js+html5 sono diretti ad un pubblico che è più interessato a creare contenuti, piuttosto che addentrarsi dentro questi dettagli architetturali (a ragione, penso)
Però è innegabile che molti progetti "complessi" usano una qualche forma di ecs, più o meno esposta all'utente.
Tra l'altro sospetto (non ho aperto il codice) che box2d sia scritto con la tecnica ecs, per cui chiunque ha usato questo motore fisico può dire di aver toccato almeno una volta ecs.
# svantaggi
cambio di prospettiva, bisogna imparare a distinguere dati da comportamenti
una certa quantità di boilerplate code (se non si usa una libreria o questa libreria è antagonista)
alcuni concetti possono essere difficili da tradurre in un mondo ecs, ma di solito è possibile creare soluzioni "ibride" (ma attenzione a non cadere nello spaghetti code)
non adatto a tutti i tipi di videogiochi
non adatto a molto altro, al di fuori del mondo dei videogiochi e delle simulazioni
# perché è figo
performace possono migliorare, con una opportuna implementazione (data driven programming credo si chiami?)
mix and match: voglio permetter ad un utente di controllare un vaso di gerani? basta aggiungere il componente isPlayer all'entità del vaso, ed ecco che il sistema input comanderà il vado
salvataggio: quanto tutto il tuo stato è contenuto nelle entità, e il gioco è deterministico, basta serializzare la lista delle entità per salvare il gioco
è un approccio "scalabile"
è bello anche scoprire il trucco dietro le magie dei modori di videogiochi
# riferimenti
massimo il bidello dell'arci ragazzi, per avermi messo la pulce nell'orecchio anni fa con il suo ecs
vittorio romero che ha creato un framework per fare ecs in c++ a tempo di compilazione, molto figo
sara bruno andrea e tsuneo che mi hanno coinvolto in quella gamejam
alla fine non usammo nè love2d nè ecs, ma facemmo un gioco in processing di due rane che saltano, e fu molto figo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment