• A* 寻路学习


    启发式搜索:启发式搜索就是在状态空间中的搜索.对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标.这样可以省略大量无谓的搜索路径,提高了效率.在启发式搜索中,对位置的估价是十分重要的,采用了不同的估价可以有不同的效果

    在启发式搜索中,对位置的估价是十分重要的.采用了不同的估价可以有不同的效果

    估价函数:从当前节点移动到目标节点的预估费用:这个估计就是启发式的.在寻路问题和迷宫问题中,我们通常用曼哈顿(manhattan)估价函数预计费用

    A*算法的特点:A*算法在理论伤是时间最优的,但是也有缺点:它的空间增长是指数级别的.(优化:二叉堆)

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

    关闭列表:列表中保存不需要再次检查的方格

    从起始节点到当前节点的距离

    从当前节点到目标节点的估计距离

    F = G + H

    F,G和H的评分被写在每个方格里.正如在紧挨起始格右侧的方格所表示的,F被打印中间,G在左上角,H则在右上角

     搜索过程:

    1:把起始格添加到开启列表(openlist)

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

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

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

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

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

    步骤总结:

    1:把起始格添加到开启列表.

    2:重复如下的工作:

      a) 寻找开启列表中F值最低的格子.我们称它为当前格.

      b) 把它切换到关闭列表.

      c) 对相邻的格中的每一个

        * 如果它不可通过或者已经在关闭列表中,略过它.反之如下.

          * 如果它不在开启列表中,把它添加进去.把当前格作为这一格的父节点.记录从起始点经过当前格的这一格的G,H,和F值.

          * 如果它已经在开启列表中,用G值为参考检查新的路径是否更好.更低的G值意味着更好的路径.

           如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值.

      d) 停止,当你

        * 把目标格添加进了关闭列表,这时候路径被找到

        * 没有找到目标格,开启列表已经空了.这时候,路径不存在.

    3.保存路径.从目标格开始,沿着每一格的父节点移动直到回到起始格.

    算法实现:

    开启集合(openlist)

    关闭集合(closedlist)

    添加起始点到开始集合中

    循环如下步骤:

      当前点=开启集合中最小F_Cost的点

      将当前点移出开启集合中

      将当前点添加到关闭集合中

      如果当前点是目标点,结束查询

      遍历当前点的每个相邻点

        如果相邻点不能访问或者相邻点在关闭集合中,跳过此相邻点

        如果新路径到相邻点的距离更短,或者相邻点不在开启集合中

          重新设置F_Cost

          重新设置其父节点为当前点

        如果相邻点不在开启集合中

          添加相邻点到开启集合中

     1 /*
     2 脚本名称:
     3 脚本作者:
     4 建立时间:
     5 脚本功能:
     6 版本号:
     7 */
     8 using UnityEngine;
     9 using System.Collections;
    10 
    11 namespace VoidGame {
    12 
    13     /// <summary>
    14     /// 节点,寻路的最小单位
    15     /// </summary>
    16     public class Node {
    17         /// <summary>
    18         /// 节点是否可以通过
    19         /// </summary>
    20         public bool m_canWalk;
    21         /// <summary>
    22         /// 节点的位置
    23         /// </summary>
    24         public Vector3 m_position;
    25         /// <summary>
    26         /// 节点在网格行(x轴)中的位置
    27         /// </summary>
    28         public int m_gridX;
    29         /// <summary>
    30         /// 节点在网格列(z轴)中的位置
    31         /// </summary>
    32         public int m_gridY;
    33 
    34         /// <summary>
    35         /// 从起始点当前节点的距离
    36         /// </summary>
    37         public int m_gCost;
    38         /// <summary>
    39         /// 从当前节点到目标节点的预估距离
    40         /// </summary>
    41         public int m_hCost;
    42         /// <summary>
    43         /// f = g + h
    44         /// </summary>
    45         public int m_fCost {
    46             get {
    47                 return m_gCost + m_hCost;
    48             }
    49         }
    50 
    51         /// <summary>
    52         /// 父节点
    53         /// </summary>
    54         public Node m_nodeParent;
    55 
    56 
    57         public Node(bool canWalk,Vector3 position,int x,int y) {
    58             m_canWalk = canWalk;
    59             m_position = position;
    60             m_gridX = x;
    61             m_gridY = y;
    62         }
    63 
    64     }
    65 }
    Node
      1 /*
      2 脚本名称:
      3 脚本作者:
      4 建立时间:
      5 脚本功能:
      6 版本号:
      7 */
      8 using UnityEngine;
      9 using System.Collections;
     10 using System.Collections.Generic;
     11 
     12 namespace VoidGame {
     13     /// <summary>
     14     /// 节点组成的网格
     15     /// </summary>
     16     public class Grid : MonoBehaviour {
     17         /// <summary>
     18         /// 节点网格数组
     19         /// </summary>
     20         private Node[,] m_nodeGrid;
     21         /// <summary>
     22         /// 整个网格的大小
     23         /// </summary>
     24         public Vector2 m_gridSize;
     25         /// <summary>
     26         /// 网格行(x轴)包含的节点数量
     27         /// </summary>
     28         public int m_gridNodeCountX;
     29         /// <summary>
     30         /// 网格列(z轴)包含的节点数量
     31         /// </summary>
     32         public int m_gridNodeCountY;
     33         /// <summary>
     34         /// 节点半径
     35         /// </summary>
     36         public float m_nodeRadius;
     37         /// <summary>
     38         /// 节点直径
     39         /// </summary>
     40         private float m_nodeDiameter;
     41 
     42         /// <summary>
     43         /// 节点不可走的层
     44         /// </summary>
     45         public LayerMask m_unWalkableLayer;
     46 
     47         /// <summary>
     48         /// 玩家
     49         /// </summary>
     50         public Transform m_startTransform;
     51 
     52         /// <summary>
     53         /// 从起点到终点的路径节点列表
     54         /// </summary>
     55         public List<Node> m_pathNodeList = new List<Node>();
     56 
     57 
     58         private void Start() {
     59             //计算节点直径
     60             m_nodeDiameter = m_nodeRadius * 2;
     61             //计算网格行(x轴)中的节点数量
     62             m_gridNodeCountX = Mathf.RoundToInt(m_gridSize.x / m_nodeDiameter);
     63             //计算网格列(z轴)中的节点数量
     64             m_gridNodeCountY = Mathf.RoundToInt(m_gridSize.y / m_nodeDiameter);
     65             //设置节点网格数组的大小
     66             m_nodeGrid = new Node[m_gridNodeCountX,m_gridNodeCountY];
     67 
     68             CreateNodeGrid();
     69         }
     70 
     71         private void Update() {
     72 
     73         }
     74 
     75         private void OnDrawGizmos() {
     76             
     77             //画节点网格包围框
     78             Gizmos.DrawWireCube(transform.position,new Vector3(m_gridSize.x,1,m_gridSize.y));
     79             //节点网格为空,直接返回
     80             if(m_nodeGrid == null) {
     81                 return;
     82             }
     83 
     84             //遍历节点网格数组
     85             foreach(var node in m_nodeGrid) {
     86                 //可以行走的网格颜色为白色,不可行走的网格颜色为红色
     87                 Gizmos.color = node.m_canWalk == true ? Color.white : Color.red;
     88                 //Gizmos视图中用比节点稍小的cube表示一个节点,便于观察
     89                 Gizmos.DrawCube(node.m_position,Vector3.one * (m_nodeDiameter - 0.1f));
     90             }
     91 
     92             //获取起始节点
     93             Node m_startNode = GetNodeFromPosition(m_startTransform.position);
     94 
     95             //如果路径列表不为空
     96             if(m_pathNodeList != null) {
     97                 //画出路线
     98                 foreach(var node in m_pathNodeList) {
     99                     Gizmos.color = Color.black;
    100                     Gizmos.DrawCube(node.m_position,Vector3.one * (m_nodeDiameter - 0.1f));
    101                 }
    102             }
    103 
    104             //如果起始节点不为空并且起始节点可以走
    105             if(m_startNode != null && m_startNode.m_canWalk) {
    106                 Gizmos.color = Color.cyan;
    107                 Gizmos.DrawCube(m_startNode.m_position,Vector3.one * (m_nodeDiameter - 0.1f));
    108             }
    109         }
    110 
    111         /// <summary>
    112         /// 创建节点网格
    113         /// </summary>
    114         private void CreateNodeGrid() {
    115             //获取起始点,为网格的左下
    116             Vector3 startPosition = new Vector3(transform.position.x - m_gridSize.x / 2,0,transform.position.y - m_gridSize.y / 2);
    117             //从左到右,从下到上生成节点
    118             for(int i = 0;i < m_gridNodeCountY;i++) {
    119                 for(int j = 0;j < m_gridNodeCountX;j++) {
    120                     Vector3 nodePosition = new Vector3(startPosition.x + i * m_nodeDiameter + m_nodeRadius,0,startPosition.z + j * m_nodeDiameter + m_nodeRadius);
    121                     //检测以节点半径为半径的球形范围内是否有属于m_unWalkableLayer的物体
    122                     bool canWalk = !Physics.CheckSphere(nodePosition,m_nodeRadius,m_unWalkableLayer);
    123                     //根据检测的结果节点是否为可走
    124                     m_nodeGrid[i,j] = new Node(canWalk,nodePosition,i,j);
    125                 }
    126             }
    127         }
    128 
    129         /// <summary>
    130         /// 根据所在位置获得位置所在的节点
    131         /// </summary>
    132         /// <param name="position">所在位置</param>
    133         /// <returns></returns>
    134         public Node GetNodeFromPosition(Vector3 position) {
    135             //获取位置所在网格行(x轴)上的比例
    136             float percentX = (position.x + m_gridSize.x / 2) / m_gridSize.x;
    137             //获取位置所在网格列(z轴)上的比例
    138             float percentY = (position.z + m_gridSize.y / 2) / m_gridSize.y;
    139             //确保行百分比和列百分比在0-1之间
    140             percentX = Mathf.Clamp01(percentX);
    141             percentY = Mathf.Clamp01(percentY);
    142 
    143             //四舍五入网格行上的比例,获得在网格行中的节点位置
    144             int x = Mathf.RoundToInt((m_gridNodeCountX - 1) * percentX);
    145             //四舍五入网格列上的比例,获得在网格列中的节点位置
    146             int y = Mathf.RoundToInt((m_gridNodeCountY - 1) * percentY);
    147             
    148             //通过行位置和列位置确定要返回的节点
    149             return m_nodeGrid[x,y];
    150         }
    151 
    152         /// <summary>
    153         /// 获取节点的邻居
    154         /// </summary>
    155         /// <param name="node">节点</param>
    156         /// <returns></returns>
    157         public List<Node> GetNeighbors(Node node) {
    158             List<Node> neighbourNodeList = new List<Node>();
    159             for(int i = -1;i <= 1;i++) {
    160                 for(int j = -1;j <= 1;j++) {
    161                     //i==0&&j==0 表示是自身,不添加
    162                     if(i == 0 && j == 0) {
    163                         continue;
    164                     }
    165                     //从左到右
    166                     int tempX = node.m_gridX + j;
    167                     //从下到上
    168                     int tempY = node.m_gridY + i;
    169                     //限制x,0 < x < m_gridNodeCountX
    170                     //限制y,0 < y < m_gridNodeCountY
    171                     //满足以上条件,将节点周围的8个节点添加到节点的邻居节点列表
    172                     if(tempX > 0 && tempX < m_gridNodeCountX && tempY > 0 && tempY < m_gridNodeCountY) {
    173                         neighbourNodeList.Add(m_nodeGrid[tempX,tempY]);
    174                     }
    175                 }
    176             }
    177 
    178             return neighbourNodeList;
    179         }
    180     }
    181 }
    Grid
      1 /*
      2 脚本名称:
      3 脚本作者:
      4 建立时间:
      5 脚本功能:
      6 版本号:
      7 */
      8 using UnityEngine;
      9 using System.Collections;
     10 using System.Collections.Generic;
     11 
     12 namespace VoidGame {
     13 
     14     public class FindPath : MonoBehaviour {
     15         /// <summary>
     16         /// 起点
     17         /// </summary>
     18         public Transform m_start;
     19         /// <summary>
     20         /// 目标
     21         /// </summary>
     22         public Transform m_target;
     23 
     24         /// <summary>
     25         /// 要寻路的网格
     26         /// </summary>
     27         private Grid m_grid;
     28 
     29         private void Start() {
     30             m_grid = GetComponent<Grid>();
     31         }
     32 
     33         private void Update() {
     34             FindingPath(m_start.position,m_target.position);
     35         }
     36 
     37         /// <summary>
     38         /// 寻路
     39         /// </summary>
     40         /// <param name="startPosition">起始位置</param>
     41         /// <param name="endPosition">结束位置</param>
     42         private void FindingPath(Vector3 startPosition,Vector3 endPosition) {
     43             //起始节点
     44             Node startNode = m_grid.GetNodeFromPosition(startPosition);
     45             //结束节点
     46             Node endNode = m_grid.GetNodeFromPosition(endPosition);
     47 
     48             //待检查的节点列表
     49             List<Node> openlist = new List<Node>();
     50             //已检查的节点列表
     51             HashSet<Node> closeList = new HashSet<Node>();
     52 
     53             //添加起始节点到待检查的列表
     54             openlist.Add(startNode);
     55 
     56             //如果待检查的列表的数量大于0
     57             while(openlist.Count > 0) {
     58                 //设置当前节点为待检查节点列表的第一个节点
     59                 Node currentNode = openlist[0];
     60 
     61                 //遍历待检查的列表
     62                 for(int i = 0;i < openlist.Count;i++) {
     63                     //如果 待检查节点的f小于当前节点的f 或者
     64                     //     待检查节点的f等于当前节点的f并且待检查节点的h小于当前节点的h
     65                     //将当前节点设置为待检查节点
     66                     if(openlist[i].m_fCost < currentNode.m_fCost ||
     67                         openlist[i].m_fCost == currentNode.m_fCost && openlist[i].m_hCost < currentNode.m_hCost) {
     68                         currentNode = openlist[i];
     69                     }
     70                 }
     71 
     72                 //从待检查列表中移除当前节点
     73                 openlist.Remove(currentNode);
     74                 //添加当前节点到已检查节点列表
     75                 closeList.Add(currentNode);
     76 
     77                 //如果当前节点等于终端节点,生成从当前节点到终端节点的路径
     78                 if(currentNode == endNode) {
     79                     GeneratePath(startNode,endNode);
     80                 }
     81 
     82                 //遍历当前节点的邻居节点
     83                 foreach(var node in m_grid.GetNeighbors(currentNode)) {
     84                     //如果邻居节点不能走或者待检查列表里包含邻居节点,不处理
     85                     if(!node.m_canWalk || closeList.Contains(node)) {
     86                         continue;
     87                     }
     88 
     89                     //获得新的g.为当前节点的g + 当前节点到邻居节点的距离
     90                     int newgCost = currentNode.m_gCost + GetDistanceBetweenNodes(currentNode,node);
     91 
     92                     //如果新g 小于 邻居节点的g 或者 待检查列表不包括邻居节点
     93                     if(newgCost < node.m_gCost || !openlist.Contains(node)) {
     94                         //邻居节点的g等于新的g
     95                         node.m_gCost = newgCost;
     96                         //邻居节点的h等于邻居节点到终端节点的距离
     97                         node.m_hCost = GetDistanceBetweenNodes(node,endNode);
     98                         //设置邻居节点的父节点为当前节点
     99                         node.m_nodeParent = currentNode;
    100                         //如果待检查节点列表不包括当前节点
    101                         if(!openlist.Contains(currentNode)) {
    102                             //添加邻居节点到待检查节点列表
    103                             openlist.Add(node);
    104                         }
    105                     }
    106                 }
    107 
    108             }
    109         }
    110 
    111         /// <summary>
    112         /// 获取两个节点之间的距离
    113         /// </summary>
    114         /// <param name="nodeA">节点a</param>
    115         /// <param name="nodeB">节点b</param>
    116         /// <returns></returns>
    117         private int GetDistanceBetweenNodes(Node nodeA,Node nodeB) {
    118             //获取节点a和节点b之间在网格行(x轴)上的间隔的节点数量
    119             int countX = Mathf.Abs(nodeA.m_gridX - nodeB.m_gridX);
    120             //获取节点a和节点b之间在网格列(z轴)上的间隔的节点数量
    121             int countY = Mathf.Abs(nodeA.m_gridY - nodeB.m_gridY);
    122 
    123             //如果网格行上的间隔节点数量大于网格列上的间隔节点数量
    124             if(countX > countY) {
    125                 return 14 * countY + 10 * (countX - countY);
    126             //否则
    127             } else {
    128                 return 14 * countX + 10 * (countY - countX);
    129             }
    130         }
    131 
    132         /// <summary>
    133         /// 生成最终路径
    134         /// </summary>
    135         /// <param name="startNode">起始节点</param>
    136         /// <param name="endNode">终端节点</param>
    137         private void GeneratePath(Node startNode,Node endNode) {
    138             List<Node> tempPath = new List<Node>();
    139             Node tempNode = endNode;
    140 
    141             //从终端节点回溯直到起始节点
    142             while(tempNode != startNode) {
    143                 tempPath.Add(tempNode);
    144                 tempNode = tempNode.m_nodeParent;
    145             }
    146 
    147             //倒转从终端节点到开始节点的列表.获得从开始节点到终端节点的列表
    148             tempPath.Reverse();
    149 
    150             m_grid.m_pathNodeList = tempPath;
    151         }
    152     }
    153 }
    FindPath

    项目:https://pan.baidu.com/s/1mi2TSxU

  • 相关阅读:
    软考下午题具体解释---数据流图设计
    透视表提取不反复记录(4)-每一个物品的最大值
    运用python抓取博客园首页的所有数据,而且定时持续抓取新公布的内容存入mongodb中
    [经验总结]material design效果与开发总结
    cocos2d-x 3.0 回调函数
    P1334 瑞瑞的木板
    P2776 [SDOI2007]小组队列
    P1886 滑动窗口
    P1160 队列安排
    P1823 音乐会的等待
  • 原文地址:https://www.cnblogs.com/revoid/p/6385708.html
Copyright © 2020-2023  润新知