寻路算法 之 A*寻路

来源:互联网 发布:武汉java薪资待遇 编辑:程序博客网 时间:2024/05/17 06:04

A*寻路是游戏设计使用比较频繁的寻路算法,主要原理是将平面划分为多个网格,来进行目标查找。


图例说明:

目标:

                                                            

                                           


开始搜索:


启发式搜索:启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提到了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。
估价函数:从当前节点移动到目标节点的预估费用;这个估计就是启发式的。在寻路问题和迷宫问题中,我们通常用曼哈顿(manhattan)估价函数预估费用。
A*算法的特点:A*算法在理论上是时间最优的,但是也有缺点:它的空间增长是指数级别的。(优化:二叉堆)

在A*寻路算法中,我们通过从点A开始,检查相邻方格的方式,向外扩展直到找到目标。


开启列表:待检查方格的集合列表,寻找周围可以达到的点,加入到此表中,并保存中心点为父节点
关闭列表:列表中保存不需要再次检查的方格


                                               


路径评分:

G-与起始点的距离
H-与目标点的距离
F的值是G和H的和。F,G和H的评分被写在每个方格里。
正如在紧挨起始格右侧的方格所表示的,F被打印中间,G在左上角,H则在右上角。

选择路径中经过哪个方格的关键是 这个等式:F = G + H


                                                                                    


                                                           

开始搜索:


                                                          


1,把起始格添加到开启列表。     


                                                               
2,寻找起点周围所有可到达或者可通过的方格,把他们加入开启列表。


                                                             
3,从开启列表中删除点A,把它加     入到一个“关闭列表”,列表中保存所有不需要再次检查的方格。

                                                              
4,把当前格子从开启列表中删除,然后添 加到关闭列表中。


                                                            
5,检查所有相邻格子。跳过那些已经在关  闭列表中的或者不可通过的,把他们添加进开启列表,把选中的方格作为新的方格的父节点。

6,如果某个相邻格已经在开启列表里了,检查现在的这条路径G值是否会更低一些。如果新的G值更低,那就把相邻方格的父节点改为目前选中的方格,重新计算F和G的值。


起始格下方格子的父节点已经和前面不同的。之前它的G值是,并且指向右上方的格子。现在它的G值是,指向它上方的格子。这在寻路过程中的某处发生,当应用新路径时,G值经过检查变得低了-于是父节点被重新指定,G和F值被重新计算。


步骤总结:

1,把起始格添加到开启列表。
2,重复如下的工作:
a) 寻找开启列表中F值最低的格子。我们称它为当前格。
b) 把它切换到关闭列表。
c) 对相邻的格中的每一个
* 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
* 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录
这一格的F,G,和H值。
* 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值
意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的
G和F值。
d) 停止,当你
* 把目标格添加进了关闭列表,这时候路径被找到
* 没有找到目标格,开启列表已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。

算法实现:

开启集合
关闭集合
添加起始点到开始集合中
循环如下步骤:
当前点=开启集合中最小F_Cost的点
将当前点移出开启集合中
将当前点添加到关闭集合中
如果当前点是目标点,结束查询
遍历当前点的每个相邻点
如果相邻点不能访问或者相邻点在关闭集合中,跳过此相邻点
如果新路径到相邻点的距离更短,或者相邻点不在开启集合中
重新设置F_Cost
重新设置其父节点为当前点
如果相邻点不在开启集合中
添加相邻点到开启集合中


具体代码:


Node 类:

using UnityEngine;using System.Collections;public class Node {    //能否行走    public bool _canWalk;    //世界坐标    public Vector3 _worldPos;    //位置    public int _gridX, _gridY;    //与目标节点和开始节点的距离    public int gCost;    public int hCost;//总距离    public int fCost    {        get { return gCost + hCost; }    }    //父节点    public Node parent;    //构造函数    public Node(bool canwalk,Vector3 position,int x,int y)    {        _canWalk = canwalk;        _worldPos = position;        _gridX = x;        _gridY = y;    }}

Grid类:

using UnityEngine;using System.Collections;using System.Collections.Generic;public class Grid : MonoBehaviour{    //节点    private Node[,] grid;    public Vector2 gridSize;    public float nodeRadius;    private float nodeDiameter;    public LayerMask whatLayer;    //玩家    public Transform player;    public List<Node> path = new List<Node>();    public int gridCntX, gridCntY;    // Use this for initialization    void Start()    {        //初始化数据        nodeDiameter = nodeRadius * 2;        gridCntX = Mathf.RoundToInt(gridSize.x / nodeDiameter);        gridCntY = Mathf.RoundToInt(gridSize.y / nodeDiameter);        grid = new Node[gridCntX, gridCntY];        CreateGrid();    }    //创建grid     private void CreateGrid()    {        Vector3 startPoint = transform.position - gridSize.x / 2 * Vector3.right - Vector3            .forward * gridSize.y / 2;        for (int i = 0; i < gridCntX; i++)            for (int j = 0; j < gridCntY; j++)            {                Vector3 worldPoint = startPoint + Vector3.right * (i * nodeDiameter + nodeRadius) + Vector3.forward * (j * nodeDiameter + nodeRadius);                bool walkable = !Physics.CheckSphere(worldPoint, nodeRadius, whatLayer);                grid[i, j] = new Node(walkable, worldPoint, i, j);            }        Vector3 endPos = transform.position - gridSize.x / 2 * Vector3.right;    }    // Update is called once per frame    void Update()    {    }    /*          根据位置获取node节点      */    public Node getFromPosition(Vector3 postition)    {        float percentX = (postition.x + gridSize.x / 2) / gridSize.x;        float percentY = (postition.z + gridSize.y / 2) / gridSize.y;        //确保在0和1之间        percentX = Mathf.Clamp01(percentX);        percentY = Mathf.Clamp01(percentY);        int x = Mathf.RoundToInt((gridCntX - 1) * percentX);        int y = Mathf.RoundToInt((gridCntY - 1) * percentY);        return grid[x, y];    }    //调用函数,进行初始化    void OnDrawGizmos()    {        Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, 1, gridSize.y));        if (grid == null)            return;        Node playerNode = getFromPosition(player.position);        //遍历        foreach (var node in grid)        {            Gizmos.color = node._canWalk ? Color.white : Color.red;            Gizmos.DrawCube(node._worldPos, Vector3.one * (nodeDiameter - .1f));        }        //遍历路径        if (path != null)        {            foreach (var node in path)            {                Gizmos.color = Color.black;                Gizmos.DrawCube(node._worldPos, Vector3.one * (nodeDiameter - .1f));            }        }        //绘制玩家颜色        if (playerNode != null && playerNode._canWalk)        {            Gizmos.color = Color.cyan;            Gizmos.DrawCube(playerNode._worldPos, Vector3.one * (nodeDiameter - .1f));        }    }    /*     *         获取节点周围的八个节点          */    public List<Node> getNeibourhood(Node node)    {        List<Node> neibourhood = new List<Node>();        for (int i = -1; i <= 1; i++)        {            for (int j = -1; j <= 1; j++)            {                if (i == 0 && j == 0)                    continue;                int tempX = node._gridX + i;                int tempY = node._gridY + j;                if (tempX < gridCntX && tempX > 0 && tempY > 0 && tempY < gridCntY)                {                    neibourhood.Add(grid[tempX, tempY]);                }            }        }        return neibourhood;    }}

FindPath 类,也是核心类:


using UnityEngine;using System.Collections;using System.Collections.Generic;public class FindPath : MonoBehaviour{    //添加玩家和目标点    public Transform player, endPoint;    private Grid _grid;    // Use this for initialization   //初始化grid    void Start()    {        _grid = GetComponent<Grid>();    }    // Update is called once per frame    //调用获取路径的函数    void Update()    {        findingPath(player.position, endPoint.position);    }    //发现路径    void findingPath(Vector3 startPos, Vector3 endPos)    {        //开始节点和结束节点        Node startNode = _grid.getFromPosition(startPos);        Node endNode = _grid.getFromPosition(endPos);        //开启列表和关闭列表        List<Node> openSet = new List<Node>();        HashSet<Node> closeSet = new HashSet<Node>();        //添加到开启列表        openSet.Add(startNode);        while (openSet.Count > 0)        {            //获取第一个几点            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.Remove(currentNode);           //添加到关闭列表            closeSet.Add(currentNode);            //如果当前节点是目标节点,完成查找            if (currentNode == endNode)            {                //获取路径                generatePath(startNode,endNode);                return;            }            //根据当前节点判断周围的节点            foreach (var node in _grid.getNeibourhood(currentNode))            {                if (!node._canWalk || closeSet.Contains(node))                    continue;                int newCost = currentNode.gCost + getDistanceNodes(currentNode, node);                //判断fCost,并更新fCost                if (newCost < node.gCost || !openSet.Contains(node))                {                    node.gCost = newCost;                    node.hCost = getDistanceNodes(node, endNode);                    node.parent = currentNode;                    if (!openSet.Contains(node))                    {                        openSet.Add(node);                    }                }            }        }    }    //生成路径    private void generatePath(Node startNode, Node endNode)    {        List<Node> path = new List<Node>();        Node temp = endNode;        while (temp != startNode)        {            path.Add(temp);            temp = temp.parent;        }        //翻转        path.Reverse();        _grid.path=path;    }    //获取距离    int getDistanceNodes(Node a, Node b)    {        int cntX = Mathf.Abs(a._gridX - b._gridX);        int cntY = Mathf.Abs(a._gridY - b._gridY);        //判断横优先还是竖优先        if (cntX > cntY)        {            return 14 * cntY + 10 * (cntX - cntY);        }        else        {            return 14 * cntX + 10 * (cntY - cntX);        }    }}


资源下载地址:http://download.csdn.net/detail/u012251421/8341159




0 0
原创粉丝点击