Como implementar o comportamento em uma arquitetura de jogos baseada em componentes?

21

Estou começando a implementar a IA do jogador e do inimigo em um jogo, mas estou confuso sobre como melhor implementá-lo em uma arquitetura de jogo baseada em componentes.

Digamos que eu tenha um personagem seguinte que possa ser parado, correndo e agitando uma espada. Um jogador pode transitar para o estado da espada oscilante a partir do estado estacionário e em execução, mas o balanço deve ser concluído antes que o jogador possa retomar de pé ou correr. Durante o balanço, o jogador não pode andar por aí.

A meu ver, tenho duas abordagens de implementação:

  • Crie um único componente AI que contenha toda a lógica do player (desacoplada do componente real ou incorporada como um PlayerAIComponent). Eu posso facilmente aplicar as restrições de estado sem criar acoplamentos entre componentes individuais que compõem a entidade do jogador. No entanto, o componente AI não pode ser dividido. Se eu tenho, por exemplo, um inimigo que só pode ficar de pé e andar por aí ou apenas andar por aí e ocasionalmente balançar uma espada, tenho que criar novos componentes de IA.
  • Divida o comportamento em componentes, cada um identificando um estado específico. Eu recebo um StandComponent, WalkComponent e SwingComponent. Para impor as regras de transição, tenho que acoplar cada componente. O SwingComponent deve desativar o StandComponent e o WalkComponent pela duração do swing. Quando tenho um inimigo que apenas fica por perto, balançando uma espada ocasionalmente, tenho que garantir que o SwingComponent desative o WalkComponent apenas se ele estiver presente. Embora isso permita melhores componentes de combinação e combinação, pode levar a um pesadelo de manutenção, à medida que uma dependência é adicionada, os componentes existentes devem ser atualizados para se adequar bem aos novos requisitos que a dependência impõe ao personagem.

A situação ideal seria que um designer possa construir novos inimigos / jogadores arrastando componentes para um contêiner, sem precisar tocar em uma única linha de mecanismo ou código de script. Embora não tenha certeza de que a codificação de script possa ser evitada, quero mantê-la o mais simples possível.

Resumindo: devo colocar toda a lógica da IA ​​em um componente ou dividir cada estado lógico em componentes separados para criar variantes de entidade mais facilmente?

editar : Eu suspeito que há alguma confusão sobre o que eu quis dizer com a primeira e a segunda situação. Eu tentei explicar isso no diagrama abaixo.

Diagrama de componentes

Observe o relacionamento entre os estados individuais e a entidade. Na primeira situação, um componente de IA é pré-construído antes de ser colocado na entidade. Um designer pode selecionar apenas um conjunto distinto de componentes AIC disponibilizados pelo programador. A segunda situação tem os diferentes estados no mesmo nível que outros componentes. Um designer agora pode criar uma entidade com IA exclusiva sem a interferência de um programador.

A questão é: essas são as duas únicas opções para estruturar a IA em uma entidade baseada em componentes e, se sim, o que daria a máxima flexibilidade?

fantasma
fonte
Eu acho que uma boa resposta dependerá de onde você deseja impor a exclusividade das ações. Se você deseja que ele esteja nos próprios objetos, o design seria muito diferente em comparação, por exemplo, aplicando-o através da interface de arrastar e soltar (este estado já possui uma ação de movimento e, portanto, não pode ter outro, esse contêiner de transição de estado já contém um estado final com base no tempo etc.).
James
2 não é uma opção viável.
Coyote

Respostas:

6

Se você pretende ter mais inimigos ou jogadores possíveis que você não pode imaginar agora, então você definitivamente deve acabar com isso. O que você está descrevendo no seu segundo ponto é basicamente o padrão de estado .

Acho que concordo com Gregory que você não deve ter componentes de estado de pé e de pé separados. É apenas um componente de movimento com velocidade 0. Por outro lado, se você tem objetos que não podem se mover, você precisa dividi-lo ou apenas colocar algum tipo de restrição booleana no estado de movimento que impede que a velocidade não seja zero .

Para o jogador, acho que não precisa ser completamente separado. Ele ainda pode usar todos os outros componentes, com a adição de um componente de entrada. Esse componente conduz as transições entre estados, enquanto no inimigo ele é controlado por uma IA padrão ou, se você quiser, subclasses diferentes de IA que seus designers inimigos podem escolher.

editar: na verdade, para seus inimigos fixos, em vez de restringir o componente de movimento, dê a eles um componente estacionário de IA que nunca opte por movê-los.

Tesserex
fonte
Um padrão de estado não implica a primeira situação? Isso resulta em um único AIComponent implementando uma máquina de estado contendo diferentes objetos de estado. O que eu quis dizer com a segunda opção foi que WalkComponent e SwingComponent são do mesmo tipo que, digamos, RenderComponent e PhysicsComponent.
ghost
@ghostonline No que diz respeito à idéia, mais ou menos. Na implementação, não realmente. AIComponent seria separado, como no segundo diagrama. Não conteria os outros componentes. A pergunta mais importante para sua segunda situação é: se o designer escolhe apenas componentes sem um programador, como a Entidade sabe quando alterar o estado? Estados diferentes implicam transições de estado diferentes - alguém ainda precisa especificá-las.
Tesserex
Você quer dizer adicionar um AIComponent à entidade no diagrama 2, que controlará o Stand / Walk / Swing-Component? Minha ideia era que os componentes enviassem sinais de bloqueio ou ativação sob certas condições. Por exemplo, SwingComponent emitia sinais genéricos, por exemplo, sinal "bound_feet" ao iniciar e "release_feet" ao finalizar o swing. O WalkComponent seria desativado e ativado com base nesses sinais. Como as 'transições de estado' são encapsuladas nos próprios componentes, o projetista não precisará de um programador conectando os componentes.
ghost
@ghostonline Isso funciona bem para coisas que têm regras fixas como "não pode andar enquanto balança", mas e as transições entre ficar e andar? Se o controle estiver em pé, como saberá tentar andar? A lógica de pé pode querer escolher andar ou balançar, o que é afetado pela total ausência de uma capacidade de andar - ela deve sempre optar por balançar nesse caso. Mas acho que você está no caminho certo.
Tesserex
2

Eu pelo menos manteria o Player AI (ou o que eu chamaria de Player Controller) como seu próprio componente. Na maioria dos jogos, o jogador é fundamentalmente diferente o suficiente dos NPCs que você não pode generalizar de um para outro, exceto no básico, como pontos de vida.

Para os NPCs, vejo StandComponent e WalkComponent como aspectos da mesma coisa. Você nunca terá um WalkComponent sem um StandComponent? Eu duvido. Da mesma forma, um RunComponent seria apenas um WalkComponent com uma velocidade mais alta e animações diferentes. Eu posso ver o valor de ter um NPCMovementComponent e um NPCSwordFighterComponent separado, mas até isso parece uma superengenharia para mim.

Gregory Avery-Weir
fonte
Eu não separaria tanto o movimento do NPC quanto o do jogador. As ações de movimento que impulsionam as animações e a física poderiam definitivamente ser compartilhadas; é o que seleciona as ações ou transições que são diferentes (o jogador recebe informações enquanto AI é ... AI). Concordo que você teria um PlayerController, mas também um AIController, que poderiam usar os componentes de movimento / componentes de balanço para executar o trabalho real de animação / física.
homebrew
Verdade. Suponho que todos os objetos em movimento tenham um PhysicsComponent ou MovementComponent que lida com seus movimentos e que o PlayerController e o AIController usariam isso para lidar com o movimento. O movimento definitivamente deve ser um componente separado, pois pode haver coisas que precisam ser movidas que não tenham IA ou tenham a IA mais simples possível (objetos de física burra, como caixotes ou lixo).
Gregory Avery-Weir
2

Primeiro, eu faria um componente State e, em seguida, criaria uma máquina de estado para lidar com as transições. Torne-o genérico o suficiente para que você possa usar isso para seus jogadores e sua IA. Isso garantirá que a IA seja executada de acordo com as mesmas regras e que você não precise alterar sua lógica ao alterar como os estados do jogador funcionam em comparação com os estados da IA.

Máquina de estado finito C ++

O exemplo acima tem um exemplo concreto de uma máquina de estado em c ++ que pode ser usada tanto por jogadores quanto por IA.

Kyle C
fonte
1

O que você quer é um componente que lide com o movimento de personagens (jogador e NPCs). O componente AI ou um componente jogador enviará comandos para esse componente de movimento e verificará se a ação pode ser iniciada. Isso encapsulará suas restrições de movimento em um único componente. Seu código de IA e código de jogador não precisam saber como a espada oscilante é executada. A IA teria estados internos, por exemplo, Ocioso, Ataque, Fugindo.

Stephen
fonte
1
TIPO: "Ele receberá ..." e o componente AI?
Pup