Como conectar máquinas de estados finitos na arquitetura baseada em componentes? [fechadas]

23

Máquinas de estado parecem causar dependências prejudiciais em arquiteturas baseadas em componentes.

Como, especificamente, é tratada a comunicação entre uma máquina de estado e os componentes que executam o comportamento relacionado ao estado?

Onde estou:

  • Eu sou novo em arquiteturas baseadas em componentes.
  • Estou fazendo um jogo de luta, embora não ache que isso importe. Eu imagino que minha máquina de estado seja usada para alternar estados como "agachado", "arrojado", "bloqueado" etc.
  • Eu descobri que essa técnica de gerenciamento de estado é o sistema mais natural para uma arquitetura baseada em componentes, mas está em conflito com as técnicas que eu li sobre: Sistema de componentes de objetos de jogos dinâmicos para caracteres de comportamento mutável Sugere que todos os componentes sejam ativados / desativados verificando continuamente uma condição para ativação.
  • Penso que ações como "correr" ou "caminhar" fazem sentido como estados, o que está em desacordo com a resposta aceita aqui: /gamedev//a/7934
  • Achei isso útil, mas ambíguo: como implementar o comportamento em uma arquitetura de jogos baseada em componentes? Sugere ter um componente separado que não contenha nada além de uma máquina de estado. Porém, isso requer algum tipo de acoplamento entre o componente da máquina de estado e quase todos os outros componentes. Não entendo como esse acoplamento deve ser tratado. Estas são algumas suposições:

    A. Os componentes dependem da máquina de estado: Os
    componentes recebem referência aos componentes da máquina de estado getState(), que retornam uma constante de enumeração. Os componentes se atualizam regularmente e verifique isso conforme necessário.

    B. A máquina de estado depende dos componentes:
    O componente da máquina de estado recebe referências a todos os componentes que está monitorando. Ele consulta seus getState()métodos para ver onde eles estão.

    C. Alguma abstração entre eles
    Use um hub de eventos? Padrão de comando?

    D. Objetos de estado separados que fazem referência aos componentes
    State Pattern são usados. Objetos de estado separados são criados, que ativam / desativam um conjunto de componentes. Máquina de estado alterna entre objetos de estado.

  • Estou vendo os componentes como implementações de aspectos . Eles fazem tudo o que é necessário internamente para que esse aspecto aconteça. Parece que os componentes devem funcionar por conta própria, sem depender de outros componentes. Eu sei que algumas dependências são necessárias, mas as máquinas de estado parecem querer controlar todos os meus componentes.

Filhote
fonte

Respostas:

7

A visão geral é bastante clara, mas confira estes slides de uma apresentação que fiz para o New Game Conf no ano passado:

https://docs.google.com/presentation/d/110MxOqut_y7KOW1pNwIdcccisIA3ooJwVR-xm-ecuc4/view

(veja as imagens pertinentes abaixo)

A essência da técnica é combinar o padrão da lista de ações (explicado - um pouco mal - nos slides) com máquinas de estados comportamentais que atuam sobre uma entidade do jogo baseada em componentes.

É basicamente o mesmo que criar um sistema de composição especial apenas para o comportamento da IA, voltado para os tipos de integração intercomportamental necessários para sistemas mais simples da IA.

Minha parte favorita desse jogo em particular era como poderíamos criar tipos totalmente novos de inimigos, basta selecionar em uma lista de comportamentos pré-escritos, colocá-los na lista de ações do objeto do jogo (residindo em um BrainComponent) na ordem desejada prioridade e tudo funcionou. Com uma lista de ações que permite ações de bloqueio / não bloqueio, isso pode fazer algumas coisas bem legais, apesar da simplicidade de sua implementação.

Até comportamentos como "atordoar", onde realmente apenas um StunBehaviorAction é empurrado para o topo da pilha da lista de ações; se o comportamento de atordoamento for ativado (depois de observar que o EarsComponent do objeto do jogo ouviu um ataque de ondas de choque impressionante), ele define seu estado interno como Atordoado, pede ao AnimationComponent para reproduzir a animação de atordoamento e define seu estado de ação para Bloqueio e o temporizador para um tempo limite de atordoamento extraído do EnemyParametersComponent do objeto do jogo. Como estava bloqueando e no topo da lista de ações, nenhum dos outros BehaviorActions na lista de ações receberia o método de atualização chamado, portanto eles seriam essencialmente desativados. Quando o tempo limite expirou, o StunBehaviorAction definiu seu estado novamente como Idle e seu estado de ação como NonBlocking.

Os outros comportamentos que implementamos foram quase todos implementados com uma única máquina de estado interna. As únicas duas que não possuíam máquinas de estado foram, de fato, o PatrolPathBehaviorAction (ele colocaria uma série de PathAction na lista de ações se estivesse ocioso, o que por sua vez empurrou o MoveAction) e o GuardHomeBehaviorAction (sempre na parte inferior do lista de ação e sempre enviaria um PathAction de volta ao local inicial do inimigo). Todo outro comportamento era uma máquina de estado.

Slide 10 Slide 25 Slide 26

Sean Middleditch
fonte
Qual é a diferença fundamental entre "Comportamentos" e "Ações"?
Pup
1
@ Filhote: De uma perspectiva de código, como eu o construí, um comportamento é uma ação. Do ponto de vista conceitual, as ações geralmente são transitórias - elas existem apenas até "concluir" - enquanto os comportamentos são eternos e nunca são removidos da lista. Vi outra equipe criar um sistema semelhante, mas com duas listas, uma para ações e outra para comportamentos, que funciona bem o suficiente. Eu gosto de ter a capacidade de uma ação bloquear certos comportamentos, porém, usando as máscaras de bits e o agrupamento (faixas, acredito que as chamei nos slides). Desculpe, o gráfico do slide do meio é tão ruim. :)
Sean Middleditch
3

Em uma empresa anterior em que trabalhei, tínhamos um sistema baseado em componentes com IA baseada em estado. Tínhamos um componente de IA que controlava todo o comportamento desse objeto / unidade. Quando a IA estava ativa, como perambular, atacar etc., ela receberia uma atualização de cada quadro para fazer a lógica necessária. Quando o AI estava ocioso ou não se movia, o componente foi desativado e não foi atualizado a cada quadro. O componente, enquanto desativado, ainda podia receber mensagens baseadas em eventos, para receber algo como um jogador entrando em seu raio de agressão e responder a isso reativando o componente AI para que ele pudesse fazer atualizações baseadas em quadros.

O componente AI possui subcomponentes, que podem ser criados e destruídos em tempo real, com base no tipo de ação que ele está executando. Por exemplo, se estivesse vagando, ele poderia criar um subcomponente errático e atualizar cada quadro enquanto vagava; se agredido enquanto vagava, fecharia esse subcomponente e abriria um subcomponente de ataque. O componente AI deve ser independente de todos os outros componentes em um objeto. Por exemplo, tínhamos um componente de entrada que simplesmente consultava valores de movimento em uma unidade. A consulta feita foi algo que os objetos humanos e de IA responderiam. Isso permitiu que o componente AI simplesmente definisse valores de movimento em si mesmo durante coisas como vagar, que o componente de entrada poderia captar, assim como um componente controlável humano definiria valores que o componente de entrada captava.

Nic Foster
fonte
Então, os subcomponentes de IA estão realmente fazendo o trabalho? Eles existiam como componentes de entidade, no mesmo nível que o componente de IA?
Pup
Os subcomponentes em nosso mecanismo faziam parte da classe de componentes base. Portanto Component, qualquer um que derivou BaseComponentpoderia ter qualquer número de SubComponents nele. O Update()método em BaseComponentverificaria a lista de subcomponentes e os chamaria Update(). Subcomponentseram totalmente opcionais, portanto, o BaseComponentitem pode não ter nenhum. Além disso, todas as mensagens que foram para um componente também foram roteadas para os subcomponentes.
Nic Foster
1

Não é claro o que você quer dizer com componentes, pois seus termos são muito vagos, sem exemplos concretos. Muitas vezes, as entidades do jogo são construídas usando a composição, e não a herança. Assim, você pode transformá-los em algo que pode sofrer danos adicionando um componente de integridade à entidade ou você pode animar adicionando um componente animado. Pode-se também colocar a IA nesse componente. Haverá lógica de tomada de decisão em seu componente de IA e, se você estiver preocupado em associá-lo a grande parte do outro código do sistema, poderá coletar as informações em um quadro-negro ao qual a lógica de IA é permitida apenas. Há também a questão das dependências na saída do sistema de IA. Basicamente, sua IA está controlando uma entidade e esse controle precisa de uma interface. Um conceito útil é o de um controlador ou gamepad. Sua IA pode preencher uma estrutura semelhante à que o gamepad de um jogador preencheria (embora possa haver alguns "botões" extras para habilidades específicas). Agora, essa estrutura poderia ser passada para o seu componente animado, que a interpretaria e selecionaria as animações apropriadas para reproduzir. Diferentes subcomponentes de IA podem até estar gravando campos diferentes da estrutura ou nos mesmos campos com prioridades diferentes. Mirar e caminhar, por exemplo. Diferentes subcomponentes de IA podem até estar gravando campos diferentes da estrutura ou nos mesmos campos com prioridades diferentes. Mirar e caminhar, por exemplo. Diferentes subcomponentes de IA podem até estar gravando campos diferentes da estrutura ou nos mesmos campos com prioridades diferentes. Mirar e caminhar, por exemplo.

Jesse Cluff
fonte