理解A*寻路算法具体过程
2016-05-20 18:53
405 查看
A*寻路
创建网格节点类
using UnityEngine; using System.Collections; public class Node { public float radium; // 半径 public int x, y; // 下标 public float gCost = 0, hCost = 0, fCost = 0; // 估价值 public Node parent; // 父节点 public bool canWalk;// 能否行走,是否有障碍物 public Vector3 worldPos;// 世界坐标 public Node(int x, int y, bool canWalk, Vector3 worldPos) {// 初始化变量 this.x = x; this.y = y; this.canWalk = canWalk; this.worldPos = worldPos; } }
创建网格
using UnityEngine; using System.Collections; using System.Collections.Generic; public class Grid : MonoBehaviour { public float gridSize;// 网格大小 public float nodeRadius;// 每一个小格子的半径 float nodeDia;// 小格子的直径 int countX, countY;// 水平格子数量和垂直格子数量 Node[,] grid;// 网格 public LayerMask obsLayer;// 障碍物所在层 public List<Node> paths;// 最终可行路径 public void Start() {// 初始化变量 nodeDia = nodeRadius * 2; countX = Mathf.CeilToInt(gridSize / nodeDia); countY = countX; grid = new Node[countX, countY]; initGrid(); } /// <summary> /// 初始化网格矩阵 /// </summary> private void initGrid() { Vector3 startPos = transform.position - new Vector3(gridSize / 2, 0, gridSize / 2); for (int i = 0; i < countX; i++) { for (int j = 0; j < countY; j++) { // 获取小方格的世界坐标点 Vector3 pos = startPos + new Vector3(i * nodeDia + nodeRadius, 0, j * nodeDia + nodeRadius); // 判断小方格周围是否有障碍物 bool canWalk = !Physics.CheckSphere(pos, nodeRadius, obsLayer); Node node = new Node(i, j, canWalk, pos); grid[i, j] = node; } } } public void OnDrawGizmos() { //Gizmos.DrawCube(transform.position, new Vector3(gridSize, 1, gridSize)); // 绘制网格矩阵 if (grid != null) { for (int i = 0; i < countX; i++) { for (int j = 0; j < countY; j++) { if (grid[i, j].canWalk) {// 如果可以行走,绘制为绿色或白色 if(i>countX/2 && j>countY/2 || i<countX/2 && j<countY/2){ Gizmos.color = Color.green; } else Gizmos.color = Color.white; } else Gizmos.color = Color.red;// 如果不可以行走,绘制为红色 Gizmos.DrawCube(grid[i,j].worldPos, new Vector3(nodeDia - 0.2f, 0.2f, nodeDia - 0.2f)); } } } // 绘制最终路径网格 if(paths!=null && paths.Count>0){ Gizmos.color = Color.cyan; foreach (var item in paths) { Gizmos.DrawCube(item.worldPos, new Vector3(nodeDia - 0.2f, 0.2f, nodeDia - 0.2f)); } } } /// <summary> /// 通过世界坐标点,获取对应网格的坐标值 /// </summary> /// <param name="pos"></param> /// <returns></returns> public Node GetNodeByPos(Vector3 pos) { Vector3 reaPos = pos - transform.position;//相对坐标位置 //Debug.Log("reaPosl" + reaPos); float percentX = (reaPos.x + gridSize / 2) / gridSize; float percentY = (reaPos.z + gridSize / 2) / gridSize; int X = Mathf.CeilToInt(countX * percentX)-1; int Y = Mathf.CeilToInt(countY * percentY)-1; if (grid != null) { return grid[X, Y]; } else return null; } /// <summary> /// 通过一个格子,根据九宫格,获取周围8个相邻格子 /// </summary> /// <param name="node"></param> /// <returns></returns> public List<Node> GetNeighborNodes(Node node) { List<Node> nodeList = new List<Node>(); int x = node.x; int y = node.y; for (int i = -1; i <=1; i++) { for (int j = -1; j <= 1; j++) { if(x+i>=0 && y+j>=0&& x+i<countX && y+j<countY){ nodeList.Add(grid[x + i, y + j]); } } } return nodeList; } }
寻路
using UnityEngine; using System.Collections; using System.Collections.Generic; public class Paths : MonoBehaviour { float cost1;// 相邻两个格子的水平估价 float cost2;//相邻两个格子的对角线估价 Grid grid; bool isMove = false; int index = 0; Vector3 des; public Transform moveTarget;//移动对象 public static Paths Instance;//创建单例 public void Awake() { Instance = this; } void Start() { grid = GetComponent<Grid>(); cost1 = grid.nodeRadius * 2; cost2 = Mathf.Sqrt(grid.nodeRadius * 2 * grid.nodeRadius * 2 * 2);//对角线 //setDis(enemy.position); } // Update is called once per frame void Update() { } //设置寻路目标点 public void setDis(Vector3 destination) { findPath(moveTarget.position, destination); } //停止寻路 public void StopDis() { grid.paths = null; } /// <summary> /// 获取从起始点到终点的最近路程 /// </summary> /// <param name="beginPos">起始点</param> /// <param name="destination">终点</param> private void findPath(Vector3 beginPos, Vector3 destination) { List<Node> openList = new List<Node>();//创建开启列表 List<Node> closeList = new List<Node>();//创建关闭列表 Node beginNode = grid.GetNodeByPos(beginPos); Node EndNode = grid.GetNodeByPos(destination); Node currentNode = beginNode;//将当前节点保存到开启列表 openList.Add(currentNode); while (currentNode != EndNode) { if (openList.Count <= 0) { break; } currentNode = openList[0]; //从开启列表寻找估价最小的节点 for (int i = 0; i < openList.Count; i++) { if (openList[i].fCost < currentNode.fCost || (openList[i].fCost == currentNode.fCost && openList[i].hCost < currentNode.hCost)) { currentNode = openList[i]; } } closeList.Add(currentNode);//将当前节点添加到关闭列表 openList.Remove(currentNode);//将当前节点从开启列表移出 if (currentNode == EndNode) {//如果寻路到达终点,生成最终路径 grid.paths = GeneratePath(beginNode, EndNode); return; } //获取当前节点周围8个点,如果它们不在关闭集合并且可以行走,计算它们的估价,并添加到开启集合 List<Node> neiborList = grid.GetNeighborNodes(currentNode); foreach (Node item in neiborList) { float gCost = GetNodesCost(item, currentNode) + currentNode.gCost; float hCost = GetNodesCost(item, EndNode); if (item == null || closeList.Contains(item) || !item.canWalk) { continue; } //如果已经保存在开启集合,计算新的g估价大于原先估价,更新其估计和父节点 if (openList.Contains(item)) { if (gCost < item.gCost) { item.gCost = gCost; item.parent = currentNode; } } else { item.gCost = gCost; item.hCost = hCost; item.parent = currentNode; openList.Add(item); } } } } /// <summary> /// 生成最终路径 /// </summary> /// <param name="beginNode">起始节点</param> /// <param name="EndNode">终止节点</param> /// <returns></returns> private List<Node> GeneratePath(Node beginNode, Node EndNode) {//通过寻找父节点的方式,生成从终点到起始点的路径,然后将集合取反,生成最终路径 List<Node> path = new List<Node>(); Node temp = EndNode; while (temp != beginNode) { path.Add(temp); temp = temp.parent; } path.Reverse();//取反 return path; } /// <summary> /// 获取两个节点之间的估价值 /// </summary> /// <param name="item"></param> /// <param name="currentNode"></param> /// <returns></returns> private float GetNodesCost(Node item, Node currentNode) { //计算节点坐标水平和垂直的差值 int offsetX = Mathf.Abs(item.x - currentNode.x); int offsetY = Mathf.Abs(item.y - currentNode.y); float dis; //让小的差值乘于对角线估价,大的差值与小的差值的差乘于水平估价 if (offsetX >= offsetY) { dis = offsetY * cost2 + (offsetX - offsetY) * cost1; } else { dis = offsetX * cost2 + (offsetY - offsetX) * cost1; } return dis; } }
相关文章推荐
- lamp (module) 部署应用
- 在Ubuntu上安装redmine
- Effective cpp 读书笔记8
- 关于jQuery里面的选择器
- Android之Activity之间的数据传递-intent-bundle
- 127.0.0.1与本机ip的区别
- NGUI poplist使用
- 重定向
- Linux下Nginx源码安装
- C++第六次作业
- PHP获取网站中各文章的第一张图片的代码示例
- CSS定位
- Vector的浅析
- php测试题整理(0519)
- 【学习C++】C++ Primer Plus (第六版)第十一章编程练习1-7
- 【M3U8】测试地址及android播放器
- 【bzoj4602】【SDOI2016】【齿轮】【dfs】
- Linux shll中的$符号 -- 读取命令行参数
- ASP.NET 验证控件结合正则表达式验证
- stringstream