A星寻路学习记录

//节点
public class Node {

//节点位置
public Vector3 position;
//在地图中的X轴位置下标
public int indexX;
//在地图中的Z轴位置下标
public int indexZ;
//该节点是否可以行走
public bool canWalk;

//行走代价
public float gCost;
//距离目标的代价
public float hCost;
public float fCost { get { return gCost + hCost; } }

//父节点 方便回溯路径
public Node parent;

public Node(Vector3 position, int indexX, int indexZ, bool canWalk)
{
    this.position = position;
    this.indexX = indexX;
    this.indexZ = indexZ;
    this.canWalk = canWalk;
}

}

public class Grid : MonoBehaviour {

//地图 储存所有的节点信息
public Node[,] maps;
//地图的长和宽
public float mapSizeX;
public float mapSizeZ;
//每一个节点的大小 
public float nodeSize;

//地图长宽各占几个节点
public int countX;
public int countZ;

//障碍物层
public LayerMask canNotWalk;

public static Grid instance;

public List<Node> path;

public void Awake()
{
    instance = this;
    Init();
}

//初始化 生成地图
public void Init()
{
    //地图大小除以节点大小 算出个数
    countX = Mathf.CeilToInt(mapSizeX / nodeSize);
    countZ = Mathf.CeilToInt(mapSizeZ / nodeSize);
    //初始化数组
    maps = new Node[countX, countZ];
    //地图赋值
    for (int x = 0; x < countX; x++)
    {
        for (int z = 0; z < countZ; z++)
        {
            float posX = (this.transform.position.x - mapSizeX / 2) + nodeSize *  (x  + 0.5f);
            float posY = this.transform.position.y;
            float posZ = (this.transform.position.z - mapSizeZ / 2) + nodeSize * (z + 0.5f);
            Vector3 nodePos = new Vector3(posX, posY, posZ);
            bool canWalk = CheckNodeCanWalk(nodePos);

            Node node = new Node(nodePos, x, z, canWalk);
            maps[x, z] = node;
        }
    }
}

//检查节点是否可以行走
public bool CheckNodeCanWalk(Vector3 nodePos)
{
    return !Physics.CheckSphere(nodePos, nodeSize / 2, canNotWalk);
}

public void OnDrawGizmos()
{
    if(maps != null)
    {
        //画出地图
        foreach (Node node in maps)
        {

                if( path.Contains(node))
                {
                    Gizmos.color = Color.black;
                    Gizmos.DrawCube(node.position, Vector3.one * nodeSize * 0.8f);
                }
                else
                {
                    if (node.canWalk == true)
                    {
                        Gizmos.color = Color.gray;
                        Gizmos.DrawCube(node.position, Vector3.one * nodeSize * 0.8f);
                    }
                    else
                    {
                        Gizmos.color = Color.red;
                        Gizmos.DrawCube(node.position, Vector3.one * nodeSize * 0.8f);
                    }
                }

        }
    }
}

//通过一个位置获取一个节点信息
public Node GetNodeFromVector3(Vector3 pos)
{
    Node result = maps[0,0];
    float mixDistance;

    mixDistance = Vector3.Distance(pos, maps[0, 0].position);

    for (int x = 0; x < countX; x++)
    {
        for (int z = 0; z < countZ; z++)
        {
            float distance = Vector3.Distance(pos, maps[x, z].position);
            if(distance < mixDistance)
            {
                mixDistance = distance;
                result = maps[x, z];
            }
        }
    }

    return result;
}


//通过一个节点获取周围8个节点的方法
public List<Node> GetNeighbourNode(Node node)
{
    List<Node> list = new List<Node>();

    for (int x = -1; x <= 1; x++)
    {
        for (int z = -1; z <= 1; z++)
        {
            if (x == 0 && z == 0) continue;

            int indexX = node.indexX + x;
            int indexZ = node.indexZ + z;

            if (indexX >= 0 && indexX < countX && indexZ >= 0 && indexZ < countZ)
                list.Add(maps[indexX, indexZ]);
        }
    }

    return list;
}

}

public class AIPath : MonoBehaviour {

//目标点
public GameObject target;
//存放路径
public List<Node> path = new List<Node>();


// Update is called once per frame
void Update () {

    FindingPath(this.transform.position, target.transform.position);

}

//寻路
void FindingPath(Vector3 startPos, Vector3 endPos)
{
    //查找开始点和终点所处于的节点
    Node startNode = Grid.instance.GetNodeFromVector3(startPos);
    Node endNode = Grid.instance.GetNodeFromVector3(endPos);

    //开关列表 开列表存放待搜索的所有节点 闭列表存放所有不需要计算的节点(例如 障碍物)
    List<Node> openList = new List<Node>();
    List<Node> closeList = new List<Node>();

    //初始点作为待搜索的第一个点  开始搜索
    openList.Add(startNode);

    while(openList.Count > 0)
    {
        //以待搜索的第一个为起始点 
        Node currentNode = openList[0];

        for (int i = 0; i < openList.Count; i++)
        {
                //判断代价F最小的点   或者  F相同判定H较小的点
            if (openList[i].fCost < currentNode.fCost ||
               (openList[i].fCost == currentNode.fCost && openList[i].hCost < currentNode.hCost))
            {
                //得出最优点
                currentNode = openList[i];
            }
        }

        //移除这个点
        openList.Remove(currentNode);
        //添加这个节点 不再计算
        closeList.Add(currentNode);

        //如果得出的最优点为终点 则算出了最终路径 结束
        if(currentNode == endNode)
        {
            //计算路径
            GetPath(startNode, endNode);
            return;
        }

        //判断邻居点 哪些符合条件需要添加到待搜索列表中
        foreach (Node node in Grid.instance.GetNeighbourNode(currentNode))
        {
            //不符合条件的节点
            if (node.canWalk == false || closeList.Contains(node)) { continue; }

            if(!openList.Contains(node))
            {
                //符合条件的节点需要计算
                node.gCost = GetDistance(node, currentNode);
                node.hCost = GetDistance(node, endNode);

                //设置父节点  方便回溯求路径
                node.parent = currentNode;
                openList.Add(node);
            }
        }
    }
}

//计算移动代价或者到达目标点的代价  (采用几何方式计算真实距离)
public float GetDistance(Node a,Node b)
{
    return Vector3.Distance(a.position, b.position);
}

//计算最终路径
public void GetPath(Node startNode,Node endNode)
{
    //清除当前路径
    path.Clear();

    Node temp = endNode;

    //开始回溯
    while(temp != startNode)
    {
        path.Add(temp);
        temp = temp.parent;
    }

    //反转 得出 由初始点到终点的路径
    path.Reverse(); 

    //传递路径
    Grid.instance.path = path;
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值