Estado do jogo e manipulação de entrada em sistemas de entidade baseados em componentes

16

Minha pergunta é:

Como posso lidar com estados de jogos no meu sistema de entidades, sem recorrer a manter uma pilha de objetos de estado de jogos?

Portanto, o design do meu sistema de entidades significa que, quando uma entidade precisa se registrar para eventos de entrada, por exemplo, o componente de entrada chama o sistema de entrada e diz "registrar esta entidade para esta entrada". Está tudo bem, mas se você adicionar a isso o conceito de estados do jogo (digamos, uma tela de pausa), torna-se um problema descobrir se uma entidade está no estado atual e deve receber a entrada.

Eu poderia aumentar o componente / sistema de entrada para que ele diga "registre essa entidade para esta entrada enquanto estiver nesses estados de jogo", mas isso exige que toda entidade saiba em quais estados será usada e isso pode não ser óbvio. Além disso, manter uma lista de estados de jogos por entrada registrada (e outros sistemas que usam retornos de chamada) não parece muito eficiente.

Outra idéia que tive foi que haverá uma entidade que representa o estado do jogo, marque-a como desativada e, ao gerar o evento de entrada, verifique se a entidade não é descendente de uma entidade do estado do jogo desativada. Parece caro determinar o pai para cada retorno de chamada.

Outra idéia é fazer com que todos os sistemas armazenem seus dados digitados no estado atual; dessa forma, ao gerar a entrada, a entidade de destino nem será candidata. No entanto, isso realmente prejudica a capacidade de permitir a comunicação entre entidades em diferentes estados (não é um problema para as telas de pausa, mas acho que a trava no Oblivion / Skyrim).

A única outra idéia que tive é que todos os componentes lidem com eventos de mudança de estado e se comuniquem com seu sistema relevante para desativar qualquer coisa que eles tenham registrado e reativá-lo ao voltar para esse estado.

A segunda (marcar um objeto como desativado) e a seguir (cada componente lida com mudanças de estado) parecem ser as melhores de minhas idéias, mas nenhuma delas me impressiona por ser particularmente boa.

Alguém mais tem alguma outra idéia de como fazer isso?

editar Enquanto falo sobre entradas especificamente nesta pergunta, pode significar qualquer sistema capaz de enviar mensagens / eventos para entidades, como colisões, eventos de timer, etc.

elFarto
fonte
6
Faço assim: Tenho Telas, MenuScreen PauseScreen GameScreen, cada tela pode criar seu próprio mundo (contêiner para entidades) e sistemas (como RenderingSystem) e, em GameScreen, crio World, Entity with CameraComponent e defino CameraComponent.RenderTarget como tela de fundo. Dessa forma, posso adicionar o InventoryScreen que terá entidades e sistemas próprios (como renderizador simplificado). A entrada pode ser passado de tela para o mundo, para que o seu userinterface vai decidir se ele vai passar a entrada para a tela (se a sua focado, visível etc) e que vai passar de entrada para entidades do mundo e
Kikaimaru
2
@ Byte56 Não, na verdade, apenas o primeiro tem a ver com gamestates (os outros 2 são estados dentro de entidades), e isso realmente não resolve o mesmo problema que estou tendo. Quando o jogo está no estado de pausa, algo tem que acontecer com o sistema de entrada para impedir que ele envie mensagens de movimento para a entidade do jogador (por exemplo), simplesmente não consigo descobrir uma boa maneira de fazer isso.
elFarto
1
OK, considere-os relacionados então. Boa pergunta.
MichaelHouse
1
Outra coisa a ser levada em consideração que havia sido um aborrecimento para meus sistemas baseados em componentes no passado: interface do usuário de várias camadas. Caixa de diálogo exibida no topo do mundo ou em telas de vários níveis. Chegou tão longe em todos os jogos que eu fiz, então eu diria para ter certeza de considerar uma abordagem que pode resolver esse problema.
BAD

Respostas:

14

O que é frequentemente usado é um intermediário Intent Systemque abstrai a entrada e mantém o controle do contexto e dos estados relevantes dos jogos.

O sistema Intent irá parar de transmitir entradas quando a simulação for pausada, por exemplo. Ele também lida com o mapeamento entre eventos e intenções do controlador (mover na direção, correr, disparar, recarregar ...).

Dessa forma, seus outros adversários não dependem de controles / entradas de jogos específicos (BUTTON_A, BUTTON_B vs BUTTON_X, BUTTON_O ...), mas todos reagem com as mesmas intenções (IntentRun, IntentReload ...).

Outra vantagem é que o sistema de intenções pode estar ciente de que os controladores disponíveis estão sendo adicionados / removidos, pois pode enviar intenções para qualquer assinante, mesmo fora da simulação com a qual você pode manipular intenções AddPlayer(controllerID).

Você decide quantas informações sobre o estado do jogo você fornece ao sistema por meio de eventos / mensagem ou diretamente. Mas o tempo investido no sistema Intent geralmente vale a pena.

Você pode gerenciar os Contextos de Intenção, que gerarão intenções quando eles estiverem anexados ao sistema.

O contexto pode ser priorizado, ou seja:

  • SimulationAvailableContext envia intenções para a simulação enquanto ela está disponível (mas não está em execução), por exemplo, mova a câmera, diminua o zoom, reduza o zoom, adicione / remova o player ...
  • SimulationRunningContext envia intenções para a simulação enquanto não está em pausa mover jogador, enviar unidade para a posição, atirar ...

Dessa forma, você pode adicionar e remover os contextos relevantes atualmente.

E uma coisa sobre todo o sistema de intenções é que ele deve ser executado enquanto a simulação está em pausa.

Uma maneira geralmente usada para reproduzir / pausar a simulação do jogo sem interromper as atualizações não relacionadas à simulação é usar diferentes conjuntos de tempos. ie GenericSystem::onTime(Long time, Long deltaTime, Long simTime, Long simDeltaTime).

Com essa abordagem, seu mecanismo pode simplesmente bloquear os incrementos no simTime dos jogos, o que, por sua vez, bloqueia as atualizações nos mecanismos relevantes de animação e física que são utilizados simTime and simDeltaTime, permitindo atualizações contínuas do efeito de mola da câmera, se ele tiver que se mover mesmo durante a pausa, a animação de o efeito de carregamento em um outdoor virtual do jogo enquanto os dados estão sendo baixados ...

Coiote
fonte
Eu gosto do fato de que isso não precisa chamar um monte de funções "Estado Alterado" em todas as entidades. Você precisa se preocupar com as intenções erradas de serem enviadas na hora errada, mas acho que isso é melhor do que a alternativa.
Thomas Marnell
suas entidades podem ignorar intenções como Jump, enquanto o estado delas não lhes permite pular (ou seja, não tocar o chão). mas eles não precisam se preocupar em receber essas intenções enquanto o jogo está em pausa.
Coyote
Eu já tinha pensado em deixar a entidade dizer ao sistema de entrada em que estados entregar mensagens, mas não pensei em colocar os estados na entrada, o que é uma boa idéia. Também é bom dividir o tempo e o simTime.
elFarto
Você deve evitar inchar seu estado relacionado à simulação com coisas não relacionadas à simulação. Mova toda a interface do usuário e o código relacionado ao jogador o mais longe possível da simulação e concentre-se apenas nas intenções.
Coyote
Hey @Coyote, este sistema parece muito interessante. Você poderia fornecer mais algumas informações respondendo a esta pergunta ? Obrigado!
Pek
2

Que tal criar um sistema de eventos global e, em seguida, ter um componente de ouvinte de eventos para cada entidade? Após um evento "Game State Change", você poderia mexer nos componentes individualmente para cada entidade específica.

Digamos que você tenha um componente de entrada. Depois que o componente ouvinte de evento recebe o evento de alteração do estado do jogo, ele altera valores muito específicos para esse componente de entrada específico, para que não receba chamadas de entrada ou faça chamadas de movimento ou resposta para o sistema ou seu proprietário.

Isso funciona para mim, pois a maioria dos meus componentes está em script (via Lua). Ou seja, eu tenho um componente de entrada, que é acionado uma vez quando uma tecla é pressionada e dispara de uma direção de movimento + e, em seguida, é acionado quando a tecla é liberada e de uma direção de parada +. Há também um componente de ouvinte de evento que entra em contato com o componente de entrada (se o jogo estiver em pausa) para parar de executar qualquer função e interromper, se necessário. Eu poderia facilmente adicionar outra entidade com uma reação diferente aos mesmos eventos e pressionar teclas usando outro script. Dessa forma, você salvaria a interação entre diferentes entidades em diferentes estados e até a tornaria muito mais personalizável. Além disso, algumas entidades podem nem ter o componente ouvinte de eventos.

O que acabei de explicar é basicamente um exemplo prático da sua quarta solução.

karmalis
fonte