A* Pathfinding com NavMesh
February 25, 2012 in AI, Pathfinding, Português, Tutorials
Estou escrevendo este post pois é um tanto quanto difícil encontrar materiais sobre A* com navmesh. A maioria dos artigos na web são e inglês e descrevem a rotina de uma forma bem genérica e qualitativa. O objetivo deste post é facilitar um pouco a vida dos programadores de jogos que não falam inglês ou que tem uma certa dificuldade com as explicações genéricas da maior parte dos artigos na internet.
Neste post vou descrever passo a passo como funciona a rotina de um A* quando trabalhamos com navmeshes. Para entender o conceito é necessário que você já esteja familiarizado com o A* para grids. Caso ainda não tenha total compreendimento do A*, por favor, leia este artigo para iniciantes antes de continuar. Nas partes com código vou utilizar a linguagem Blitz3D a qual tem uma sintaxe derivada do basic e portanto é de fácil entendimento. De qualquer forma, você pode programar o seu algorítimo na linguagem que preferir.
Antes de começar você já deve ter os seus navmeshes mapeados. Eles podem ser qualquer polígono, com a única condição de ser obrigatoriamente convexo. Eu recomendo fortemente que se utilize triângulos. Apesar de não ser a forma mais rápida de rodar, é de fato, a forma mais fácil de programar. Neste tutorial eu vou utilizar este método. Você pode mapeá-los de inúmeras formas, desde a simplicidade de fazer isso a mão ou criar algorítimos sofisticados para mapear automaticamente (pretendo escrever sobre isso mais tarde). O importante neste tutorial é que a estrutura dos seus navmeshes contenha as posições das três vértices que compõem cada triângulo do seu navmesh. Em B3D você pode guardar esse dados facilmente desta forma:
Type Triangulo
…….Field Vertice1X#, Vertice1Y
…….Field Vertice2X#, Vertice2Y
…….Field Vertice3X#, Vertice3Y#
End Type
(obs: você pode utilizar BlitzArrays se preferir)
Com todos os triângulos mapeados é hora de abordar o problema. Podemos explicar a problemática através da seguinte imagem:
Seu objetivo aqui é simples: Conseguir encontrar o caminho mais curto do ponto inicial até o ponto final, de forma que, o caminho só seja válido se estiver dentro do NavMesh. Para isso, vamos dar uma olhada mais próxima dos pontos inicial e final.
A probabilidade do início ou fim do caminho estarem exatamente sobre a vértice de algum triângulo é praticamente nula. Por este motivo, nós temos que ter uma rotina para descobrir em qual triângulo cada pontos está situado.
Um método fácil é criar uma função chamada PontoEstaNoTriangulo( args ). Você fornece a esta função as coordenadas das três vértices do triângulo juntamente com a coordenada do ponto em questão. Esta função deverá retornar VERDADEIRO se o ponto estiver dentro do triângulo e FALSO caso não esteja. Existe uma maneira bem simples e barata (em termos de processamento) de fazer isso. Utiliza-se produtos vetoriais para verificar se o ponto está dentro da geometria ou não. Este algorítimo está descrito neste artigo (em inglês). Caso não consiga entender o método, por favor, deixe um comentário no post que posso explicar mais detalhadamente. Há outra maneira de realizar está tarefa. Ela envolve calcular a área de alguns triângulos criados por estas vértices e compara-las. Eu fortemente desaconselho está opção. Pode parecer uma solução fácil, mas ela é muito cara em termos de processamento e o limite de casa decimais da variável utilizada pode levar a resultados incorretos. Utilize o algorítimo sugerido acima.
Com a sua função escrita, o que você precisa fazer é testar o ponto de início e o ponto de final com todos os triângulos do seu navmesh até encontrar a quais triângulos eles estão situados. A primeira vista pode parecer uma tarefa pesada de se calcular, no entanto ela só é realizada uma vez durante todo o calculo do caminho. Depois desta analise você pode ter três tipos de resultados e é importante analisar o significado de cada um deles.
- Ambos os pontos estão situados no mesmo triângulo
Este resultado quer dizer que é possível ir em linha reta do ponto inicial ao final (o que com certeza é o menor caminho entre eles). Neste caso você não precisa calcular um caminho e sim apenas retornar uma linha reta como resultado. - Para um ou ambos os pontos não foi encontrado um triângulo
Isso significa que um ou ambos os pontos estão fora do navmesh. Minha solução neste caso é simplesmente abortar o cálculo e não retornar nenhum caminho. Entretanto, você pode fazer alguma função para lidar com erro como esse (por exemplo: um caminho em linha reta até o navmesh mais próximo e depois calcular o caminho normalmente). - Os pontos estão em triângulo diferentes
Neste caso você deve calcular o caminho utilizando a metodologia abaixo.
Sabendo em qual triângulo seu ponto inicial está localizado, você pode adicionado a lista fechada e colocar todos os pontos adjacentes a ele (vértices do triângulo) na lista aberta. Lembre-se de colocar o ponto inicial como pai das vértices na lista aberta. Fazendo este procedimento, você chegará a algo assim:
Para cada ponto enviado para a lista aberta, calcule o custo G e H dele. Você pode encontrar na internet várias formas de calcula-los. Neste caso vou utilizar a distância euclidiana (em linha reta) do ponto até o ponto inicial como o custo G e a distância euclidiana do ponto até o ponto final como custo H. Com certeza não é a melhor solução, mas funciona! Após realizado os cálculos, encontre o ponto que contém o menor valor de F, onde F = G + H.
Para o ponto encontrado repita o procedimento anterior (enviar para a lista fechada, enviar para a lista aberta os pontos adjacentes, calcular os valores de G e H e encontrar o ponto com o menor F). A única questão de atenção aqui é em colocar uma condição para os pontos na lista fechada nunca serem enviados para a lista aberta. Eles já foram analisados e não precisamos analisá-los novamente.
Caso algum ponto que você esteja tentando enviar para a lista aberta já estiver lá, apenas cacule novamente o valor do custo G. Caso ele seja menor do que o custo atual, substitua na lista. Dica: Na hora de calcular o custo G, não se esqueça que ele é a soma da distância do ponto inicial até a vértice 2 mais a distância da vértice 2 até a vértice a ser calculada.
Repetindo o processo você saberá que terminou quando você adicionar a lista fechada qualquer uma das vértices do triângulo que contém o ponto final. Quando você chegar nesta situação o seu próximo caminho é apenas uma linha reta da vértice adicionada até o ponto final.
Exatamente como fazemos no algorítimo para grids, devemos realizar uma backtracking para descobrir o caminho até o ponto. Isso é realizado começando do ponto final e voltando (através dos pontos pais) até o ponto inicial. Depois disso é só retornar o caminho encontrado para o personagem seguir.
Você provavelmente irá notar que este algorítimo retorna um caminho encostado as paredes. Este é um fenômeno natural dos navmeshes. Para conserta-lo você pode criar uma outra análise após o caminho ser gerado que faz o seguinte:
Se é possivel ir em linha reta do ponto 1 até o ponto 3. Então, deletar o ponto 2 (pois é inútil).
Utilizando a lógica acima, você terá o caminho mais curto do ponto inicial ao final. Entretanto, isso ainda parece um pouco robótico. Se quiser simular caminhos que pareçam mais humanos, sugiro a leitura do seguinte artigo The AI Systems of Left 4 Dead (em inglês) escrito pelo Michael Booth, onde ele descreve como foram criados alguns dos sistemas de inteligência artificial do Left 4 Dead.
Ficamos por aqui pessoal! Caso ainda tenha alguma duvida, deixe um comentário abaixo que irei responder assim que possível.
Referências
- AI Game Programming Wisdom, Ed. Steve Rabin, Charles River Media, 2002
- MILLINGTON, Ian. Artificial intelligence for games. 2nd ed. New York: Elsevier; c2009. 870 p. ISBN 9780123747310
- A * Pathfinding para Iniciantes - http://www.policyalmanac.org/games/aStarTutorial_port.htm
- Point in triangle test - http://www.blackpawn.com/texts/pointinpoly/default.html
- Fixing Pathfinding Once and For All - http://www.ai-blog.net/archives/000152.html
- Michael Booth. 2009. “The AI Systems of Left 4 Dead.” Artificial Intelligence and Interactive Digital Entertainment Conference (Stanford University). http://www.valvesoftware.com/publications/2009/ai_systems_of_l4d_mike_booth.pdf





















