AStar (A*) algorithm core idea (for unity)

AStar Algorithm

A* algorithm, A* (A-Star) algorithm is the most effective direct search method for solving the shortest path in a static road network, and it is also an effective algorithm for solving many search problems. The closer the distance estimate in the algorithm is to the actual value, the faster the final search.

Note: The AStar class should be used as a singleton class that only provides calling methods, and the initialization of nodes should be done in the node class


Algorithm ideas:
1. Create two lists for maintaining nodes, openList and closeList.
openList is used to store all saved but unexamined nodes.
closeList is used to store visited nodes
. 2. As long as there are unexamined nodes in openList, the Take a node with the lowest cost as the current node.
3. If the current node is the target node, it means that a suitable path has been found. If not, start why the node.
4. Re-maintain the cost of the nodes not in the closeList list, and then put them into the openList.
5 continues the process of 2->4.


举例理解

First we need to knowValuation function: F(N)=G(N) + H(N)The AStar algorithm will constantly maintain the cost of nodes to obtain a path with the least cost.
Among them: G is the path frominitial statearrivestate nThe minimum cost
H is fromstate narrivetarget stateThe minimum estimated cost of

Take a square node as an example:
for a unit square, the side length is 1, and the diagonal cost is √2 ≈ 1.1414.
However, in order to calculate the cost × 10, we stipulate that the cost required to move in the horizontal and vertical directions is 10, and the diagonal The cost required to move in the direction is 14. square node cost
I will label the cost of the square node in the following format.
Please add a picture description
So what happens if you use the A* algorithm to find the path from (0,0,) to (2,2)?
The first search:
At the beginning, there is only one node (0,0) in the openList, so directly find the neighbors of (0,0), the red part is the neighbor node of (0,0) (a square should have 8 neighbor nodes, But it can also be stipulated that the nodes in the diagonal direction are not neighbor nodes, but this is generally not done.)
Please add a picture description
These three neighbor nodes do not exist in the closeList at the beginning, so put these three neighbor nodes into the openList (When saving a node, the cost of the stored node will not be considered. Any node with a high cost at the moment may become the smallest node in the openList after several searches, so that our algorithm can "repent" the path it has traveled )

The second search:
At this time, there are three nodes in the openList, and it can also be seen that the cost of (1,1) is the smallest, so we take out the (1,1) node and the
neighbor nodes of (1,1) are: ( 0,0), (1,0), (2,0), (0,1), (2,1),(0,2), (1,2), (2,2)
The (0,0) node is already in the closeList, (1,0) and (0,1) are in the openList, so none of these three nodes are recalculated (no regressing, and no intentional detours)
Please add a picture description
In fact, when searching for this step, the result will be returned directly when the (2,2) node is found, we can also see that throughThe node cost found by the A* algorithm is the smallest, but the path is not necessarily unique(If in the above square nodes nodes in the diagonal direction are not considered neighbors, then there can be two paths leading from (0,0) to (2,2))


Computing F in a square node will 曼哈顿方法cost
Vector2 dist =new Vector2Int(Mathf.Abs((int)pos.x - (int)other.pos.x), Mathf.Abs((int)pos.x- ( int) other.pos.y));
int lowest =Mathf.Min(dist.x, dist.y);
int highest =Mathf.Max(dist.x, dist.y);
int horizontalMoveRequired =highest - lowest;
return lowest *14 + horizontalMoveRequired *10;

It means that for a right-angled triangle path, we can follow the diagonal line of an isosceles right-angled triangle with a shorter right-angled side first, and then walk the distance between the two right-angled sides.
Please add a picture description


core code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class AStar{
    
    
    public static List<NodeBase> FindPath(NodeBase startNode,NodeBase endNode){
    
    
        //打开列表和关闭列表
        List<NodeBase> _openList = new List<NodeBase>(){
    
     startNode}; //起始需要在打开列表中填入开始节点
        List<NodeBase> _closeList =new List<NodeBase>();

        //初始将所有节点的G代价调为最大,否侧起始节点到终点的花费将会因为是0而无法计算。
        foreach(var node in GridManger.Instance.Tiles.Values) node.G = int.MaxValue;

        startNode.G = 0;
        startNode.H = startNode.GetDistance(endNode.coords);
        
        while(_openList.Count > 0){
    
    
            NodeBase currentNodeBase = GetLowestFCostNode(_openList);
            
            if(currentNodeBase == endNode) return CalculatePath(endNode);
            
            _openList.Remove(currentNodeBase);
            _closeList.Add(currentNodeBase);
            
            //遍历邻居
            foreach(NodeBase n in currentNodeBase.Neighbours){
    
    
                //如果这个节点已经存在于关闭列表或者打开列表中就直接跳过
                if(_closeList.Contains(n) || _openList.Contains(n)) continue;
                //暂时的G代价
                var tantativeGCost = currentNodeBase.G + currentNodeBase.GetDistance(n.coords); 
                if(tantativeGCost < n.G){
    
    
                    n.father =currentNodeBase;
                    n.G= tantativeGCost;
                    n.H= n.GetDistance(endNode.coords);
                    _openList.Add(n);
                }
            }
        }
        return null; //没有路径
    }

    //得到Fcost最低的一个点
    private static NodeBase GetLowestFCostNode(List<NodeBase> nodes){
    
    
        NodeBase lowestFcostNode = nodes[0];
        foreach(Node n  in nodes){
    
    
            if(n.F < lowestFcostNode.F) lowestFcostNode = n;
        }
        return lowestFcostNode;
    }

    //计算结果路径路径
    private static List<NodeBase> CalculatePath(NodeBase node){
    
    
        List<NodeBase> nodeBases = new List<NodeBase>();
        while(node != null){
    
    
            nodeBases.Add(node);
            node=node.father;
        }
        nodeBases.Reverse();
        return nodeBases;
    }
}

Guess you like

Origin blog.csdn.net/blastospore/article/details/128707522