A*算法

看完视频后理解的A*算法
A *算法

Node 定义小方格

public class Node
{
public bool walkable;
public int x;
public int z;
public Vector3 pos; // 当前位置
public int hCost; // 从当前节点到目标节点的距离
public int gCost; // 从起始点到当前节点的距离
public Node parent;

public int fCost { get { return gCost + hCost; } }     //   最终的距离

public Node(int x,int z,bool walkable,Vector3 pos)
{
    this.x = x;
    this.z = z;
    this.walkable = walkable;
    this.pos = pos;
}

}

Grids 绘制地图

public class Grids : MonoBehaviour
{
public LayerMask unwalkbaleMask; // 定义障碍物
public Vector3 gridSize; // 当前网格尺寸
public float nodeRadius; // 节点的半径
float nodeDiameter; // 节点的直径
public int nodeNumX,nodeNumZ; // 定义每个节点的数量,分别是x和y的数量
public Node[,] grid; // 定义的节点,利用二维节点数组来定义网格
public List path; // 定义寻路路径列表,记录寻路的经过的节点
public Transform Player, Target;
Node playerNode, targetNode;

// Start is called before the first frame update
void Start()
{
    nodeDiameter = nodeRadius * 2;
    nodeNumX = Mathf.RoundToInt(gridSize.x / nodeDiameter);         // Mathf.RoundToInt()   整形化
    nodeNumZ = Mathf.RoundToInt(gridSize.z / nodeDiameter);
    CreateGrid();
}

public void CreateGrid()
{
    grid = new Node[nodeNumX,nodeNumZ];
    Vector3 startPos = transform.position - new Vector3(gridSize.x / 2, 0, gridSize.z / 2);         //地图左下角的位置坐标
    for (int x = 0; x < nodeNumX; x++)
    {
        for (int z = 0; z < nodeNumZ; z++)
        {
            Vector3 currentPos = startPos + new Vector3(x * nodeDiameter + nodeRadius, 0, z * nodeDiameter + nodeRadius);
            //  设置每个节点(小方格)的中心位置坐标
            bool walkable = !Physics.CheckSphere(currentPos, nodeRadius, unwalkbaleMask);
            //  在每个小方块位置处检查一个小圆球范围内是否有物体
            //  并且这个物体的层级是unwalkbaleMask  
            //  如果是将这个节点定义为不可移动点  返回false 如果不是返回true
            grid[x,z]=new Node(x,z,walkable,currentPos);
            //  给每个节点小方格设置数据
        }
    }
}
public Node GetNodeFromPosition(Vector3 pos)            //  该方法从某个位置获取节点
{
    float penrcentX = (pos.x + gridSize.x / 2) / gridSize.x;
    penrcentX = Mathf.Clamp01(penrcentX);                       //  使penrcentX范围在0-1之间
    float penrcentZ = (pos.z + gridSize.z / 2) / gridSize.z;
    penrcentZ = Mathf.Clamp01(penrcentZ);                       //  使penrcentX范围在0-1之间
    int x = Mathf.RoundToInt(penrcentX * (nodeNumX - 1));
    int z = Mathf.RoundToInt(penrcentZ * (nodeNumZ - 1));
    return grid[x, z];
}

private void OnDrawGizmos()         //画出方格
{
    Gizmos.color = Color.green;
    Gizmos.DrawCube(transform.position, new Vector3(gridSize.x,1, gridSize.z ));
    if (grid!=null)
    {
        foreach (Node n in grid)
        {
            if (n.walkable)
            {
                Gizmos.color = Color.gray;
                if (GetNodeFromPosition(Player.position)==n)
                {
                    Gizmos.color = Color.red;
                }
                if (GetNodeFromPosition(Target.position) == n)
                {
                    Gizmos.color = Color.white;
                }
                if (path != null && path.Contains(n))
                {
                    Gizmos.color = Color.blue;
                }
                    
                Gizmos.DrawCube(n.pos,new Vector3(nodeDiameter*0.9f,1.2f,nodeDiameter*0.9f));
            }
        }
    }
}

}

FindPath 使用A*算法寻路

public class PathFinding : MonoBehaviour
{
Grids myGrid;
Node startNode;
Node targetNode;
// Start is called before the first frame update
void Awake()
{
myGrid = GetComponent();
}

void FindPath(Vector3 startPos,Vector3 targetPos)
{
    startNode = myGrid.GetNodeFromPosition(startPos);       //  得到开始节点
    targetNode = myGrid.GetNodeFromPosition(targetPos);     //  得到结束节点
    List<Node> openSet = new List<Node>();                  //  开始节点列表
    HashSet<Node> closeSet = new HashSet<Node>();           //  关闭节点列表
    openSet.Add(startNode);                                 //  开始节点加入到openSet
    while (openSet.Count>0)                                 //  openSet中有节点开始循环
    {
        Node currentNode = openSet[0];                      //  当前节点
        for (int i = 0; i < openSet.Count; i++)             //  开始循环列表    
        {
            if (openSet[i].fCost<currentNode.fCost || (openSet[i].fCost==currentNode.fCost && openSet[i].hCost<currentNode.hCost))
            {
                currentNode = openSet[i];                   //  满足条件重新得到当前节点是openSet[i],
            }
            openSet.Remove(currentNode);                    //  在openSet中移除当前节点
            closeSet.Add(currentNode);                      //  在closeSet中加入当前节点
            if (currentNode==targetNode)                    //  到指定位置
            {
                print("path was found");
                RetrievePath(currentNode);                  //  调用 RetrievePath()方法
                return;
            }
            //print("Path");
            foreach (Node n in GetNeighbors(currentNode))           //  遍历周围一圈节点即遍历 neighbor 列表 调用GetNeighbors
            {                                                       //  GetNeighbors(currentNode)可以得到当前节点周围的邻居节点
                if (!n.walkable || closeSet.Contains(n))            //  如果该节点为不能走的节点或者在关闭列表中有该节点
                    continue;                                       //  结束本次循环,进行下一个节点的判断
                int newgCost = currentNode.gCost + GetDistance(currentNode, n);     //  当前节点和邻居节点之间的距离
                bool inOpenset = openSet.Contains(n);               //  如果打开列表中有该节点返回true
                if (newgCost<n.gCost || !inOpenset)                 //  如果newgCost小于邻居节点从起始点到当前节点的距离
                {                                                   //  或者该邻居节点不在打开节点列表中
                    n.gCost = newgCost;                             //  此时节点n的 gCost为newgCost
                    n.hCost = GetDistance(n, targetNode);           //  求出此时节点n到终点的距离
                    n.parent = currentNode;                         //  设置父节点
                    if (!inOpenset)                                 //  如果不在打开节点列表中
                    {
                       // Debug.Log("aa");
                        openSet.Add(n);                             //  把节点n添加到打开列表中
                    }
                }
                
            }
        }
    }
}

private void RetrievePath(Node n)           //  上方调用该函数时传递的参数为当前节点
{
    List<Node> p = new List<Node>();        //  新建列表 p,p是用来存储寻路的路径
    while (n!=startNode)                    //  如果当前节点不是开始位置节点
    {
        p.Add(n);                           //  列表p中添加节点n,n为当前节点
        n = n.parent;                       //  n重新赋值,赋值为它的父节点,直到n为开始位置节点
    }
    p.Reverse();                            //  因为赋值节点是从后往前,所以要反转顺序
    myGrid.path = p;                        //  把路径返回
}

int GetDistance(Node n1,Node n2)              //  求距离(pos1和pos2之间的距离)
{
    int distanceX = (int)Mathf.Abs(n1.x - n2.x);    
    int distanceZ = (int)Mathf.Abs(n1.z - n2.z);
    if (distanceX > distanceZ)                      //  计算距离的方式,水平垂直方向一格为10,斜方向为14  
    {
        return 14 * distanceZ + 10 * (distanceX - distanceZ);
    }
    else
    {
        return 14 * distanceX + 10 * (distanceZ - distanceX);
    }
}

private List<Node> GetNeighbors(Node n)                 //  获得周围一圈节点,上方调用该函数传递的参数为当前节点位置 
{                                                       //  n是当前位置传递进来的坐标
    List<Node> neighbors = new List<Node>();            //  新建列表 neighbor 用来存放当前节点周围一圈的节点
    int xx = n.x;                                       //  xx是当前位置的x轴上的值
    int zz = n.z;                                       //  zz是当前位置的z轴上的值
    Debug.Log(xx + "," + zz);
    Debug.Log("a="+n.pos);
    for (int x = -1; x <= 1; x++)                       //  获取周围一圈节点坐标的x
    {
        for (int z = -1; z <= 1; z++)                   //  获取周围一圈节点坐标的z
        {
            if (x == 0 && z == 0)                       //  如果x=0,z=0即位置没发生改变,得到的是自己当前坐标,不需要
                continue;
            if (xx+x >= 0 && xx+x < myGrid.nodeNumX && zz + z >= 0 && zz + z < myGrid.nodeNumZ)     //  获取周围一圈节点坐标
            {
                neighbors.Add(myGrid.grid[xx + x, zz + z]);     //  把每个邻居节点的信息存储到 neighbor 列表中
            }                                                   //  在Grids中从左下角第一个方格开始[x,z]是[0,0]
        }
    }
    return neighbors;               //  返回 neighbor 列表
}

// Update is called once per frame
void Update()
{
    FindPath(myGrid.Player.position, myGrid.Target.position);
}

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值