以前项目中用到了寻路,就写了个A*寻路的算法类,感觉还不错,这里分享出来。

这里有可运行的测试项目:https://github.com/LiuFeng1011/Test/tree/master/Assets/AStar

A*算法的原理网上有很多,这里只简单说一下我这个算法的逻辑:

*我的这个算法里面没有关闭列表,因为我会根据地图数据创建一个对应的节点数据的数组,每个节点数据记录自己当前的状态,是开启还是关闭的。节点数据只有在寻找周围点被找到的时候才会建立并放到数组中,这样在每次添加节点时,只要在数组中取出对应位置的节点数据,就能知道节点是开启的还是关闭的或者还没有被添加,不用去遍历open和close列表。

*开放列表直接使用List来保存,为了使列表保持有序,每次添加节点时直接把节点按照从小到大的顺序插入到对应位置。这里可以使用其它数据结构或者排序算法来进行优化。

*路径可以行进的方向使用一个数组来表示:

//附近的格子 4方向int[,] nearArray = new int[,]{{0, 1},{1, 0},{0, -1},{-1, 0},};
    //附近的格子 8方向int[,] nearArray= new int[,]{{0, 1},   {1, 1},   {1, 0},   {1, -1},  {0, -1},  {-1, -1}, {-1, 0},  {-1, 1}   };

这里可以修改成任意个方向和距离,我只对1个距离以内的移动进行过测试。其它距离不能保证正确性。

*G和H值的计算可以使用曼哈顿距离或者欧式距离,曼哈顿距离效率上会更高。

    float GetNodeG(MapNode parent,MapNode node){//曼哈顿距离float dis = Mathf.Abs(parent.p.x - node.p.x) + Mathf.Abs(parent.p.y - node.p.y);//欧式距离//float dis = Vector2.Distance(parent.p, node.p);return parent.g + dis;}float GetNodeH( MapNode node){//曼哈顿距离return Mathf.Abs(endPosition.x - node.p.x) + Mathf.Abs(endPosition.y - node.p.y);//欧式距离//return Vector2.Distance(endPosition,node.p);}

最终的结果只有在4方向时表现上会有些许不同:

   

左图为曼哈顿距离的结果,右图为欧式距离的结果,虽然移动距离是一样的,但是很明显欧式距离更符合我们的习惯。

*使用方式:

调用AStar类的StratAStar方法即可

    /// <summary>/// 开始寻路/// </summary>/// <returns>路径点数据,从起始点到结束点路径的有序vector数组.</returns>/// <param name="map">地图数据 二维数组,0为可移动路径,1为不可移动路径.</param>/// <param name="startPosition">开始位置.</param>/// <param name="endPosition">结束位置.</param>public List<Vector2> StratAStar(int[,] map,Vector2 startPosition,Vector2 endPosition)

*示例:

定义一个二维数据作为我们的地图数据:

int[,] map = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,1,1,1,1,1,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,1,0,0,0,0},{0,0,0,1,1,1,1,1,1,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0}};

设置起始和结束位置:

        Vector2 startPosition = new Vector2(5,6);Vector2 endPosition = new Vector2(8, 12);

创建AStar类并调用开始方法:

        AStar astar = new AStar();List<Vector2> path = astar.StratAStar(map,startPosition,endPosition);

看下效果:

下面使用随机数组:

int[, ] map = new int[mapWidth,mapHight];

设定起始点和结束点,然后随机向数组中填充数据

        Vector2 startPosition = new Vector2(Random.Range(0, mapWidth),Random.Range(0, mapHight));Vector2 endPosition = new Vector2(Random.Range(0, mapWidth), Random.Range(0, mapHight));for (int i = 0; i < mapWidth;i ++){for (int j = 0; j < mapHight; j++){//如果是起点或者终点 跳过if((i == (int)startPosition.x && j == (int)startPosition.y) ||(i == (int)endPosition.x && j == (int)endPosition.x) ){continue;}if (Random.Range(0f, 1f) < 0.2f){map[i, j] = 1;}}}

看下效果:

完整的AStar类:

using System.Collections.Generic;
using UnityEngine;public class AStar
{//节点private class MapNode{//节点状态 public enum enNodeState{normal,//待机open,//开启close //关闭}public MapNode parent = null;//父节点public float g, h, f;public Vector2 p;//位置public enNodeState state = enNodeState.normal;}//附近的格子 8方向int[,] nearArray= new int[,]{{0, 1},   {1, 1},   {1, 0},   {1, -1},  {0, -1},  {-1, -1}, {-1, 0},  {-1, 1}   };//附近的格子 4方向//int[,] nearArray = new int[,]{//    {0, 1},//    {1, 0},//    {0, -1},//    {-1, 0},//};Vector2 startPosition, endPosition;//起始点和结束点//开放列表,在插入时根据MapNode的f值进行排序,即优先队列List<MapNode> openList = new List<MapNode>();//所有点MapNode[,] mapList;//向开放列表中加入节点,这里需要进行排序void PushNode(MapNode node){node.state = MapNode.enNodeState.open;for (int i = 0; i < openList.Count; i++){if (openList[i].f > node.f){openList.Insert(i,node);return;}}openList.Add(node);}//创建节点时即对节点进行估价MapNode CreateNode(Vector2 p,MapNode parent){MapNode node = new MapNode();node.parent = parent;node.p = p;//f = g+h//g和h直接使用曼哈顿距离//--------g------if(parent != null){node.g = GetNodeG(parent,node);}else {node.g = 0;}//--------h------node.h =GetNodeH(node);//--------f------node.f = node.g + node.h;//创建的节点加入到节点列表中mapList[(int)node.p.x,(int)node.p.y] = node;return node;}float GetNodeG(MapNode parent,MapNode node){//曼哈顿距离//float dis = Mathf.Abs(parent.p.x - node.p.x) + Mathf.Abs(parent.p.y - node.p.y);//欧式距离float dis = Vector2.Distance(parent.p, node.p);return parent.g + dis;}float GetNodeH( MapNode node){//曼哈顿距离//return Mathf.Abs(endPosition.x - node.p.x) + Mathf.Abs(endPosition.y - node.p.y);//欧式距离return Vector2.Distance(endPosition,node.p);}/// <summary>/// 开始寻路/// </summary>/// <returns>路径点数据,从起始点到结束点路径的有序vector数组.</returns>/// <param name="map">地图数据 二维数组,0为可移动路径,1为不可移动路径.</param>/// <param name="startPosition">开始位置.</param>/// <param name="endPosition">结束位置.</param>public List<Vector2> StratAStar(int[,] map,Vector2 startPosition,Vector2 endPosition){mapList = new MapNode[map.GetLength(0),map.GetLength(1)];//附近可移动点的数量int nearcount = nearArray.GetLength(0);this.startPosition = startPosition;this.endPosition = endPosition;//起始点加入开启列表MapNode startNode = CreateNode(startPosition,null);PushNode(startNode);MapNode endNode = null;//目标节点//开始寻找路径while(openList.Count > 0){//取出开启列表中f值最低的节点,由于我们在向开启列表中添加节点时已经进行了排序,所以这里直接取第0个值即可MapNode node = openList[0];//如果node为目标点则结束寻找if(node.p.x == endPosition.x && node.p.y == endPosition.y){endNode = node;break;}//设置为关闭状态并从开启列表中移除node.state = MapNode.enNodeState.close;openList.RemoveAt(0);//当前点坐标//相邻格子加入到开启列表for (int i = 0; i < nearcount; i ++){Vector2 nearPosition = node.p - new Vector2(nearArray[i,0], nearArray[i,1]);//位置是否超出范围if((nearPosition.x < 0 || nearPosition.x >= map.GetLength(0)) ||(nearPosition.y < 0 || nearPosition.y >= map.GetLength(1))){continue;}//该位置是否可以移动if(map[(int)nearPosition.x,(int)nearPosition.y] != 0){continue;}//是否已经创建过这个点MapNode nearNode = mapList[(int)nearPosition.x, (int)nearPosition.y];if(nearNode != null){//该节点已经创建过//节点是否关闭if(nearNode.state == MapNode.enNodeState.close){continue;}//重新计算gfloat newg = GetNodeG(node, nearNode);if(newg < nearNode.g){nearNode.parent = node;nearNode.g = newg;nearNode.f = nearNode.g + nearNode.h;Debug.Log("==" + openList.Count);//重新对开放列表排序openList.Remove(nearNode);Debug.Log(openList.Count);}else{continue;}}else{//创建节点nearNode = CreateNode(nearPosition, node);}PushNode(nearNode);}}//路径数据List<Vector2> ret = new List<Vector2>();if (endNode == null){Debug.Log("no path!");return ret;}//将路径保存到数组中while(endNode.parent != null){ret.Insert(0,endNode.p);endNode = endNode.parent;}return ret;}
}

unity中的一个简单易用的A*寻路算法类相关推荐

  1. 学习在Unity中创建一个动作RPG游戏

    游戏开发变得简单.使用Unity学习C#并创建您自己的动作角色扮演游戏! 你会学到什么 学习C#,一种现代通用的编程语言. 了解Unity中2D发展的能力. 发展强大的和可移植的解决问题的技能. 了解 ...

  2. idea log 不输出error_还在使用console.log()吗?Bunyan:一个简单易用的JS日志框架

    Bunyan是一个简单易用的JS日志框架,可以工作在多种环境下,这里以Nodejs为例说明Bunyan的基本用法.是时候替换console.log的写法了. 安装 npm install --save ...

  3. 基于ForkJoin构建一个简单易用的并发组件

    2019独角兽企业重金招聘Python工程师标准>>> 基于ForkJoin构建一个简单易用的并发组件 在实际的业务开发中,需要用到并发编程的知识,实际使用线程池来异步执行任务的场景 ...

  4. 开发一个简单易用的SDK的详细步骤(超详细,超适用)

    文章目录 开发一个简单易用的SDK的详细步骤 创建starter步骤 关键点 总结 开发一个简单易用的SDK的详细步骤 创建starter步骤 1.新建一个 spring boot 初始化项目 2.添 ...

  5. easyopen——一个简单易用的接口开放平台

    摘要: 一个简单易用的接口开放平台,平台封装了常用的参数校验.结果返回等功能,开发者只需实现业务代码即可. easyopen介绍 一个简单易用的接口开放平台,平台封装了常用的参数校验.结果返回等功能, ...

  6. Unity入门——实现一个简单的跑酷游戏(资源预制)

    Unity入门--实现一个简单的跑酷游戏 资源预制 一款跑酷游戏,需要大量重复的场景资源,比如道路.障碍物等,无论是从游戏体验的角度还是运行效率的角度考虑,都不应该全部事先摆好,而是应该由代码随机生成 ...

  7. 【Unity3D 灵巧小知识点】 ☀️ | Unity中几个简单又常见的报错异常

    Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 . 包括游戏开发.美术.建筑.汽车设计.影视在内的所有创作者,借助 Unity 将创意 ...

  8. [Unity3D]用C#在unity里面写一个简单的红绿灯

    [Unity3D]用C#在unity里面写一个简单的红绿灯 00.成果展示 01.创建可变颜色的小球 02.编写代码 R1.cs R2.cs G1.cs G2.cs Y1.cs Y2.cs 03.自己 ...

  9. d3.js 旋转图形_一个简单易用但功能强大的图形矢量化软件,扫描图片转换成CAD图的软件等等...

    背景简介 很多童鞋可能可能有想要把图片转成CAD能打开的格式,但是找不到软件,今天要分享的这个软件值得一试. 内容简介 AlgoLabR2VToolkit是一个将光栅图像转换为矢量图像的软件,转换后的 ...

最新文章

  1. Qt Style Sheet 翻译(中)--类似css
  2. Oracle错误大全(目前自己所碰到的)
  3. RSGAN:对抗模型中的“图灵测试”思想
  4. realmeq参数配置详情_realmeq2i参数_realmeq2i参数配置详情
  5. php数组格式化显示,php 打印数组格式化显示
  6. python填写excel内容_python实现数据写入excel表格
  7. C++中面向对象的理解
  8. SCUT - 299 - Kaildls的数组划分 - dp - 高精
  9. koa2 导出excel表格设置样式_一调整Excel表格的行列宽度,图片又得重新调整?点这个设置就行...
  10. matlab第四章答案,matlab第四章课后
  11. 冈萨雷斯 数字图像处理 插图 打包下载 原tif版 + png版
  12. 项目管理十大知识领域
  13. 计算力矩——计算关节力矩以平衡端点力和力矩
  14. android吉他谱组件,Paranoid Android drum吉他谱
  15. 今日推荐:2068个开源的网站模板【免费下载】
  16. 计算球体积和球表面积
  17. 如何更换文件夹图标、简洁有效地去除Windows 10快捷方式小箭头?
  18. 回归预测 | MATLAB实现GWO-BiLSTM灰狼算法优化双向长短期记忆神经网络多输入单输出回归预测
  19. 黑防VIP会员工具包
  20. 如何选择考研数学资料?最全考研数学复习资料推荐

热门文章

  1. 作业帮发布新品学习机,首创“AI老师一对一”模式
  2. 智能+建筑>智能建筑
  3. 【java】java技术要学习的内容
  4. vue实现商城列表渲染
  5. win10可以运行linux模拟器,Win10将原生兼容安卓App,但模拟器不会被打败
  6. matlab计算下列极限,MATLAB微积分计算极限,又快又好
  7. 不得不说,这是青铜才需要了解的,绝地求生刺激战场段位保护解析
  8. verilog实现计算均值
  9. 天嵌开发版 imx6 移植qt
  10. 洗地机哪个品牌好用,洗地机十大品牌分享