1. Introdução
Ao desenvolver meu jogo, cheguei ao ponto de criar os primeiros NPCs. E surgiu a questão de como fazer o NPC contornar o muro e não "entrar nele".
Subindo pela Internet, encontrei os seguintes algoritmos:
- Pesquisa ampla (BFS, pesquisa pela primeira vez)
- Algoritmo de Dijkstra (Dijkstra)
- Uma estrela "A com um asterisco"
- Pesquise pela primeira melhor correspondência (pesquisa pela primeira vez)
- IDA (A com aprofundamento iterativo)
- Pesquisa de ponto de salto
E decidi tentar implementar meu A * em uma grade 3D voxel.

Exemplo de mapa do meu jogo Descrição do algoritmo
Um * passo a passo examina todos os caminhos que vão do vértice inicial ao final, até encontrar o mínimo. Como todos os algoritmos de pesquisa informados, ele primeiro analisa as rotas que "parecem" levar ao objetivo. Do algoritmo ganancioso, que também é o algoritmo de busca da primeira melhor correspondência, ele se distingue pelo fato de que, ao escolher um vértice, leva em consideração, entre outras coisas, todo o caminho percorrido até ele.
Visualização do trabalho de A * na Wikipedia Implementação
Como o algoritmo precisa de "nós" - pontos para determinar o caminho, escrevemos a estrutura de classes do nó:
Código do nó:public enum EMoveAction { walk, jump, fall, swim }; public class PathPoint {
Construtores de classe pequena: private PathPoint NewPathPoint(Vector3 point, float pathLenghtFromStart, float heuristicEstimatePathLenght, EMoveAction moveAction) { PathPoint a = new PathPoint(); a.point = point; a.pathLenghtFromStart = pathLenghtFromStart; a.heuristicEstimatePathLenght = heuristicEstimatePathLenght; a.moveAction = moveAction; return a; } private PathPoint NewPathPoint(Vector3 point, float pathLenghtFromStart, float heuristicEstimatePathLenght, EMoveAction moveAction, PathPoint ppoint) { PathPoint a = new PathPoint(); a.point = point; a.pathLenghtFromStart = pathLenghtFromStart; a.heuristicEstimatePathLenght = heuristicEstimatePathLenght; a.moveAction = moveAction; a.cameFrom = ppoint; return a; }
Em seguida, a estrutura das configurações de pesquisa de caminho será útil:
Código de configurações da pesquisa de caminho: public struct SPathFinderType {
Além disso, "World" é um tipo de classe de banco de dados para armazenar informações sobre blocos de mapas. O seu pode ser implementado de maneira diferente.
O resultado da função de pesquisa de caminho de obter a rota: public List<PathPoint> GetPathToTarget(Vector3 beginPoint, Vector3 targetPoint, World worldData, SPathFinderType pfType) { List<PathPoint> path = new List<PathPoint>();
Ponto próximo
A função ClosePoint depende apenas da implementação da classe World, adiciona todos os caminhos possíveis à lista de pontos em aberto e remove o ponto atual dessa lista (fecha-a). Vou dar um exemplo do meu "ponto de fechamento" nas quatro primeiras direções.
Aviso grande pilha de código private List<PathPoint> ClosePoint(int index, List<PathPoint> openPoints, List<PathPoint> closedPoints, World worldData, SPathFinderType pfType, Vector3 targetPoint) { List<PathPoint> newOpenPoints = openPoints; PathPoint lastPoint = openPoints[index];
Otimização
Simplesmente dividindo o caminho do ponto inicial ao ponto atual, reduzimos o número de nós muitas vezes e o tornamos mais ganancioso.
return this.heuristicEstimatePathLenght + this.pathLenghtFromStart /2;
Sumário
Prós:
- Pesquisa rápida em espaços abertos.
- Versatilidade do algoritmo
Contras:
- Requer muita memória para calcular a rota.
Verde mostra uma lista aberta de nós, caminho vermelho para o destino, nós fechados azuis.
Rotas recebidas antes da otimização: Rotas recebidas após otimização: Literatura
https://tproger.ru/articles/pathfindings/
https://ru.wikipedia.org/wiki/A*