Implementar o Path Finder para agentes de IA com o NavMesh

imagem

Seguindo o caminho e controlando o tráfego


Às vezes, precisamos de personagens de IA para percorrer o mundo do jogo, seguindo um caminho mais ou menos definido. Por exemplo, em um jogo de corrida, os oponentes de IA devem viajar ao longo da estrada e, no RTS, as unidades devem poder se mover para o ponto desejado, se movendo ao longo do terreno e levando em consideração a posição um do outro.

Para parecerem inteligentes, os agentes de IA devem ser capazes de determinar o que estão fazendo e, se não puderem alcançar o ponto desejado, devem ser capazes de traçar a rota mais eficaz e mudar seu caminho quando obstáculos surgirem.

Evitar obstáculos é um comportamento simples que permite que as entidades de IA alcancem pontos de destino. É importante observar que o comportamento implementado neste post é para comportamentos como simulação de multidões, em que o principal objetivo de cada agente é evitar outros agentes e atingir o objetivo. Eles não determinam o caminho mais eficiente e o mais curto.

Requisitos técnicos


Requer o Unity 2017 instalado em um sistema com Windows 7 SP1 +, 8, 10 ou Mac OS X 10.9+. O código deste artigo não funcionará no Windows XP e Vista, e as versões do servidor do Windows e OS X não foram testadas.

Os arquivos de código para esta postagem podem ser encontrados no GitHub .

Para aprender o código em ação, assista a este vídeo .

Malha de navegação


Vamos descobrir como usar o gerador de malha de navegação Unity incorporado, que pode simplificar bastante a busca de caminhos para agentes de IA. Nos estágios iniciais do Unity 5.x, a função NavMesh ficou disponível para todos os usuários, incluindo aqueles com licenças de edição pessoal, embora anteriormente fosse uma função apenas para o Unity Pro. Antes do lançamento do 2017.1, o sistema era atualizado para fornecer um fluxo de trabalho baseado em componentes, mas como requer um pacote adicional para download, que no momento da redação está disponível apenas na versão de visualização, aderiremos ao fluxo de trabalho padrão baseado em cena. Não se preocupe, os conceitos de ambas as abordagens são semelhantes e, quando a implementação final finalmente chegar a 2017.x, não haverá mudanças significativas.

Saiba mais sobre o sistema de componentes NavMesh no Unity no GitHub .

Agora vamos explorar todas as possibilidades que esse sistema pode nos oferecer. Para procurar caminhos de IA, a cena deve ser apresentada em um formato específico; em um mapa 2D, uma grade bidimensional (matriz) é usada para procurar caminhos usando o algoritmo A *. Os agentes de IA precisam saber onde estão os obstáculos, especialmente os estáticos. Lidar com colisões entre objetos em movimento dinâmico é outro problema comumente chamado de comportamento de direção. O Unity possui uma ferramenta integrada para gerar o NavMesh, representando a cena em um contexto conveniente para os agentes de IA encontrarem o caminho ideal para o destino. Para começar, abra um projeto de demonstração e vá para a cena NavMesh.

Cartão de estudo


Depois de abrir a cena de demonstração do NavMesh, ela deve aparecer na captura de tela:


Cena do obstáculo e da inclinação

Esta será a nossa caixa de areia para explicar e testar a funcionalidade do sistema NavMesh. O esquema geral é semelhante a um jogo do gênero RTS (estratégia em tempo real). Nós dirigimos um tanque azul. Clique em pontos diferentes para que o tanque se mova em direção a eles. O indicador amarelo é o alvo atual do tanque.

Navegação estática


Primeiro, você precisa dizer que deve marcar toda a geometria da cena, inserida no NavMesh, como Navegação estática . Você já deve ter visto isso antes, por exemplo, no sistema de mapas de iluminação do Unity. Para tornar os objetos do jogo estáticos é muito simples, basta marcar a caixa Estática para todas as suas propriedades (navegação, iluminação, seleção, lotes, etc.) ou use a lista suspensa para especificar propriedades. A caixa de seleção está localizada no canto superior direito do inspetor dos objetos selecionados.


Navegação de propriedade Estática

Isso pode ser feito individualmente para diferentes objetos ou, se você tiver uma hierarquia interna de objetos de jogo, aplique o parâmetro ao objeto pai, após o qual o Unity oferecerá a aplicação a todos os objetos filhos.

Assar uma malha de navegação


Para toda a cena, as opções de navegação navmesh são aplicadas usando a janela Navegação . Para abrir esta janela, vá para Janela | Navegação Como qualquer outra janela, pode ser desconectada para livre circulação ou corrigida. Em nossas capturas de tela, ela é mostrada como uma guia ancorada ao lado da hierarquia, mas você pode colocar essa janela em qualquer local conveniente.

Abrindo a janela, você verá abas individuais. Será algo parecido com isto:


Janela de Navegação

No nosso caso, a captura de tela anterior mostra a guia Bake , mas em seu editor, qualquer guia pode ser selecionada por padrão.

Vamos olhar para cada uma das guias, começando pela esquerda e movendo para a direita. Vamos começar com a guia Agents , que se parece com a captura de tela:


Guia Agentes

Se você estiver trabalhando em outro projeto, poderá achar que algumas das configurações são diferentes daquelas que definimos para o projeto de exemplo mostrado na captura de tela. Na parte superior da guia, há uma lista na qual você pode adicionar novos tipos de agentes clicando no botão + . Você pode remover agentes adicionais selecionando-os e clicando no botão - . A janela mostra claramente o que várias configurações fazem ao alterá-las. Vamos ver o que cada uma das configurações faz:

  • Nome: nome do tipo de agente exibido na lista suspensa Tipos de agentes.
  • Raio: Você pode pensar nisso como o "espaço pessoal" de um agente. Os agentes tentarão evitar contato muito próximo com outros agentes com base nesse valor, porque é usado para evitar.
  • Altura: como você pode imaginar, essa configuração define a altura do agente que ele usa para evitar verticalmente (por exemplo, ao passar sob objetos).
  • Altura da etapa: esse valor determina a altura que o agente pode subir.
  • Max Slope: como veremos na próxima seção, esse valor determina o ângulo máximo no qual o agente pode subir. Usando esse parâmetro, você pode tornar as inclinações íngremes do mapa inacessíveis ao agente.

Em seguida, temos a guia Áreas , que se parece com a mostrada nesta captura de tela:


Como você pode ver na captura de tela, o Unity fornece vários tipos de áreas que não podem ser alteradas: Walkable , Not Walkable e Jump . Além de nomear e criar novas áreas, você pode atribuir a essas áreas o custo de se movimentar por elas.

As áreas têm dois propósitos: tornar as áreas acessíveis ou inacessíveis ao agente e marcar as áreas como menos desejáveis ​​em termos de custos de viagem. Por exemplo, você pode desenvolver um RPG no qual inimigos demoníacos não podem entrar em áreas marcadas como “solo consagrado”. Você também pode marcar algumas áreas do mapa como um “pântano” ou “pântano”, que o agente evitará devido ao alto custo de movimentação.

A terceira guia Bake é provavelmente a mais importante. Permite criar o próprio NavMesh para a cena. Você já deve estar familiarizado com algumas das opções. A guia Bake fica assim:


Guia Assar

As opções de tamanho do agente nesta guia determinam como os agentes irão interagir com o ambiente, enquanto as opções na guia Agentes controlam interações com outros agentes e objetos em movimento. Mas eles controlam os mesmos parâmetros, então vamos ignorá-los. A altura da queda e a distância do salto controlam até que ponto o agente pode “pular” para alcançar a parte do NavMesh que não está diretamente relacionada àquela em que o agente está localizado atualmente. Consideraremos isso com mais detalhes abaixo; portanto, se você não tiver certeza, ainda não poderá estudar esses parâmetros.

Além disso, existem opções avançadas que geralmente estão ocultas por padrão. Para expandir essas opções, basta clicar no triângulo suspenso ao lado do cabeçalho Avançado . O tamanho manual do Voxel pode ser considerado uma configuração de "qualidade". Quanto menor o tamanho, mais detalhes serão armazenados na malha. A área da região mínima é usada para pular plataformas ou superfícies de cozimento abaixo do limite selecionado. A malha de altura fornece dados verticais mais detalhados ao assar uma malha. Por exemplo, esta opção permite manter o local correto do agente ao subir escadas.

O botão Limpar exclui todos os dados do NavMesh da cena e o botão Bake cria uma malha para a cena. O processo de cozimento é bem rápido. Contanto que você tenha uma janela selecionada, poderá observar a geração do NavMesh com o botão Bake na janela de cena. Vamos clicar no botão Assar para ver os resultados. Em nossa cena de exemplo, acabamos com algo semelhante a esta captura de tela:


As áreas azuis mostram NavMesh. Abaixo retornaremos a isso. Enquanto isso, vamos para a última guia - Objeto , que se parece com isso:


Os três botões mostrados na captura de tela anterior - Todos , Renderizadores de malha e Terrains - são usados ​​como filtros de cena. Eles são úteis ao trabalhar em cenas complexas com muitos objetos na hierarquia. A seleção de uma opção filtra o tipo correspondente da hierarquia, facilitando a seleção deles. Você pode usar os botões para explorar sua cena em busca de objetos que deseja marcar como estático de navegação.

Usando o Nav Mesh Agent


Agora que montamos a cena com o NavMesh, precisamos de uma maneira do agente usar essas informações. Felizmente para nós, o Unity possui um componente Nav Mesh Agent que você pode arrastar para um personagem. Em nossa cena de exemplo, há um objeto de jogo chamado Tank , ao qual um componente já está anexado. Olhe para a hierarquia e você verá algo assim:


Existem alguns parâmetros aqui, e não consideraremos tudo, porque eles são bem claros e a descrição pode ser encontrada na documentação oficial do Unity. Mas vamos mencionar as principais coisas:

  • Tipo de agente : Lembre-se da guia Agentes na janela Navegação ? Tipos de agentes atribuíveis podem ser selecionados aqui.
  • Desviar automaticamente o link de malha desativada : essa opção permite que os agentes usem automaticamente o recurso Links de malha desativada , que discutiremos abaixo.
  • Máscara de área : aqui você pode selecionar as áreas configuradas na guia Áreas da janela Navegação .

Isso é tudo. Este componente faz 90% do trabalho duro para nós: abrindo o caminho, evitando obstáculos e assim por diante. A única coisa que você precisa é transferir o ponto de destino para o agente. Vamos olhar para este problema.

Configuração do ponto de destino


Depois de configurar o agente de IA, precisamos de uma maneira de dizer a ele para onde ir. Em nosso projeto de exemplo, existe um script chamado Target.cs que executa exatamente essa tarefa.

Esta é uma classe simples que faz três coisas:

  • "Dispara" o feixe da câmera para a posição do mouse no mundo
  • Atualiza a posição do marcador
  • Atualiza a propriedade de destino para todos os agentes NavMesh.

O código é bastante simples. Toda a classe é a seguinte:

using UnityEngine; using UnityEngine.AI; public class Target : MonoBehaviour { private NavMeshAgent[] navAgents; public Transform targetMarker; private void Start () { navAgents = FindObjectsOfType(typeof(NavMeshAgent)) as NavMeshAgent[]; } private void UpdateTargets ( Vector3 targetPosition ) { foreach(NavMeshAgent agent in navAgents) { agent.destination = targetPosition; } } private void Update () { if(GetInput()) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; if (Physics.Raycast(ray.origin, ray.direction, out hitInfo)) { Vector3 targetPosition = hitInfo.point; UpdateTargets(targetPosition); targetMarker.position = targetPosition; } } } private bool GetInput() { if (Input.GetMouseButtonDown(0)) { return true; } return false; } private void OnDrawGizmos() { Debug.DrawLine(targetMarker.position, targetMarker.position + Vector3.up * 5, Color.red); } } 

As seguintes ações ocorrem aqui: no método Start , inicializamos a matriz navAgents usando o método FindObjectsOfType () .

O método UpdateTargets () passa por nossa matriz navAgents e define o ponto de destino para eles no Vector3 fornecido. Essa é a chave do código. Você pode usar qualquer mecanismo para obter o ponto de destino e, para o agente ir para lá, basta definir o campo NavMeshAgent.destination ; o agente fará o resto.

Em nosso exemplo, os cliques são usados ​​para se mover; portanto, quando um jogador clica no mouse, liberamos o raio da câmera para o mundo na direção do cursor do mouse e, se ele se cruzar com alguma coisa, atribuímos um ponto de colisão ao novo agente targetPosition . Também ajustamos o marcador de destino para visualizar facilmente o destino no jogo.

Para testar a operação, você precisa assar o NavMesh de acordo com a descrição da seção anterior, iniciar o modo de reprodução e selecionar qualquer área no mapa. Se você clicar várias vezes, poderá ver que o agente não pode alcançar algumas áreas - a parte superior dos cubos vermelhos, a plataforma superior e a plataforma na parte inferior da tela.

Cubos vermelhos estão muito altos. A inclinação que leva à plataforma mais alta é muito acentuada para nossas configurações de Max Slope , e o agente não pode subir. As seguintes capturas de tela mostram como as configurações de inclinação máxima afetam o NavMesh:


NavMesh com inclinação máxima = 45

Se você alterar o valor de Max Slope para algo como 51 e, em seguida, clique no botão Bake novamente para executar o NavMesh novamente, os resultados serão os seguintes:


NavMesh com inclinação máxima = 51

Como você pode ver, podemos personalizar o design do nível, tornando áreas inteiras inacessíveis, alterando um único parâmetro. Isso pode ser útil, por exemplo, quando você tem uma plataforma ou borda que requer uma corda, escada ou elevador para subir. Ou talvez uma habilidade especial, por exemplo, a capacidade de escalar?

Aplicação fora dos links de malha


Você pode perceber que existem duas interrupções em nossa cena. Nosso agente pode entrar no primeiro, mas o da parte inferior da tela está muito longe. Esses cálculos não são completamente arbitrários. Os Links fora da malha criam essencialmente uma ponte entre os espaços entre segmentos NavMesh não relacionados. Esses links podem ser vistos no editor:


Círculos azuis com linhas de conexão são conexões.

O Unity pode gerar esses links de duas maneiras. O primeiro que já consideramos. Lembre-se do valor de Distância do salto na guia Assar da janela Navegação ? O Unity usa automaticamente esse valor para gerar esses links ao assar o NavMesh. Tente alterar o valor em nossa cena de teste para 5 e assar novamente. Veja - as plataformas agora estão conectadas? Isso ocorre porque as malhas agora estão dentro do novo limite especificado.

Mude o valor para 2 novamente e asse. Agora vamos olhar para o segundo caminho. Crie as esferas que serão usadas para conectar as duas plataformas. Coloque-os aproximadamente como mostrado na captura de tela:


Você já pode ver o que está acontecendo, mas vamos analisar o processo que permite que eles se conectem. No nosso caso, chamei a esfera no começo direito e a esfera no lado esquerdo. Você logo entenderá o porquê. Em seguida, adicionei o componente Off Mesh Link à plataforma à direita (em relação à captura de tela anterior). Você notará que o componente possui campos de início e fim . Como você pode imaginar, arrastaremos as esferas criadas anteriormente para os slots correspondentes - a esfera inicial no campo inicial e a esfera final no campo final . O inspetor ficará assim:


O valor da Substituição de Custo é levado em consideração quando recebe um valor positivo. Aplica um fator de custo ao usar esse relacionamento, em vez de uma rota mais econômica para o destino.

Bidirecional, se verdadeiro, permite que o agente se mova nas duas direções. Para criar links com tráfego unidirecional, você pode desativar esse valor. O valor Ativado é usado de acordo com seu nome. Se falso, o agente ignora essa associação. Você pode ativá-lo e desativá-lo para criar cenários de jogo nos quais, por exemplo, um jogador deve pressionar um botão para ativar uma conexão.

Para habilitar esse relacionamento, não é necessário re-assar. Olhe para o seu NavMesh e você verá que ele se parece exatamente com a captura de tela:


Como você pode ver, o menor espaço ainda se conecta automaticamente e agora temos uma nova conexão gerada pelo componente Off Mesh Link entre as duas esferas. Inicie o modo Play e clique na plataforma remota. Como esperado, o agente agora pode navegar para a plataforma desconectada:


Nos níveis do seu jogo, você pode precisar alterar esses parâmetros para alcançar os resultados desejados, mas uma combinação desses recursos fornece uma ferramenta conveniente e pronta. Você pode criar rapidamente um jogo simples usando a funcionalidade NavMesh.

Este tutorial faz parte da Unity 2017 Game AI Programming - Third Edition de Ray Barrera, Aung Sithu Kyaw e Thet Naing Swe.

Source: https://habr.com/ru/post/pt414453/


All Articles