Finora i sistemi di componenti di entità che ho usato hanno funzionato principalmente come l'artemis di Java:
- Tutti i dati nei componenti
- Sistemi indipendenti senza stato (almeno nella misura in cui non richiedono input per l'inizializzazione) che scorre su ogni entità che contiene solo i componenti a cui un determinato sistema è interessato
- Tutti i sistemi elaborano le loro entità con un segno di spunta, quindi il tutto ricomincia.
Ora sto provando ad applicarlo per la prima volta a un gioco a turni, con tonnellate di eventi e risposte che devono avvenire in un ordine stabilito l'uno rispetto all'altro, prima che il gioco possa andare avanti. Un esempio:
Il giocatore A subisce danni da una spada. In risposta a questo, l'armatura di A entra e abbassa il danno subito. Anche la velocità di movimento di A viene ridotta a causa dell'indebolimento.
- Il danno subito è ciò che scatena l'intera interazione
- L'armatura deve essere calcolata e applicata al danno in arrivo prima che il danno sia applicato al giocatore
- La riduzione della velocità di movimento non può essere applicata a un'unità se non dopo che il danno è stato effettivamente inflitto, poiché dipende dall'ammontare del danno finale.
Gli eventi possono anche innescare altri eventi. Ridurre il danno della spada usando l'armatura può farla frantumare (ciò deve avvenire prima che la riduzione del danno sia completata), che a sua volta può causare ulteriori eventi in risposta ad essa, essenzialmente una valutazione ricorsiva degli eventi.
Tutto sommato, questo sembra portare ad alcuni problemi:
- Molti cicli di elaborazione sprecati: la maggior parte dei sistemi (ad eccezione delle cose che funzionano sempre, come il rendering) semplicemente non ha nulla di utile da fare quando non è "il loro turno" per funzionare, e passa la maggior parte del tempo in attesa che il gioco entri uno stato di lavoro valido. Questo cosparge ogni sistema di questo tipo con controlli che continuano a crescere di dimensioni più stati vengono aggiunti al gioco.
- Per scoprire se un sistema è in grado di elaborare entità presenti nel gioco, hanno bisogno di un modo per monitorare altri stati entità / sistema non correlati (il sistema responsabile di infliggere danni deve sapere se l'armatura è stata applicata o meno). Questo o confonde i sistemi con molteplici responsabilità, o crea la necessità di sistemi aggiuntivi senza altro scopo se non quello di scansionare la raccolta di entità dopo ogni ciclo di elaborazione e comunicare con una serie di ascoltatori dicendo loro quando va bene fare qualcosa.
I due punti precedenti presuppongono che i sistemi funzionino sullo stesso insieme di entità, che finiscono per cambiare stato usando flag nei loro componenti.
Un altro modo per risolverlo sarebbe aggiungere / rimuovere componenti (o creare entità completamente nuove) come risultato di un singolo lavoro di sistema per far avanzare lo stato dei giochi. Ciò significa che ogni volta che un sistema ha effettivamente un'entità corrispondente, sa che è autorizzato a elaborarlo.
Ciò rende tuttavia i sistemi responsabili dell'attivazione dei sistemi successivi, rendendo difficile ragionare sul comportamento dei programmi poiché i bug non verranno visualizzati come risultato di una singola interazione del sistema. L'aggiunta di nuovi sistemi diventa anche più difficile poiché non possono essere implementati senza sapere esattamente come influenzano altri sistemi (e potrebbe essere necessario modificare i sistemi precedenti per attivare gli stati a cui il nuovo sistema è interessato), un po 'vanificando lo scopo di avere sistemi separati con una sola attività.
È qualcosa con cui dovrò convivere? Ogni singolo esempio di ECS che ho visto è stato in tempo reale, ed è davvero facile vedere come funziona questo ciclo di ripetizione per gioco in questi casi. E ne ho ancora bisogno per il rendering, sembra davvero inadatto per i sistemi che mettono in pausa la maggior parte degli aspetti di se stesso ogni volta che succede qualcosa.
Esiste un modello di progettazione per far avanzare lo stato del gioco che è adatto a questo, o dovrei semplicemente spostare tutta la logica fuori dal ciclo e invece attivarla solo quando necessario?