Neste tutorial você irá aprender como criar uma inteligência artificial que segue o jogador pelo cenário em um jogo 3D. Para isso usaremos o node Navigation.

Tópicos:

O que é Pathfind

Exemplo de pathfind

Pathfinding significa em inglês busca de caminho. O algoritmo responsável por mover um personagem de um ponto a outro, desviando de obstáculos, é famoso pois é usado muito por programadores de games. A movimentação de agentes (um personagem do jogo) em um espaço é um dos segmento da Inteligência Artificial mais comuns na produção de games. O objetivo desse tipo de algoritmo é achar o caminho mais rápido entre dois pontos ( por exemplo: o inimigo é o jogador) evitando obstáculos, como paredes ou outros obstáculos.

O que é o Navigation

O node Navigation é responsável por encontrar este caminho. Ele possui métodos para encontrar o caminho dentro de uma malha, esta malha é um node chamado NavigationMeshInstance. Basicamente esta malha é o caminho que representa onde um personagem controlado pelo Navigation pode se locomover.

O espaço mais claro é a malha do node NavigationMeshInstance, que representa o espaço físico onde um agente pode se mover

Estrutura da Cena

Antes de prosseguir, é preciso entender a estrutura do node Navigation. O Navigation precisa de um node filho chamado NavigationMeshInstance. Isto é importante, pois este é o node que representa o caminho que o agente vai se mover o qual nós iremos gerar este caminho. Quando geramos este caminho estamos fazendo um “bake” que inglês significa cozinhar. Para realizar o “bake” precisamos de alguns nodes de malha, que podem ser um MeshInstance ou algum node do tipo CSG. Estes nodes devem ser filho do node NavigationMeshInstance.

Também temos um node para colocar os inimigos, um Player que se movimenta em 8 direções (Veja o Compendium -  Movimento em 8 Direções 23) e o Navigation.

Repare na mensagem de erro próxima ao node.

Ele está informando que precisamos um NavigationMesh para o node NavigationMeshInstance. Para fazer isso selecione o node e no Inspector crie um novo NavigationMesh.

Após montar a cena desejada, selecionamos o node NavigationMeshInstance. Ao fazer isso um botão irá surgir na parte de cima da Viewport chamado Bake NavMesh. Clique neste botão para gerar a malha de movimentação dos agentes.

Após carregar, a malha de navegação estará construída e pronta para ser usada.



Código Principal

Antes de prosseguir, crie um Script para o node fase, depois vamos adicionar um node Timer na cena, este timer será responsável por ficar chamando a função que irá calcular o melhor caminho para o alcançar o jogador. Como o jogador se move o tempo todo o tempo desse timer deve ser baixo.

Crie já um sinal no Timer para chamar no código da fase. Mude as configurações do Timer para estes valores:

As primeira linhas de código serão dentro do sinal criado pelo Timer:

Na linha 4 estamos pegando a posição do jogador e na linha 5 pegamos o node Navigation. Agora vamos criar um código para ficar atualizando o movimento de todos os inimigos. O código será esse:

Na linha 7 iniciamos um loop que irá executar o código para cada inimigo filho do node Enemies. A função get_simple_path( ) busca o melhor caminho entre o inimigo atual ( enemy.global_position ) e o jogador ( player ).

Na linha 11 verificamos se o caminho até o jogador é possível, se for o chamamos a função que faz o inimigo começar andar pelo caminho. Esta função ainda não existe, vamos ter que criar ela mais para frente no inimigo.

Basicamente o que este código faz é isto:

Ele cria um caminho por pontos, que são coordenadas para cada inimigo seguir.

Código do Inimigo

Para criar a cena do inimigo vamos usar um KinematicBody. Mude o nome desse node para Enemy. Coloque o Collision Shape que irá representar a colisão do objeto.

Use o formato de Sphere

Após isso você deve adicionar um node Spatial e dois Nodes MeshInstance. Crie uma estrutura como esta:

Crie Malhas para os objetos criados

Você pode também criar materiais para os objetos, a fim de mudar a cor deles.

Crie um Script para o inimigo. As primeiras variáveis desse script serão speed e path.

A variável path será usado para armazenar o caminho entre o inimigo e o jogador, para o nosso inimigo usar este caminho como referência. Já a variável speed representa a velocidade do inimigo.

Agora vamos criar um processo para movimentar o inimigo assim que ele possuir um caminho. Adicione este script após as variáveis.

A linha 7 verifica se o inimigo possui um caminho. Se possuir, criamos a variável dir. Esta variável será um Vector3 que armazena a direção que inimigo tem que andar e distância entre o ponto mais próximo que explicamos mais cedo.

A linha 11 verifica se o inimigo está próximo ao ponto mais próximo. Se for verdade o código remove o ponto, fazendo o inimigo ir para o próximo ponto. Se não, a linha 14 move o inimigo para o ponto mais próximo

Agora só precisamos daquela função que faz o inimigo receber o caminho: move_to( )

Coloque alguns inimigos filhos do nodo Inimigos na cena principal.

Se você testar o projeto agora você verá um comportamento estranho:

Você vai perceber que os inimigos tendem a ficar presos na parede. Para evitar este tipo de comportamento é bom desativar a colisão dos inimigos com as paredes, mas continuar colidindo com o jogador. Para isso vamos ter alterar as máscaras de colisão dos inimigos e do jogador. Abra cena do inimigo e altere estas propriedades:

Isto significa que o inimigo não vai mais colidir com objetos ques esteja na Layer 1 e somente na Layer 2. Todos objetos por padrão colidem na Layer 1. Neste caso o inimigo não vai colidir com a parede.

Agora vamos alterar a Layer do Player, para ele colidir tanto quanto a Layer 1 e a Layer 2.

Agora teste o projeto. Você terá um resultado como este abaixo:

É um resultado interessante, mas nos próximos tópicos vamos ver como é possível melhorar este movimento.

Melhorando a Movimentação do Inimigo

A primeira coisa que poderíamos fazer para melhorar a forma como o inimigo segue o jogador é fazê-lo olhar em direção para onde ele está indo. Para isso podemos adicionar essas linhas no script do inimigo, antes da variável dir.

 Se você testar o projeto o resultado será esse: