Alternativa para Game State System?


30

Até onde eu sei, a maioria dos jogos tem algum tipo de "sistema de estado do jogo" que alterna entre os diferentes estados do jogo; podem ser coisas como "Introdução", "MainMenu", "CharacterSelect", "Carregando" e "Jogo".

Por um lado, faz totalmente sentido separá-las em um sistema de estados. Afinal, eles são díspares e, caso contrário, precisariam estar em uma declaração grande de switch, o que é obviamente confuso; e eles certamente estão bem representados por um sistema estatal. Mas, ao mesmo tempo, olho para o estado "Jogo" e me pergunto se há algo errado nessa abordagem do sistema de estados. Porque é como o elefante na sala; É enorme e óbvio, mas ninguém questiona a abordagem do sistema de estado do jogo.

Parece-me bobagem que "Jogo" seja colocado no mesmo nível que "Menu Principal". No entanto, não há como romper o estado "Jogo".

Um sistema de estado de jogo é o melhor caminho a percorrer? Existe alguma técnica diferente e melhor para gerenciar, bem, o "estado do jogo"? Tudo bem ter um estado de introdução que desenhe um filme e escute enter, depois um estado de carregamento que faz um loop no gerenciador de recursos e, em seguida, o estado do jogo que faz praticamente tudo ? Isso também não parece desequilibrado para você? Estou esquecendo de algo?

Respostas:


30

Eu acho que você está apenas discutindo semântica aqui. Chama-se Game State porque se comporta como uma Máquina de Estados Finitos , com um número finito de estados e transições entre eles. O 'Jogo' no 'Sistema de Estado do Jogo' refere-se ao sistema geral, com 'Carregamento', 'MainMenu' etc. sendo os estados do jogo. Estes poderiam ser facilmente chamados de 'cenas' ou 'telas' ou 'níveis'. É apenas semântica.

Não tenho certeza de que um FSM estrito se aplique mais. Nas minhas implementações, chamo estados de 'Telas' e permito que sejam empilháveis ​​- ie. as telas podem ser desenhadas sobre outras telas, controlando se as telas abaixo ou não são atualizadas ou desenhadas. Dessa maneira, posso ter várias telas ativas ao mesmo tempo com lógica e código independentes específicos dessa tela, sem precisar me preocupar com nenhuma outra tela.

Uma tela de Pausa, por exemplo, pode ser aberta em cima da minha tela principal de jogo que não permite atualizações, mas permite desenhar abaixo de si mesma. Uma tela de inventário de personagens pode permitir desenho e atualizações - para que o jogo continue sendo jogado enquanto você trabalha no seu inventário.


é exatamente assim que deve ser feito. As telas devem poder conter várias outras telas em uma arquitetura semelhante a uma árvore. Portanto, seu programa contém uma tela de jogo, que contém uma tela de menu de pausa, que contém uma tela de configurações de áudio e uma tela de configurações de jogos. etc
Iain

1
Eu adoraria ver exemplo de código fonte para isso! (Preferencialmente em C ++ / C # / Java)
Zolomon

1
Infelizmente, eu só tenho o código de produção no momento, mas há um conceito semelhante na amostra XNA Game Estado: creators.xna.com/en-GB/samples/gamestatemanagement
DrDeth

7

Claro que o estado do jogo seria enorme, mas não há razão para que o próprio estado do jogo não possa conter uma máquina de estado para gerenciar seus dados. Máquinas de estado hierárquico são úteis.


4

Eu sempre gosto de pensar em cada "estado" como uma "cena". Portanto, o vídeo de abertura é uma cena, apenas estática. Os créditos são uma cena. O menu é uma cena. A única diferença entre todos eles é o nível de interatividade e lógica do jogo.


3

Também tenho problemas com isso.

Digamos que você tenha um jogo.

Em vez de tornar 'Game' um estado como 'Loading', 'Main Menu' etc. - Na IMO, é melhor deixar o Game ter vários estados:

"Carregando" - "exibindo menu" - "pausado" etc.

O jogo ainda está rodando, mas quando mostra o menu principal, ele fica no modo 'show menu'.

E quando o jogo não está em nenhum estado específico, está apenas rodando.

Faz muito mais sentido, pelo menos para mim. :)


Concordo. Ninguém quer sair do estado de jogo apenas para entrar no estado de pausa . Por outro lado: É ainda um sistema estatal .. apenas aninhada :)
bummzack

2

Um programa online (no significado tradicional de online, ou seja, executando e respondendo continuamente à entrada, em vez do significado conectado à Internet) geralmente consiste em três coisas:

  • coleta e manuseio de entradas
  • atualização da lógica
  • saída

De um modo geral, esses três estão relacionados e mudam ao mesmo tempo. Por exemplo, ao exibir uma tela inicial, você pode mapear todas as suas chaves para um comando 'fechar tela' e a atualização pode desvanecer um gráfico lentamente, com a saída apenas mostrando esse gráfico. Mas, ao jogar, as teclas podem ser mapeadas para diferentes comandos e a atualização está alterando as propriedades de muitos objetos do jogo.

Quando você o vê dessa maneira, faz sentido separar uma Introdução da Criação de personagem e do Jogo propriamente dito: cada um tem seu próprio conjunto de regras de entrada, atualização e saída. Eles são quase como programas independentes que compartilham alguns dados e código de biblioteca. E, com isso em mente, geralmente faz sentido ter apenas um estado de Jogo, já que a jogabilidade é bastante homogênea.

Claro, se você realmente tem tipos de jogo separados (por exemplo, um exemplo de RPG - Mapa do Mundo, Mapa da Cidade, Cortes Cênicos, Combate), com entradas, atualizações e saídas diferentes, não há razão para que você não possa ter vários estados lá também em vez de apenas um estado de jogo. Mas isso depende do seu jogo.


1

Eu olho para o outro lado. 'Menu', 'HighScores', "credits" ou o que você quiser, pode ser considerado apenas mais um nível e, em seguida, esse estado não é necessariamente mais leve que o seu estado de 'jogo' (o estado do jogo passa a ter mais entidades em geral, e diferentes, mas no final é apenas outro nível em que as entidades mostram um comportamento mais previsível e os 'mapas' geralmente são menos complexos).
Fazer essa mudança no seu pensamento definitivamente o tira da síndrome do "menu chato".


Eu ia dizer o mesmo ... Todos os meus menus, telas, qualquer que seja, são apenas outro nível.
speeder

1

No meu jogo, eu tenho:

O Execution Manager , que inicializa o aplicativo (jogo), carrega recursos, libera recursos na saída do aplicativo, etc. Ele inicializa o Application Engine, GameViewEngine, GameLogicEngine.

Game State Manager , que reside no GameLogicEngine, e é responsável por controlar os itens relacionados ao loop principal do jogo: detecção de colisão, cálculo físico, leitura de teclado, operações matemáticas, etc ...

Inicialmente, eu tendia a ter apenas um Game State Manager que fazia parte do meu GameLogicEngine. No entanto, tive algumas dificuldades em controlar a inicialização de subsistemas principais (GameLogic, ApplicationEngine, ...). Poderia ter sido feito, mas era mais bagunçado.

Agora as coisas parecem mais transparentes para mim e estou feliz com o design.


0

Renomeie o estado 'Game' para algo como 'Gameplay'. Então sua lógica parece melhor; Você para de jogar para ir para o menu: você sai do estado Gameplay para ir para o estado MainMenu.

Além disso, acho que coisas como pausa, que exigiriam que o jogo estivesse no mesmo estado de quando você pausou o jogo, não deveriam ser estados separados. Estados filho e nidificação, talvez? O jogo tem um menu de pausa.


0

Eu acho que existe um bom método chamado pilha de estado do jogo. Eu não vi nenhum artigo ou artigo sobre isso, mas está se espalhando um pouco por voz. Essencialmente, o estado de jogo mais alto da pilha é chamado primeiro e consegue fazer o que quiser com entrada / renderização etc. O estado de jogo mais alto é o único permitido para enviar estados pop ou push.

No meu mecanismo, os estados do jogo são na verdade apenas listas de entidades do jogo. Então, tenho entidades que funcionam como menus. Meus estados de menu pausam o jogo (não atualizando o próximo item na pilha), mas permitem que os outros estados empurrem seus modelos para o renderizador, para que meu menu de pausa (que não cobre a tela inteira) ainda tem a renderização do jogo nas costas.

Espero que isso dê uma idéia de um sistema um pouco diferente que não seja baseado em uma máquina de estado.


0

Tudo bem ter um estado de introdução que desenhe um filme e escute enter, depois um estado de carregamento que faz um loop no gerenciador de recursos e, em seguida, o estado do jogo que faz praticamente tudo? Isso também não parece desequilibrado para você? Estou esquecendo de algo?

Isso está perfeitamente bem. Ou, pelo menos, é uma melhoria em relação a "ter uma grande e feia opção, dependendo do estado do jogo".

Eu gostaria de salientar que na maioria dos jogos você já precisará de uma espécie de máquina de estados finitos para lidar com uma entidade AI simples. O exemplo típico são os inimigos que estão no estado ocioso, atacando ou morrendo.

Se você tiver uma Máquina de Estado Finito suficientemente abstrata, poderá reutilizá-la para o objeto Jogo e sua IA; de repente você não está "investindo" muito esforço no estado do jogo - em vez disso, está reutilizando o código que usou de qualquer maneira.

O auto-plug descarado resulta: eu implementei uma máquina de estado finito na minha biblioteca de jogos Lua, MiddleClass (concretamente, o complemento chamado MindState). Aqui está como você faz uma coisa com o Game State .


0

Uma abordagem diferente para isso é usar um conceito do mundo da programação funcional chamado União Discriminada . Embora eles normalmente sejam encontrados em idiomas FP, você pode emulá-los usando classes .

Basicamente, uma União Discriminada é um tipo que é sempre um dos ncasos, e os dados armazenados podem variar de acordo com cada caso.

Por exemplo:

type GameState =
  | Menu of MenuState
  | Playing of SimulationState

Aqui, nosso GameStatetipo pode ser Menuou Playing. Se for Menu, ele conterá um MenuStateobjeto. Se for Playing, ele conterá um SimulationStateobjeto.

Para atualizar, entraríamos matchno estado e chamaríamos uma função diferente de acordo:

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 da mesma forma para renderização:

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

Uma vantagem dessa abordagem é que você pode lidar com coisas entre estados (como recursos) com mais facilidade, sem globais ou passando objetos de "serviço".

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.