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.
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?
fonte
Respostas:
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.
fonte
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.
fonte
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.
fonte
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.
fonte