Gerenciamento de estado do jogo (jogo, menu, tela de título etc.)

11

Basicamente, em todos os jogos que eu criei até agora, sempre tenho uma variável como "current_state", que pode ser "game", "tela de título", "tela de jogo" etc.

E então, na minha função Update, tenho uma enorme:

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

No entanto, não acho que essa seja uma maneira profissional / limpa de lidar com os estados. Alguma idéia de como fazer isso de uma maneira melhor? Ou é este o caminho padrão?

David Gomes
fonte
Qual idioma, estrutura etc. você está usando?
quer
Geralmente Lua + AMOR. Eu também acabei de descobrir que estruturas diferentes têm maneiras diferentes de lidar com isso. O SFML parece ter uma classe de tela muito boa.
David Gomes
1
Você já olhou para máquinas de estado?
Darcara 25/08/12
1
Você também pode procurar por gamestates na barra de pesquisa no canto superior direito. Deve dar alguns resultados.
TravisG
É preciso segundo Darcara - isso parece exatamente para o que as Máquinas de Estado são usadas.
precisa saber é o seguinte

Respostas:

14

Como você está falando de telas, acho melhor separar toda essa lógica em diferentes telas. O que eu normalmente faço:

Defina uma interface chamada tela e faça com que várias telas a implementem. Como LoadingScreen, MainMenuScreen, GameScreen, GameOverScreen, HighScoreScreen etc. No seu jogo, você coloca uma variável que mantém a tela atual. A cada loop, você chama screen.update () e renderiza a tela atual. Isso economizará muito "se esse estado fizer isso", pois seu estado é definido pela tela atual.

Isso irá separar sua lógica muito bem.

Código de exemplo:

### 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();
    }
}

Ou, dependendo da configuração do jogo, talvez você tenha um loop infinito como seu jogo.

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

Se você já está usando o Middleclass, existe uma excelente biblioteca de máquinas de estado, chamada Statefull . É fácil de usar e expõe as mesmas idéias que Matsemann propôs.

WuTangTan
fonte
2

Se sua current_statevariável é uma string, isso é realmente fácil em 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
fonte
1

O que faço é aproximadamente o seguinte:

Eu tenho uma estrutura de dados de gráfico acíclica direcionada , que é essencialmente apenas um monte de nós que apontam um para o outro. Cada nó representa um sistema de jogo. por exemplo, a interface do usuário, o mundo, a entrada, a renderização. E cada nó aponta para outros nós que vêm antes ou depois dele. Quando todos os nós estão no lugar, é fácil achatá-lo em uma lista simples. A configuração desse DAG é a primeira coisa que faço durante a inicialização do jogo. Sempre que eu quiser adicionar um novo sistema, digamos AI, posso apenas escrever esse código e depois dizer ao meu jogo do que depende e do que deve depender.

Meu loop principal do jogo vem depois disso e simplesmente executa cada sistema em ordem. A primeira entrada é manipulada, as atualizações mundiais e outras coisas ... A interface do usuário está próxima do fim e a renderização é a última. Quando o jogo começa, não há mundo, física ou IA; portanto, essas etapas são essencialmente ignoradas e apenas a tela do título é exibida. Quando você inicia o jogo corretamente, a interface do usuário envia uma mensagem ao sistema mundial para ligar, e ele se cuida sozinho. Gerenciar o estado do jogo significa apenas ligar e desligar os vários sistemas. Cada sistema possui seu próprio conjunto de informações de estado que são tratadas de forma mais ou menos independente de todos os outros (isso não é totalmenteverdade, na verdade, muitos sistemas agem no mesmo conjunto de dados - o sistema de interface do usuário, por exemplo, captura dados do mundo para exibir informações, por exemplo. O sistema de IA também precisa observar e enviar mensagens para entidades do mundo).

Alex Ames
fonte
Esta resposta é uma boa resposta para uma pergunta diferente .
Matsemann
Como assim? Ele perguntou como configurar seus vários estados de jogo, e minha solução não é usar uma máquina de estado como ele tem agora, mas sim dividir os bits em vários sistemas que não são uma máquina de estado, mas um DAG.
Alex Ames
1

Veja como eu organizo meus estados no Lua + Love2d. Evita as longas declarações if / then.

Primeiro, crio uma classe básica que contém os métodos update (dt) e render (). Você também pode fornecer métodos de manipulação de eventos, como onKeyDown (key). Chamo essa classe de estágio, mas qualquer objeto que implemente os métodos funcionará. Em seguida, faço uma instância dessa classe para cada estado do jogo, implementando os métodos necessários. Em seguida, crio uma tabela de chave / valor com o nome do estado e a instância do estado. Em seguida, acompanhe o currentState no escopo global para que os estados possam alterá-lo quando uma determinada condição for atendida.

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
fonte
-1

Bem, embora não seja bonito, não há problema em lidar com estados dessa maneira, IMO. Você pode torná-lo muito mais limpo usando funções para cada estado, como:

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

ou algo que o incomoda nessa abordagem (quero dizer, exceto que o método Update é muito longo)?

Petr Abdulin
fonte