Personagens inimigos
Na Unreal Engine, os PNJs e os inimigos geralmente são configurados de forma muito semelhante ao jogador, ou seja, o inimigo tem um controle e um pawn. Na verdade, os inimigos e o jogador têm algumas funcionalidades compartilhadas em uma classe base comum, AParrotCharacterBase. No jogo Parrot, as árvores de comportamento são usadas para controlar o comportamento do inimigo, então a classe controlador de IA é usada para criar nossos controladores inimigos. Começaremos com o Blueprint de animação, já que essa configuração é compartilhada entre os inimigos.
Os Blueprints de animação do inimigo
Modelos de Blueprint de animação
Para casos em que você tem várias malhas esqueléticas que compartilham necessidades idênticas de animação, você pode criar o modelo de projeto de animação (consulte Animation Blueprint Linking). Neste caso, há quatro tipos diferentes de inimigos: um esqueleto sem cabeça, um esqueleto, um tubarão e um tubarão chefe. Como todos eles compartilham um esqueleto e uma configuração de animação idênticos, um modelo que implementa o gráfico de animação e o gráfico de eventos pode ser reutilizado para todos eles. Você pode ver como este modelo é configurado aqui: Blueprints > Enemy > EnemyBase > ABT_EnemyBase. É muito semelhante ao Blueprint de animação do jogador, porque um modelo é configurado quase da mesma forma que o Blueprint de animação.
Blueprints de animação
Nesse caso, toda a implementação está no modelo, enquanto as animações em si estão ausentes, já que o modelo não faz referência a um esqueleto específico. Cada um dos inimigos tem seu próprio Blueprint de animação derivado do modelo, e cada um tem as animações daquele esqueleto inseridas nas substituições do gráfico de animação. Você pode ver um exemplo disso aqui: Blueprints > Enemy > HeadlessSkeleton > ABP_Enemy_HeadlessSkeleton.
Abaixo, selecionamos as animações corretas para a malha do esqueleto sem cabeça para cada estado de animação.
O pawn inimigo
Os personagens inimigos de Parrot compartilham muitas funcionalidades com o jogador; especificamente, pontos de vida e funções similares (como a morte). Esta implementação compartilhada pode ser vista em AParrotCharacterBase. Para a implementação específica do inimigo, temos uma subclasse AParrotEnemyCharacterBase. Isso lida com toda a implementação de como o sistema de patrulha funciona, como o combate funciona e muito mais. Você pode aprender mais sobre como o combate funciona na documentação do Combate em Parrot.
Nesta configuração, os testes de acerto e ferimento de combate são feitos no inimigo com uma implementação sobre como acionar volumes na classe base do Blueprint encontrada aqui: Blueprints > Enemy > EnemyBase > BP_EnemyCharacter_Base.
Essa implementação é feita aqui em vez de ser manipulada na classe C++ nativa, porque os volumes de disparador precisam ser diferentes para cada inimigo devido aos diferentes formatos, tamanhos e complexidades de suas malhas. Isso é necessário porque um volume de disparador herdado não pode ser modificado na classe derivada.
O controlador de IA inimigo
Como mencionado anteriormente, os inimigos são controlados com árvores de comportamento, então há uma classe base derivada de AIController em AParrotEnemyAIControllerBase. Aqui você verá uma variedade de BlueprintImplementableEvents usados para enviar dados ao quadro-negro para serem usados pelas árvores de comportamento.
Você pode ver como o controlador de IA passa dados para a árvore de comportamento e como ele lida com a detecção da presença do jogador em Blueprints > AI > EnemyBase > BP_EnemyController_Base.
Criando IA com Árvores de Comportamento
A Unreal Engine oferece uma infraestrutura poderosa e flexível para construir IA usando árvores de comportamento. Um guia de início rápido para configurar o boilerplate básico com alguns comportamentos pode ser encontrado no Behavior Tree Quick Start Guide. Os resultados desse guia são usados como base da IA inimiga e para fazer alterações para configurar os comportamentos necessários.
Temos duas tarefas de árvore de comportamento, Blueprints > AI > EnemyBase > BTT_FindNextPatrol e Blueprints > AI > EnemyBase > BTT_AttackPlayer para fornecer a funcionalidade básica necessária para patrulhar e atacar o jogador. Há um quadro-negro compartilhado, Blueprints > AI > EnemyBase > BB_Enemy_Base, já que a infraestrutura para dar suporte a todas as funcionalidades está presente em cada inimigo. Cabe à implementação da árvore de comportamento decidir quais funcionalidades usar para personalizar os comportamentos dos inimigos.
Cada um dos inimigos tem uma configuração de árvore de comportamento distinta, de modo que cada um deles se comporta de uma maneira diferente. Você pode dar uma olhada em todos os quatro nos seguintes locais:
Blueprints > AI > HeadlessSkeleton > BT_HeadlessSkeleton
Blueprints > AI/Skeleton > BT_Skeleton
Blueprints > AI > Sharky > BT_Sharky
Blueprints > AI > BossShark > BT_Boss_Shark
Criando um sistema de pontos de patrulhamento autorizável.
Para os inimigos, o Parrot não quer a mesma funcionalidade de patrulha demonstrada no guia da árvore de comportamento, onde a IA inimiga seleciona aleatoriamente um ponto navegável dentro de um raio a cada 4 segundos. O que se quer são inimigos que sigam ativamente uma rota de patrulha para frente e para trás, como em muitos outros jogos de plataforma.
Para isso, a Parrot criou seu próprio sistema que fornece as funcionalidades necessárias. Isso consiste no UParrotEnemyPatrolRigComponent que pode ser colocado na cena para criar uma patrulha. Ele usa subobjetos padrão de classe para instanciar uma spline. A spline é usada para criar as trajetórias de patrulha, dois volumes de disparador (usados para acionar o comportamento da IA) e um visualizador somente para o editor, que desenha a ordem das trajetórias. A ordem das trajetórias permite que a direção do caminho de patrulha seja vista no momento da edição. Você pode ver os detalhes da implementação C++ em UParrotEnemyPatrolRigComponent.
Esta implementação é um componente para que um equipamento de patrulha possa ser anexado a qualquer ator em uma cena. Isso permite colocar um equipamento em um objeto em movimento e significa que a patrulha permanecerá corretamente posicionada no espaço local. Para colocar um equipamento de patrulha que não esteja anexado a um ator existente em uma cena, temos o AParrotEnemyPatrolRigActor, que é um ator que você pode colocar em qualquer lugar de uma cena e que gera umUParrotEnemyPatrolRigComponent como um subobjeto padrão em si mesmo.
Esta implementação permite selecionar um inimigo para ser gerado em tempo de execução para seguir o caminho da patrulha. Esse processo usa uma funcionalidade da Unreal Engine chamada geração de ator adiada, que permite a geração de um ator em dois estágios: o primeiro constrói o objeto ator sem executar nenhuma inicialização AActor, como BeginPlay. Isso permite que você execute qualquer configuração ou instalação necessária para o ator antes de ele ser inicializado. Depois de executar essa configuração, você invoca o segundo estágio para finalizar a geração e inicializar o ator. Isso é feito para o equipamento de patrulha para que, quando o ator inimigo for gerado, a spline de patrulha e os volumes de disparador possam ser canalizados para o ator para que possam ser processados durante a inicialização do ator e a sequência de patrulha possa começar automaticamente.
Você pode visualizar o código que executa a geração de ator adiado em UParrotEnemyPatrolRigComponent. Existe uma funcionalidade semelhante no Blueprint, onde você pode marcar propriedades em um ator de Blueprint como Expor ao gerar, o que permite que você passe argumentos para o nó do Blueprint de geração, que são definidos antes da inicialização do ator ocorrer.
Na imagem abaixo, você pode ver um exemplo de como um desses equipamentos de patrulha é configurado em Maps > Level_1 > Level_1 com um equipamento de patrulha de dois pontos, começando no ponto 0 à direita. Este equipamento de patrulha está configurado para gerar um BP_EnemyCharacter_Skeleton.
Abaixo, há um menu de alternância Mostrar sinalizador para identificar os pontos de referência do equipamento de patrulha para fácil identificação no momento da edição. A maioria das caixas de seleção de exibição de sinalizadores foram selecionadas. Você pode ver detalhes de como isso é implementado em um componente visualizador somente de editor, UParrotPatrolRigDebugVisualizer.