Stato del gioco e gestione dell'input nei sistemi di entità basati su componenti


16

La mia domanda è:

Come posso gestire gli stati di gioco nel mio sistema di entità, senza ricorrere a mantenere una pila di oggetti di stato di gioco in giro?

Quindi la progettazione del mio sistema di entità significa che quando un'entità deve registrarsi per eventi di input, ad esempio, il componente di input chiama il sistema di input e dice "registra questa entità per questo input". Questo va bene e va bene, tuttavia se si aggiunge a questo il concetto di stati del gioco (diciamo una schermata di pausa), diventa un problema capire se un'entità è nello stato corrente e dovrebbe ricevere l'input.

Potrei aumentare il componente / sistema di input in modo che dica "registra questa entità per questo input mentre si trova in questi stati di gioco", ma ciò richiede che ogni entità sappia in quali stati verrà utilizzata e che potrebbe non essere ovvio. Inoltre, mantenere un elenco di stati di gioco in giro per input registrato (e altri sistemi che utilizzano callback) non sembra troppo efficiente.

Un'altra idea che ho avuto è che ci sarà un'entità che rappresenta lo stato del gioco, contrassegnarlo come disabilitato, quindi quando si genera l'evento di input verificare che l'entità non discenda da un'entità di stato del gioco disabilitata. Sembra costoso elaborare il genitore per ogni richiamata.

Un'altra idea è che tutti i sistemi memorizzino i loro dati in chiave sullo stato corrente, in questo modo quando si genera l'input, l'entità target non sarà nemmeno un candidato. Tuttavia, ciò danneggia davvero la capacità di consentire la comunicazione tra entità in diversi stati (non è un problema per le schermate di pausa, ma pensa alla selezione dei blocchi in Oblivion / Skyrim).

L'unica altra idea che ho avuto è quella di avere tutti i componenti in grado di gestire eventi di cambiamento di stato e comunicare con il loro sistema pertinente per disabilitare tutto ciò che hanno registrato e riattivarlo quando si torna a questo stato.

Il secondo (contrassegnare un oggetto come disabilitato) e avanti (ogni componente ha a che fare con i cambiamenti di stato) sembra la migliore delle mie idee, ma nessuna di esse mi salta fuori come particolarmente grande.

Qualcun altro ha altre idee su come farlo?

modifica Mentre parlo di input specificamente in questa domanda, può significare qualsiasi sistema in grado di inviare messaggi / eventi a entità, come collisioni, eventi timer, ecc ...


6
Lo faccio in questo modo: ho schermate, MenuScreen PauseScreen GameScreen, ogni schermata può creare il proprio mondo (contenitore per Entità) e sistemi (come RenderingSystem) e quindi in GameScreen creo World, Entity con CameraComponent e imposta CameraComponent.RenderTarget su schermi di sfondo. In questo modo posso aggiungere InventoryScreen che avrà le proprie entità e sistemi (come renderer semplificato). L'input può essere passato dallo schermo al mondo, quindi l'interfaccia utente deciderà se passerà l'input allo schermo (se focalizzato, visibile ecc.) E che passerà l'input al mondo e alle entità
Kikaimaru,


2
@ Byte56 Non proprio, solo il primo ha a che fare con i gamestates (gli altri 2 sono stati all'interno delle entità), e questo non affronta davvero lo stesso problema che sto riscontrando. Quando il gioco è in pausa, deve accadere qualcosa al sistema di input per impedirlo di inviare messaggi di movimento all'entità giocatore (ad esempio), non riesco proprio a trovare un buon modo per farlo.
elFarto

1
OK, considerali correlati allora. Buona domanda.
MichaelHouse

1
Qualcos'altro da tenere in considerazione che in passato è stato un fastidio per i miei sistemi basati su componenti: interfaccia utente multi-layer. Finestra di dialogo che appare in cima al mondo o schermi multi-livello. È arrivato così lontano in ogni gioco che ho realizzato, quindi direi di prendere in considerazione un approccio in grado di risolvere quel problema.
ADB

Risposte:


14

Ciò che viene spesso utilizzato è un intermedio Intent Systemche estrae l'input e tiene traccia del contesto e dei relativi gamestates.

Ad esempio, il sistema Intent interromperà la trasmissione di input quando la simulazione viene messa in pausa. Gestisce anche la mappatura tra eventi e intenti del controller (muovi in ​​direzione, corri, spara, ricarica ...).

In questo modo i tuoi altri componenti non dipendono da gamepad / input specifici (BUTTON_A, BUTTON_B vs BUTTON_X, BUTTON_O ...) ma tutti reagiscono agli stessi intenti (IntentRun, IntentReload ...).

Un altro vantaggio è che il sistema di intenti può essere consapevole del fatto che i controller disponibili vengono aggiunti / rimossi, in quanto può inviare gli intenti a qualsiasi abbonato anche al di fuori della simulazione che è possibile gestire AddPlayer(controllerID).

Quante informazioni sullo stato del gioco fornite al sistema tramite eventi / messaggi o dipendono direttamente da voi. Ma il tempo investito nel sistema Intent di solito vale la pena.

È possibile gestire i contesti di intenti che genereranno intenti quando sono collegati al sistema.

Il contesto può essere prioritario, ovvero:

  • SimulationAvailableContext invia intenti alla simulazione mentre è disponibile (ma non in esecuzione), ad esempio spostare la telecamera, ingrandire con lo zoom, aggiungere / rimuovere il lettore ...
  • SimulationRunningContext invia intenti alla simulazione mentre non è in pausa il giocatore in movimento, invia l'unità in posizione, spara ...

In questo modo è possibile aggiungere e rimuovere i contesti attualmente rilevanti.

E una cosa sull'intero sistema di intenti è che dovrebbe funzionare mentre la simulazione è in pausa.

Un modo che viene spesso utilizzato per giocare / mettere in pausa la simulazione del gioco senza interrompere gli aggiornamenti non correlati alla simulazione è quello di utilizzare un diverso set di tempi. vale a direGenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime) .

Con questo approccio il tuo motore può semplicemente bloccare gli incrementi del simTime dei giochi che a sua volta bloccherà gli aggiornamenti sui relativi motori di animazione e fisica che utilizzano simTime and simDeltaTimeconsentendo al contempo aggiornamenti continui dell'effetto molla della videocamera se deve muoversi anche durante la pausa, l'animazione di l'effetto di caricamento su un cartellone di gioco virtuale durante il download dei dati ...


Mi piace il fatto che questo non debba chiamare un gruppo di funzioni "State Changed" su tutte le entità. Devi preoccuparti che le intenzioni sbagliate vengano inviate nel momento sbagliato, ma penso che sia meglio dell'alternativa.
Thomas Marnell,

le tue entità possono ignorare intenti come Jump mentre il loro stato non consente loro di saltare (cioè non toccare il suolo). ma non devono preoccuparsi di ricevere tali intenti mentre il gioco è in pausa.
Coyote,

Avevo già pensato di lasciare che l'entità dicesse al sistema di input in quali stati recapitare i messaggi, ma non avevo pensato di inserire gli stati sull'input stesso, il che è una buona idea. Anche dividere il tempo e simTime a parte è bello.
elFarto

Dovresti evitare di gonfiare il tuo stato relativo alla simulazione con cose non correlate alla simulazione. Sposta tutta l'interfaccia utente e il codice relativo al giocatore il più lontano possibile dalla simulazione stessa e nella simulazione concentrati solo sugli intenti.
Coyote,

Ehi @Coyote, questo sistema sembra molto interessante. Potresti forse fornire qualche informazione in più rispondendo a questa domanda ? Grazie!
pek

2

Che ne dici di creare un sistema di eventi globale e quindi avere un componente listener di eventi per ogni tua entità? Dopo un evento "Cambio dello stato del gioco" potresti armeggiare con i componenti individualmente per ciascuna entità particolare.

Supponiamo che tu abbia un componente di input. Dopo che il componente listener di eventi ha ricevuto l'evento di modifica dello stato del gioco, cambia valori molto specifici per quel particolare componente di input, in modo da non ricevere chiamate di input o effettuare chiamate di movimento o di risposta al sistema o al suo proprietario.

Questo funziona per me dato che la maggior parte dei miei componenti sono sottoposti a script (tramite Lua). Vale a dire che ho un componente di input, che viene attivato una volta quando viene premuto un tasto e si attiva di un movimento + direzione e quindi viene attivato quando il tasto viene rilasciato e si attiva di un arresto + direzione. C'è anche un componente listener di eventi che contatta il componente di input (se il gioco è in pausa) per interrompere l'esecuzione di qualsiasi funzione e arrestarsi se necessario. Potrei quindi facilmente aggiungere un'altra entità con una diversa reazione agli stessi eventi e ai tasti premuti usando un altro script. In questo modo risparmieresti l'interazione tra entità diverse in stati diversi e renderai anche molto più personalizzabile. Inoltre, alcune entità potrebbero non contenere nemmeno il componente listener di eventi.

Quello che ho appena spiegato è sostanzialmente un esempio pratico della tua quarta soluzione.

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.