Alternativa al sistema State Game?


30

Per quanto ne so, la maggior parte dei giochi ha una sorta di "sistema di stato del gioco" che cambia tra i diversi stati del gioco; potrebbero trattarsi di "Intro", "MainMenu", "CharacterSelect", "Loading" e "Game".

Da un lato, ha perfettamente senso separarli in un sistema statale. Dopotutto, sono diversi e avrebbero altrimenti bisogno di essere in una grande istruzione switch, che è ovviamente disordinata; e certamente sono ben rappresentati da un sistema statale. Ma allo stesso tempo, guardo lo stato "Gioco" e mi chiedo se c'è qualcosa di sbagliato in questo approccio al sistema statale. Perché è come l'elefante nella stanza; è ENORME ed ovvio ma nessuno mette in discussione l'approccio del sistema dello stato del gioco.

Mi sembra sciocco che "Gioco" sia allo stesso livello di "Menu principale". Tuttavia non esiste un modo per spezzare lo stato "Gioco".

Un sistema di gioco è il modo migliore per andare? Esiste una tecnica diversa e migliore per gestire lo "stato del gioco"? Va bene avere uno stato di introduzione che disegna un film e ascolta per entrare, quindi uno stato di caricamento che scorre sul gestore delle risorse, e quindi lo stato del gioco che fa praticamente tutto ? Non ti sembra sbilanciato anche questo? Mi sto perdendo qualcosa?

Risposte:


30

Penso che tu stia solo discutendo di semantica qui. Si chiama Game State perché si comporta come una macchina a stati finiti, con un numero finito di stati e transizioni tra di loro. Il "Gioco" in "Sistema di stato del gioco" si riferisce al sistema generale, con "Caricamento", "MainMenu" ecc. Che sono gli stati del gioco. Questi potrebbero facilmente essere chiamati 'scene' o 'schermi' o 'livelli'. È solo semantica.

Non sono più sicuro che si applichi un rigoroso FSM. Nelle mie implementazioni chiamo gli stati 'Schermate' e consento loro di essere impilabili, ad es. le schermate possono essere disegnate sopra altre schermate, controllando se le schermate sottostanti sono aggiornate o disegnate. In questo modo, posso avere più schermi attivi contemporaneamente con una logica autonoma e un codice specifico per quello schermo, senza dovermi preoccupare di nessun altro schermo.

Una schermata di pausa, ad esempio, potrebbe essere aperta in cima alla mia schermata di gioco principale che non consente gli aggiornamenti, ma consente di disegnare sotto di sé. Una schermata di inventario dei personaggi può consentire sia il disegno che gli aggiornamenti, quindi il gioco continua a giocare mentre lavori nell'inventario.


questo è esattamente come dovrebbe essere fatto. Gli schermi dovrebbero essere in grado di contenere più altri schermi in un'architettura ad albero. Quindi il tuo programma contiene una schermata di gioco, che contiene una schermata del menu di pausa, che contiene una schermata delle impostazioni audio e una schermata delle impostazioni del gioco. ecc.
Iain,

1
Mi piacerebbe vedere il codice sorgente di esempio per questo! (Preferibilmente in C ++ / C # / Java)
Zolomon

1
Purtroppo, ho solo il codice di produzione al momento, ma c'è un concetto simile nel campione XNA Game Stato: creators.xna.com/en-GB/samples/gamestatemanagement
DrDeth

7

Sicuramente lo stato del gioco sarebbe enorme, ma non c'è motivo per cui lo stato del gioco stesso non possa contenere una macchina a stati per gestire i suoi dati. Le macchine a stati gerarchici sono utili.


4

Mi piace sempre pensare a ogni "stato" come a una "scena". Quindi il video di apertura è una scena, solo statica. I crediti sono una scena. Il menu è una scena. L'unica differenza tra loro è il livello di interattività e la logica di gioco.


3

Anch'io ho problemi con quello.

Diciamo che hai un gioco.

Invece di rendere "Gioco" uno stato come "Caricamento", "Menu principale", ecc. - IMO è meglio lasciare che Gioco abbia diversi stati:

"Caricamento" - "mostra menu" - "in pausa" , ecc.

Il gioco è ancora in esecuzione, ma quando mostra il menu principale sarebbe in modalità "mostra menu".

E quando il gioco non è in uno stato particolare, è solo in esecuzione.

Ha molto più senso, almeno per me. :)


Sono d'accordo. Nessuno vuole uscire dallo stato di gioco solo per entrare nello stato di pausa . D'altra parte: è ancora un sistema statale .. appena annidato :)
bummzack

2

Un programma online (nel significato tradizionale di online, ovvero esecuzione e risposta continue all'input, piuttosto che significato connesso a Internet) in genere consiste di 3 cose:

  • raccolta e gestione degli input
  • aggiornamento della logica
  • produzione

In generale, questi 3 sono correlati e cambiano allo stesso tempo. Ad esempio, quando si visualizza una schermata iniziale, è possibile mappare tutti i tasti su un comando 'Chiudi schermata' e l'aggiornamento potrebbe far sbiadire lentamente un elemento grafico con l'output che mostra solo tale elemento grafico. Quando si gioca, i tasti possono essere associati a comandi diversi e l'aggiornamento sta cambiando le proprietà di molti oggetti di gioco.

Quando lo vedi in questo modo, ha senso separare un'introduzione dalla creazione del personaggio e dal gioco: ognuno ha il suo set di regole di input, aggiornamento e output. Sono quasi dei programmi autonomi che condividono alcuni dati e il codice della libreria. E, con questo in mente, di solito ha senso avere solo uno stato di gioco, poiché il gameplay è abbastanza omogeneo in tutto.

Naturalmente, se in realtà hai tipi di gioco separati (ad es. Un esempio di gioco di ruolo - Mappa del mondo, Mappa della città, Cutscene, Combattimento), con input, aggiornamenti e output diversi, non c'è motivo per cui non potresti avere più stati lì anche al posto di 1 solo stato di gioco. Ma dipende dal tuo gioco.


1

Lo guardo dall'altra parte. "Menu", "Punteggi", "crediti" o whathaveyou, potrebbero essere considerati solo come un altro livello e quindi quello stato non è necessariamente più leggero del tuo stato "gioco" (lo stato del gioco ha solo più entità in generale, e diversi, ma alla fine è solo un altro livello in cui le entità mostrano comportamenti più prevedibili e le "mappe" sono generalmente meno complesse).
Fare questo cambiamento nel tuo modo di pensare ti allontana definitivamente dalla sindrome del "menu noioso".


Stavo per dire lo stesso ... Tutti i miei menu, schermate, qualunque cosa, sono solo un altro livello.
speeder

1

Nel mio gioco, ho:

Execution Manager , che inizializza l'applicazione (gioco), carica risorse, rilascia risorse all'uscita dell'applicazione, ecc. Inizializza Application Engine, GameViewEngine, GameLogicEngine.

Game State Manager , che risiede nel GameLogicEngine, ed è responsabile del controllo delle cose relative al loop principale del gioco: rilevamento delle collisioni, calcolo della fisica, lettura della tastiera, operazioni matematiche, ecc ...

Inizialmente, tendevo ad avere un solo Game State Manager che faceva parte del mio GameLogicEngine. Tuttavia, ho avuto delle difficoltà con il controllo sull'inizializzazione dei sottosistemi principali (GameLogic, ApplicationEngine, ...). Avrebbe potuto essere fatto, ma era più disordinato, imo.

Ora le cose mi sembrano più trasparenti e sono contento del design.


0

Rinomina lo stato "Gioco" in "Gameplay". Quindi la tua logica sembra migliore; Smetti di giocare per andare al menu: esci dallo stato di gioco per passare allo stato MainMenu.

Inoltre, penso che cose come la pausa, che richiederebbe che il gioco sia nello stesso stato di quando hai messo in pausa il gioco, non dovrebbero essere stati separati. Stati del bambino e nidificazione, forse? Il gameplay ha un menu di pausa.


0

Penso che esista un buon metodo chiamato stack dello stato di gioco. Non ho visto documenti o articoli a riguardo, ma si è diffuso un po 'con la voce. Essenzialmente, lo stato di gioco più in alto nello stack viene chiamato per primo e fa tutto quello che vuole con input / rendering ecc. Lo stato di gioco più in alto è l'unico a cui è consentito spingere o pop.

Nel mio motore, gli stati di gioco sono in realtà solo elenchi di entità di gioco. Ho quindi entità che funzionano come menu. Il mio stato di menu o mette in pausa il gioco (non aggiornando l'elemento successivo in pila) ma lascia che gli altri stati spingano i loro modelli sul renderer in modo che il mio menu di pausa (che non copre l'intero schermo) sia ancora ha il rendering del gioco nella parte posteriore.

Spero che dia un'idea di un sistema un po 'diverso che non si basa su una macchina a stati.


0

Va bene avere uno stato di introduzione che disegna un film e ascolta per entrare, quindi uno stato di caricamento che scorre sul gestore delle risorse, e quindi lo stato del gioco che fa praticamente tutto? Non ti sembra sbilanciato anche questo? Mi sto perdendo qualcosa?

Questo va benissimo. O almeno, è un miglioramento rispetto a "avere un grosso brutto passaggio a seconda dello stato del gioco".

Vorrei sottolineare che nella maggior parte dei giochi avrete già bisogno di una sorta di macchina a stati finiti per gestire la semplice IA di entità. L'esempio tipico sono i nemici che si trovano nello stato Inattivo, Attaccando o Morendo.

Se disponi di una macchina a stati finiti sufficientemente astratta, puoi riutilizzarla sia per l'oggetto di gioco che per l'IA; all'improvviso non stai "investendo" un grande sforzo sullo stato del Gioco, ma stai riutilizzando il codice che hai usato comunque.

Ne deriva un'auto-spina spudorata: ho implementato una tale macchina a stati finiti sulla mia libreria di giochi Lua, MiddleClass (concretamente, il componente aggiuntivo chiamato MindState). Ecco come fare una cosa di stato del gioco con esso .


0

Un approccio diverso a questo è quello di utilizzare un concetto del mondo della programmazione funzionale chiamato Unione Discriminata . Mentre questi si trovano in genere nei linguaggi FP, è possibile emularli usando le classi .

Fondamentalmente, un'Unione discriminata è un tipo che è sempre uno dei ncasi e i dati memorizzati possono variare a seconda dei casi.

Per esempio:

type GameState =
  | Menu of MenuState
  | Playing of SimulationState

Qui, il nostro GameStatetipo può essere Menuo Playing. In tal caso Menu, conterrà un MenuStateoggetto. In tal caso Playing, conterrà un SimulationStateoggetto.

Per aggiornare, dovremmo matchsullo stato e chiamare di conseguenza una funzione diversa:

let update gameTime = 
  let nextState = 
    match gameState with
    | Menu menuState -> updateMenu gameTime menuState
    | Playing simulationState -> updateSimulation gameTime simulationState

  // Mutate the current state
  gameState <- nextState

E allo stesso modo per il rendering:

let render gameTime = 
  let nextState = 
    match gameState with
    | Menu menuState -> renderMenu menuState
    | Playing simulationState -> renderSimulation simulationState

Un vantaggio di questo approccio è che puoi gestire le cose attraverso stati (come risorse) più facilmente senza globuli o passando oggetti "di servizio".

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.