Gestión del estado del juego (Juego, Menú, Pantalla de títulos, etc.)

11

Básicamente, en todos los juegos que he hecho hasta ahora, siempre tengo una variable como "current_state", que puede ser "game", "title screen", "gameoverscreen", etc.

Y luego, en mi función de actualización, tengo un enorme:

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

Sin embargo, no creo que esta sea una forma profesional / limpia de manejar estados. ¿Alguna idea sobre cómo hacer esto de una mejor manera? ¿O es esta la forma estándar?

David Gomes
fuente
¿Qué lenguaje, marco, etc. está utilizando?
Petr Abdulin
Por lo general, Lua + AMOR. También acabo de descubrir que diferentes marcos tienen diferentes formas de manejar esto. SFML parece tener una muy buena clase de pantalla.
David Gomes el
1
¿Has mirado en las máquinas de estado?
Darcara
1
También puedes buscar estados de juego en la barra de búsqueda en la esquina superior derecha. Debería dar algunos resultados.
TravisG
Debe segundo Darcara: esto parece exactamente para qué se utilizan las máquinas de estado.
balajeerc

Respuestas:

14

Como estás hablando de pantallas, creo que es mejor separar toda esa lógica en diferentes pantallas. Lo que normalmente hago:

Defina una interfaz llamada pantalla y haga que varias pantallas la implementen. Como LoadingScreen, MainMenuScreen, GameScreen, GameOverScreen, HighScoreScreen, etc. En su juego, coloca una variable que contiene la pantalla actual. Cada ciclo, llama a screen.update () y renderiza la pantalla actual. Esto le ahorrará mucho "si este estado hace eso", ya que su estado se define en la pantalla actual.

Esto separará su lógica muy bien.

Código de ejemplo:

### 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 dependiendo de la configuración de su juego, tal vez tenga un bucle infinito como juego.

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

Si ya está utilizando Middleclass, hay una excelente biblioteca de máquinas de estado para acompañarla llamada Statefull . Es fácil de usar y utiliza las mismas ideas que Matsemann propuso.

WuTangTan
fuente
2

Si su current_statevariable es una cadena, entonces esto es realmente fácil en 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]()
John Calsbeek
fuente
1

Lo que hago es más o menos lo siguiente:

Tengo una estructura de datos de gráfico acíclico dirigida , que es esencialmente solo un grupo de nodos que apuntan entre sí. Cada nodo representa un sistema de juego. Por ejemplo, la interfaz de usuario, el mundo, la entrada, la representación. Y cada nodo apunta a otros nodos que vienen antes o después. Una vez que todos los nodos están en su lugar, es fácil aplanarlo en una lista simple. Configurar este DAG es lo primero que hago durante el inicio del juego. Cada vez que quiero agregar un nuevo sistema, digamos AI, solo puedo escribir ese código y luego decirle a mi juego de qué depende y qué debería depender de él.

Mi ciclo de juego principal viene después de eso y simplemente ejecuta cada sistema en orden. Primero se maneja la entrada, luego las actualizaciones mundiales, luego otras cosas ... La interfaz de usuario está cerca del final y la representación es la última. Cuando el juego comienza por primera vez, no hay mundo ni física ni IA, por lo que esos pasos se omiten esencialmente y solo se muestra la pantalla de título. Cuando comienzas el juego correctamente, la interfaz de usuario envía un mensaje al sistema mundial para que se encienda, y solo se ocupa de sí mismo. Administrar el estado del juego solo significa encender y apagar los diversos sistemas. Cada sistema tiene su propio conjunto de información de estado que se maneja más o menos independientemente de todos los demás (eso no es totalmentecierto en realidad, muchos sistemas actúan sobre el mismo conjunto de datos: el sistema UI, por ejemplo, toma datos del mundo para mostrar información, por ejemplo. El sistema de IA también necesita mirar y enviar mensajes a entidades en el mundo).

Alex Ames
fuente
Esta respuesta es una buena respuesta a una pregunta diferente .
Matsemann
¿Cómo es eso? Preguntó cómo configurar sus diversos estados de juego, y mi solución no es usar una máquina de estados como la que tiene ahora, sino dividir los bits en varios sistemas que no son una máquina de estados sino un DAG.
Alex Ames
1

Así es como organizo mis estados en Lua + Love2d. Evita las largas declaraciones if / then.

Primero, creo una clase básica que contiene métodos de actualización (dt) y render (). También puede darle métodos de manejo de eventos, como onKeyDown (clave). Llamo a esta clase Etapa, pero cualquier objeto que implemente los métodos funcionará. Luego, hago una instancia de esa clase para cada estado del juego, implementando los métodos necesarios. Luego creo una tabla clave / valor con el nombre del estado y la instancia del estado. Luego, realice un seguimiento del estado actual en el ámbito global para que los estados puedan cambiarlo cuando se cumpla una determinada condición.

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
h4tch
fuente
-1

Bueno, aunque no es bonito, está bien manejar estados de esta manera, en mi opinión. Puede hacerlo mucho más limpio utilizando funciones para cada estado, como:

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

o algo más te molesta en este enfoque (quiero decir, excepto que el método de actualización es muy largo)?

Petr Abdulin
fuente