Introduction
When developing my game, I got to the point of creation of the first NPCs. And the question was how to make the NPC go around the wall and not “go into it”.
Climbing on the Internet, I found these algorithms:
- Search Wide (BFS, Breadth-First Search)
- Dijkstra's Algorithm (Dijkstra)
- And Star "A with an asterisk"
- Search for the first best match (Best-First Search)
- IDA (A with iterative deepening)
- Jump Point Search
And I decided to try to implement my A * on the voxel 3d grid.

Algorithm Description
A * step through all the paths leading from the initial vertex to the final, until it finds the minimum. Like all informed search algorithms, he first looks at those routes that “appear” to lead to the goal. It differs from the greedy algorithm, which is also the search algorithm for the first best match, when choosing a vertex it takes into account, among other things, the entire path traveled to it.
Visualization of work A * from Wikipedia Implementation
Since the algorithm needs "nodes" - points for defining the path, we write the class structure of the node:
Node code:public enum EMoveAction { walk, jump, fall, swim }; public class PathPoint {
Small class constructors: 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; }
Next, we need the structure of the path search settings:
Path Search Settings Code: public struct SPathFinderType {
Further, "World" is a class of a unique database for storing information about map blocks. You may have implemented differently.
The result of the pathfinding route retrieval function: public List<PathPoint> GetPathToTarget(Vector3 beginPoint, Vector3 targetPoint, World worldData, SPathFinderType pfType) { List<PathPoint> path = new List<PathPoint>();
Closepoint
The ClosePoint function depends entirely on the implementation of the World class; it adds all possible paths from it to the list of open points and removes the current point from this list (closes it). I will give an example of my "closing point" in the first four directions.
Attention big code jumble 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];
Optimization
By simply dividing the path from the start to the current point, we reduce the number of nodes many times and make it more "greedy".
return this.heuristicEstimatePathLenght + this.pathLenghtFromStart /2;
Results
Pros:
- Quick search in open spaces.
- Universality of the algorithm
Minuses:
- It requires a lot of memory to calculate the route.
Green is the open list of nodes, red is the way to the target, blue is closed nodes.
Received routes to optimization: Received routes after optimization: Literature
https://tproger.ru/articles/pathfindings/
https://ru.wikipedia.org/wiki/A*