寻路算法 之 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
- 寻路算法 之 A*寻路
- 寻路算法之A*
- JS算法之A*(A星)寻路算法
- A*算法寻路之Ogre实践
- A*算法寻路之Ogre实践
- A*算法寻路之Ogre实践
- 人工智能 之 A* 寻路算法剖析
- A*算法程序之二------A*寻路初探
- A*寻路算法
- A* 寻路算法
- A* 寻路算法
- A*寻路算法
- A*寻路算法
- A*寻路算法
- A*寻路算法
- A* 寻路算法
- A*寻路算法
- A* 寻路算法
- 2015年第一天,参加上海东方明珠迎新跑
- mount --bind 的妙用
- PostgreSQL简介
- Android UI 详解之Bitmap
- C++通过jni调用java静态方法和非静态方法
- 寻路算法 之 A*寻路
- 深入分析 Linux 内核链表
- C#1-100不重复的随机数
- collect2: error: ld returned 1 exit status
- Java-Excel Sheet Column Title
- 学习记录
- Windows 编程基础知识
- intent传值方式
- 【数据结构】排序算法总结