Respostas:
Geralmente, você deseja usar o A *, a menos que haja algo importante que esteja procurando.
Eu precisava resolver um problema semelhante: encontrar caminhos em uma grande grade semelhante a um labirinto, com constantes "custos" e barreiras.
O problema é que, no jogo de defesa de torre, o número de entidades que precisam resolver o caminho para elas é geralmente muito maior que o número de nós no gráfico. A * não é o algoritmo mais apropriado para lidar com isso, porque você precisará resolvê-lo novamente sempre que algo for alterado. Bem, é apropriado se você precisar encontrar apenas um caminho, mas no meu caso eu precisava ser capaz de lidar com entidades que podem aparecer em locais diferentes e cada uma tem seu próprio caminho.
O algoritmo de Floyd-Warshall é muito mais apropriado, embora, para o meu caso, eu escrevi um algoritmo personalizado que, sempre que um nó é alterado, recalcula o custo desse nó de todos os seus vizinhos e, se os vizinhos foram alterados, ele é chamado recursivamente sobre eles.
Então, no início do jogo, eu apenas inicio esse algoritmo em todos os meus nós de "objetivo". Então, sempre que um único nó é alterado (por exemplo, torna-se impossível de passar), eu apenas o aciono nesse nó e a alteração é propagada para todos os nós que serão afetados e, em seguida, interrompida. Portanto, não há necessidade de recálculo global, e o algoritmo é completamente independente do número de entidades que exigirão a busca de caminhos.
Meu algoritmo era basicamente algo como (pseudo-código):
update_node method in Node class:
$old <- $my_score
find $neighbor node among all neighbors such that
$neighbor.score + distance_to($neighbor) is minimal
$my_score <- $neighbor.score + distance_to($neighbor)
$next_node <- $neighbor
if ($my_score != $old)
for each $neighbor
$neighbor.update_node()
Com a pontuação inicial, dependendo se o nó é um alvo ou algum tipo de barreira.
O algoritmo de rota que usei no meu TD foi invertido do caminho A * normal devido ao número de entidades que eu tinha no jogo. Em vez de passar do gol para os bandidos, passei do gol para todos os quadrados vazios do tabuleiro.
Isso não leva muito tempo, você continua repetindo o quadro até encontrar seus "custos" e fornece um bom feedback para rotas bloqueadas (se você estiver fazendo isso). O uso do algoritmo de Floyd é rápido e fácil de armazenar em cache em comparação com o A *, pois não faz pesquisas dependentes de dados, é apenas carregar e operar dados em fluxos.
Comece com seu quadro definido como custo infinito, defina o quadrado da meta como zero e, em seguida, repita o quadro para verificar se uma célula vizinha custa menos que o custo atual mais o custo da viagem. O custo da viagem é onde você coloca sua heurística (no meu caso, o custo de viajar na diagonal era infinito, mas o custo de viajar através de uma torre era alto, então eles eram autorizados a comer através de torres, mas não o faziam, a menos que não tivessem escolha)
Depois de obter sua grade de custos, você pode criar rapidamente uma grade de "fluxo" testando o gradiente de custo mais alto nas células. Isso funciona muito bem para grandes quantidades de bandidos, porque nenhum deles precisa encontrar o caminho, apenas segue as placas virtuais.
Outro benefício é que, dessa forma, você só precisa executar essa tarefa toda vez que ajustar os obstáculos (ou no meu jogo, quando um rastejador come uma de suas torres). Nem toda vez que um creep / mob entra em campo (o que com milhares de monstros e dezenas de segundos seria uma dor de cabeça).
A busca de caminhos é rápida e, em algo do tamanho de um jogo de defesa de torre normal, você não terá problemas para executar um passe A * ou Dijkstra completo sempre que algo mudar. Estamos falando bem em um milissegundo para uma atualização completa. Qualquer tipo de busca de caminhos adaptativos acaba terrivelmente complicada. Faça da maneira mais simples, a menos que você esteja na maior grade de defesa de torre do mundo.
Como o A * pathfinding funciona? pode ser um bom lugar para começar :-)
Isso pode ser um exagero para a sua solução, mas pense em rotear para trás em vez de para a frente.
Em um jogo de TD, os inimigos geralmente estão tentando chegar a um objetivo específico. Rota para trás desse objetivo para o primeiro inimigo e, em seguida, armazene em cache esse caminho. Na geração do caminho para inimigos subsequentes, use o primeiro caminho como ponto de partida e assim por diante. Se você tiver vários pontos de entrada de inimigos, ou vários objetivos, tenha vários caminhos pré-armazenados em cache para começar.
A busca de waypoints provavelmente seria a melhor para um jogo td, porque geralmente com base no nível em que o ai segue um caminho direto. Basicamente, você define seus nós ou waypoints, em seguida, tem o ponto "ai" em direção ao waypoiny e caminha até ele, uma vez que se aproxima o suficiente para que ele vá para o próximo waypoint, enfrenta-o e move-se em direção a ele.
Como alguém perguntou, você aborda ambientes de mudança dinâmica recalculando o caminho toda vez que o jogador coloca ou remove uma torre, e você apenas armazena esse caminho na memória nesse meio tempo. O ambiente não muda em todos os quadros.
Observe que alguns jogos de TD têm um caminho definido e não permitem que você coloque torres nele, então eles resolvem encontrar o caminho da maneira mais fácil: codificando um caminho e não permitindo que você o bloqueie :)
Uma solução simples é enganar.
Crie o mapa com antecedência para garantir que não haja becos sem saída. Em cada cruzamento, adicione um gatilho que faça o personagem escolher uma rota (exemplo: sempre vire à esquerda, sempre vire à direita ou aleatório).