Unity中利用A*算法实现简单寻路
来源:互联网 发布:陈克礼枪毙 知乎 编辑:程序博客网 时间:2024/06/15 09:35
A*算法理解
A*算法目的在于找到一条能够到达目的地的最短路线,其核心思想是广度优先搜索,以当前格子为中心,遍历周围的格子,通过计算每个格子到达目标点的距离,找出一条能够到达且消耗最小的路径。A*算法中的公式很简单:F =G+H,我是这样理解的:
如图,假设角色需要从A点移动到B点。G代表从A点移动到自身相邻的八个点(A1到A8)所消耗的体力值,因为正方向比斜方向移动的距离短,所以正方向消耗10点体力(A到A5),斜方向消耗14点体力(A到A3);H代表从当前点到终点的横纵坐标之差的绝对值,即曼哈顿距离(从A到B为40,从A5到B为30)。也可以这样理解:G为单次行走消耗的体力值,H表示到达终点还将需要消耗的体力值。当某个点受到阻挡时,需返回到之前的点重新计算。这样一来,地图上每一个格子所代表的点到达终点需要消耗的体力值都被量化,下一步我们要做的事情就是找到一条消耗体力值F最小的路径。
Unity中模拟A*算法
1.定义格子类
格子代表着二维地图中的坐标点,且每个格子有F、G、H值。因此,格子可以用一个类来表示:
public class Node {//坐标public int X, Y;//记录消耗值public int F, G, H;//父节点public Node Parent;//自动计算H值,估计值public void CalculateH(Vector2 end){ this.H = (int)(10 * (Mathf.Abs(end.x - X) + Mathf.Abs(end.y - Y)));}//自动计算F值public void CalculateF(){ this.F = G + H;}public Node(int x, int y, Node parent = null){ this.X = x; this.Y = y; this.Parent = parent; }public Node(Vector2 point, Node parent = null){ this.X = (int)point.x; this.Y = (int)point.y; this.Parent = parent;}}
2.生成简易的格子地图
由于格子代表一个个坐标点,因此可以利用txt文档存储地图坐标信息,用0表示起始点,9表示目标点,2表示障碍物,1表示可行走的格子。因此txt中存放数据为:
1111111
1112111
1012191
1112111
1111111
利用StreamReader类加载数据,代码如下:
byte[,] mapData; //用来接受地图数据int rowCount; //地图行数int colCount; //地图列数string mapPath; //txt文件存放路径void LoadMapData(string path){ StreamReader sr = new StreamReader(mapPath); string content = sr.ReadToEnd(); string[] rows = content.Split('\n'); rowCount = rows.Length; colCount = rows[0].Trim().Length; mapData = new byte[rowCount, colCount]; for (int i = 0; i < rowCount; i++) { for (int j = 0; j < colCount; j++) { mapData[i, j] = byte.Parse(rows[i][j].ToString()); } } sr.Close(); sr.Dispose();}
加载完地图数据后,在场景中创建出地图(用cube表示格子,因此提前制作好三种cube的预制体)
void CreatMap(){ //生成蓝色Cube表示起始点 GameObject bluePrefab = Resources.Load<GameObject>("BluePrefab"); //生成红色Cube表示终点 GameObject redPrefab = Resources.Load<GameObject>("RedPrefab"); //生成黑色Cube表示障碍物 GmaObject blackPrefab = Resources.Load<GameObject>("BlackPrefab"); //生成白色Cube表示普通格子 GmaObject blackPrefab = Resources.Load<GameObject>("WhitePrefab"); //for循环遍历mapData中的数据,并根据数值生成相应的Cube for (int i = 0; i < rowCount; i++) { for (int j = 0; j < colCount; j++) { GameObject grid = null; switch ((int)mapData[i, j]) { case 0: grid = Instantiate(bluePrefab); break; case 1: grid = Instantiate(WhitePrefab); break; case 2: grid = Instantiate(blackPrefab); break; case 9: grid = Instantiate(redPrefab); break; } grid.transform.position = GetPosition(i,j); grid.transform.SetParent(transform); grid.transform.localScale = Vector3.one * 0.8f; grid.name = i + "_" + j; } }}
生成后的地图如下:
3.实现A*算法
Vector2 start; //起始点Vector2 endd; //终止点//某点的八个相邻方向Vector2[] dirs = new Vector[]{Vector2.up,Vector2.down,Vector2.left,Vector2.right,new Vector2(1,1),new Vector2(1,-1),new Vector2(-1,1),new Vector2(-1,-1)};List<Node> list = new List<Node>(); //待访问的节点列表List<Node> visited = new List<Node>(); //已访问的节点列表Stack<Vector2> path = new Stack<Vector2>();//行走路径//A*算法void AStar(){ GetStartAndEnd(); //创建起始节点进入队列 Node root = new Node(start); list.Add(root); //开始检索list中的节点以及周围八个方向的点(广度优先算法) while(list.Count>0) { //先对list中的节点按照F值排序 list.Sort(NodeSort); //取F值最小的节点作为起始节点 Node node = list[0]; list.Remove(node); visited.Add(node); //对节点周围八个方向的点进行遍历 for(int i=0;i<dirs.Length;i++) { Vector2 point; point.x=node.X+dirs[i].x; point.y=node.Y+dirs[i].y; //判断该点能否到达 if(IsOk(point)) { Node n = new Node(point); //计算该点的G、H、F值 n.G = i>3?(node.G+14):(node.G+10); n.CalculateH(end); n.CalculateF(); list.Add(n); if(point == end) { Debug.Log("Find!") Node p = n; //遍历父节点(来时的路径) while(p!=null) { path.Push(new Vector2(p.x,p.y)); p = p.parent; } return; } } }}//获取起始点和终止点void GetStartAndEnd(){ for(int i=0;i<rowCount;i++) { for(int j=0;j<colCount;j++) { //0表示起始点 if(mapData[i,j]==0) { start.x = i; start.y = j; } //9表示终止点 if(mapData[i,j]==9) { start.x = i; start.y = j; } } }}//集合中的节点按照F值排序void NodeSort(Node node1,Node node2){ if (x.F > y.F) return 1; else if (x.F < y.F) return -1; else return 0;}//判断该节点是否能够到达bool IsOk(Vector2 point){ //越界 if (point.x < 0 || point.x >= rowCount || point.y < 0 || point.y >= colCount) { return false; } //障碍物 if (mapData[(int)point.x, (int)point.y] == 2) { return false; } //节点已访问 for (int i = 0; i < visited.Count; i++) { if (visited[i].X == point.x && visited[i].Y == point.y) { return false; } } //节点在List中 for (int i = 0; i < list.Count; i++) { if (list[i].X == point.x && list[i].Y == point.y) { return false; } } return true;}
4.创建角色进行寻路测试
//用于测试的移动角色Transform player; //角色Vector3 target; //当前移动的目标点Vector3 moveDir; //当前移动的方向void Awake(){ mapPath=Application.dataPath+"Map.txt"; LoadMapData(mapPath); CreateMap();}void Start(){ AStar(); player = GameObject.CreatePrimitive(PrimitiveType.Capsule).transform; player.transform.position = GetPosition(start); target = getPosition(path.Peek());}void Update(){ moveDir = (target - player.position).normalized; player.Translate(moveDir*Time.deltaTime); if(Vector3.Distance(player.position,target)<0.1f&&path.Count>0) { path.Pop(); if(path.Count == 0) { this.enabled = false; } else { target = GetPosition(path.Peek()); } } }//获取横纵坐标值获取世界坐标Vector3 GetPosition(int i,int j){ //j控制内层循环,因此关联x轴坐标(表示列数) float x = j - colCount * 0.5f; //i控制外层循环,因此关联z轴坐标(表示行数) float y = rowCount*0.5f - i; return new Vector3(x, 0, y);}//根据vector2坐标获取世界坐标Vector3 GetPosition(Vector2 point){ float x = point.y - colCount * 0.5f; float y = rowCount * 0.5f - point.x ; return new Vector3(x, 0, y);}
运行结果如下:
0 0
- Unity中利用A*算法实现简单寻路
- 使用Unity实现A*寻路算法
- Unity A*寻路三个简单实用的算法
- Unity A*寻路三个简单实用的算法
- Unity A*算法实现
- Unity A*寻路算法
- A*寻路算法C++简单实现
- unity中实现A*算法的一些问题
- Unity3d利用A*寻路算法实现寻路模拟
- JS中实现A*算法寻路
- unity 接入A*寻路算法总结
- [Unity] A-Star(A星)寻路算法
- Unity中如何利用一个协程和www类实现简单的图片下载
- cocos2dx游戏中A*寻路算法的实现
- Unity Game Programming AI(5)A Star寻路算法
- Unity A*寻路算法(人物移动AI)
- 利用Unity和高通实现简单的AR功能
- 利用unity实现简单的贪吃蛇游戏
- 图之 邻接表 邻接矩阵 结构体定义
- redis学习教程
- centos php扩展开发流程
- 九、递归和分治思想
- DM9000网卡驱动
- Unity中利用A*算法实现简单寻路
- C++栈的地址变化
- ubuntu安装程序时遇到错误 you might want to run 'apt-get -f install' to correct these
- 文章标题
- python爬虫程序-登录
- windows API 实现精确的打点计时器
- Elevator
- 图之 宽度优先遍历 BFS 邻接表创建的图
- 微服务架构设计 第八步: 设计微服务对外 REST API