Gestione dello stato di gioco (Gioco, Menu, Schermata titoli, ecc.)


11

Fondamentalmente, in ogni singolo gioco che ho realizzato finora, ho sempre una variabile come "stato_attuale", che può essere "gioco", "schermata dei titoli", "schermata dei giochi", ecc.

E poi sulla mia funzione di aggiornamento ho un enorme:

if current_state == "game" 
  game stuf
  ...
else if current_state == "titlescreen"
  ...

Tuttavia, non credo che questo sia un modo professionale / pulito di gestire gli stati. Qualche idea su come farlo in un modo migliore? O è questo il modo standard?


Quale lingua, framework, ecc. Stai usando?
Petr Abdulin,

Di solito Lua + AMORE. Ho anche appena scoperto che quadri diversi hanno modi diversi di gestirlo. SFML sembra avere una classe molto bella per lo schermo.
David Gomes,

1
Hai esaminato le macchine statali?
Darcara,

1
Puoi anche cercare gamestates nella barra di ricerca in alto a destra. Dovrebbe dare dei risultati.
TravisG,

Darcara deve essere secondo: questo sembra esattamente ciò per cui vengono utilizzate le macchine statali.
balajeerc,

Risposte:


14

Dato che stai parlando di schermi, ritengo sia meglio separare tutta quella logica in schermi diversi. Cosa faccio normalmente:

Definire un'interfaccia chiamata schermo e farla implementare da più schermi. Come LoadingScreen, MainMenuScreen, GameScreen, GameOverScreen, HighScoreScreen ecc. Nel gioco inserisci una variabile che contiene la schermata corrente. Ad ogni loop, si chiama screen.update () e si esegue il rendering della schermata corrente. Questo ti farà risparmiare molto "se questo stato lo fa" poiché il tuo stato è definito dalla schermata corrente.

Ciò separerà molto bene la tua logica.

Codice di esempio:

### Screen interface ###
public interface Screen {

    public void show();

    public void update(float delta);

    public void render(float delta);

    public void hide ();
}

### An implementation of screen ###
public class MainMenuScreen implements Screen {

    private Game game;

    public MainMenuScreen(Game game) {
        this.game = game;
    }

    public void show() {
        // init stuff
    }

    public void update(float delta) {
        // react to clicks, update animations etc.
        if (buttonwasclicked) {
            game.setScreen(new GameScreen(game)); // change the screen
        }
    }

    public void render(float delta) {
        // draw everything
    }

    public void hide() {
        // release all resources, as the screen is being hidden
    }
}

### Game, drawing the appropriate screen ###
public class Game {

    public Screen screen;

    public void update() {
        screen.update(getDeltaTime);
        screen.render();
    }

    public void setScreen(Screen screen) {
        this.screen.hide();

        this.screen = screen;
        this.screen.show();
    }
}

O a seconda della configurazione del tuo gioco, potresti avere un loop infinito come gioco.

while(true) {
    calculatetimesincelastframe()
    screen.update(time);
    screen.render(time);
}

5

Se stai già usando Middleclass, c'è un'eccellente libreria di macchine a stati per accompagnarla chiamata Statefull . È facile da usare ed espone le stesse idee proposte da Matsemann.


2

Se la tua current_statevariabile è una stringa, questo è davvero facile in Lua:

game_states = {}
function game_states.game()
    -- game stuff
end
function game_states.titlescreen()
    -- title screen stuff
end

-- then, inside the Update function:
game_states[current_state]()

1

Quello che faccio è all'incirca il seguente:

Ho una struttura dati grafica aciclica diretta , che è essenzialmente solo un mucchio di nodi che si puntano l'un l'altro. Ogni nodo rappresenta un sistema di gioco. ad es. l'interfaccia utente, il mondo, l'input, il rendering. E ogni nodo punta ad altri nodi che lo precedono o lo seguono. Una volta che tutti i nodi sono a posto, è facile appiattirlo in un semplice elenco. L'impostazione di questo DAG è la prima cosa che faccio durante l'avvio del gioco. Ogni volta che voglio aggiungere un nuovo sistema, ad esempio AI, posso solo dire scrivere quel codice e dire al mio gioco da cosa dipende e da cosa dovrebbe dipendere da esso.

Il mio ciclo di gioco principale viene dopo e semplicemente esegue ogni sistema in ordine. Il primo input viene gestito, quindi gli aggiornamenti mondiali, quindi altre cose ... L'interfaccia utente è vicina alla fine e il rendering è ultimo. Quando il gioco inizia per la prima volta, non c'è mondo, fisica o intelligenza artificiale, quindi quei passaggi vengono essenzialmente saltati e viene visualizzata solo la schermata del titolo. Quando si avvia il gioco in modo corretto, l'interfaccia utente invia un messaggio al sistema mondiale per l'accensione e si prende cura di se stessa. Gestire lo stato del gioco significa solo accendere e spegnere i vari sistemi. Ogni sistema ha il proprio set di informazioni sullo stato che sono gestite più o meno indipendentemente da tutti gli altri (non è totalmentevero in realtà, molti sistemi agiscono sullo stesso insieme di dati: ad esempio il sistema UI prende i dati dal mondo per visualizzare informazioni. Il sistema di intelligenza artificiale deve anche guardare e inviare messaggi alle entità del mondo).


Questa risposta è una buona risposta a una domanda diversa .
Matsemann,

Come mai? Ha chiesto come impostare i suoi vari stati di gioco, e la mia soluzione non è quella di utilizzare una macchina a stati come ha fatto ora, ma piuttosto di dividere i bit in vari sistemi che non sono una macchina a stati ma piuttosto un DAG.
Alex Ames,

1

Ecco come organizzo i miei stati in Lua + Love2d. Evita le dichiarazioni if ​​/ then lunghe.

Innanzitutto, creo una classe di base che contiene i metodi update (dt) e render (). Puoi anche assegnargli metodi di gestione degli eventi, come onKeyDown (chiave). Chiamo questa classe Stage, ma qualsiasi oggetto che implementa i metodi funzionerà. Quindi creo un'istanza di quella classe per ogni stato di gioco, implementando i metodi necessari. Quindi creo una tabella chiave / valore con il nome dello stato e l'istanza dello stato. Quindi tenere traccia di currentState nell'ambito globale in modo che gli stati possano modificarlo quando viene soddisfatta una determinata condizione.

states = {}
states["title"] = title   -- Where title implements Stage class.
states["game"] = game     -- You could create the instance of 'game' lazily too.
currentState = "title"

function love.update(dt)
    if states[currentState] ~= nil then
       states[currentState]:update(dt) 
    end
end

-1

Bene, anche se non è carino, va bene gestire gli stati in questo modo, IMO. Puoi renderlo molto più pulito usando le funzioni per ogni stato, come:

if current_state == "game" 
  game()
else if current_state == "titlescreen"
  titlescreen()

o qualcos'altro ti disturba in questo approccio (voglio dire tranne che il metodo di aggiornamento è molto lungo)?

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.