算法简介:

A*搜寻算法俗称A星算法。A*算法是比较流行的启发式搜索算法之一,被广泛应用于路径优化领域[。它的独特之处是检查最短路径中每个可能的节点时引入了全局信息,对当前节点距终点的距离做出估计,并作为评价该节点处于最短路线上的可能性的量度。[1] - 百度百科

  通俗点说,就是在起点与目标点之中找出一条可通行的最短路线。常见于各类RPG游戏中的自动寻路功能:点击某个任务,人物会自动移动过去;点击地图上某个点,人物也会照着显示出来(或者隐藏了)的路线前进。玩过LoL,红色警戒等类似游戏的小伙伴都知道,右击小地图的某一处,小地图会出现一条从当前位置到所点击位置的红色路线,然后英雄就会随着这条路线一直走到目标点。这种功能,就是A*算法的在游戏中的常见应用之处。

场景布置:

1 布置地面:场景中新建一个Plane(地面)重置一下Transform,然后将Scale拉伸至20倍(此时地面的大小是200x200),地面是不带y坐标的,即只有xz平面,此时平面上左下角坐标点是(-100,-100),右上角是(100,100)这个很重要,后面的判断坐标点是否越界(超出地面范围)就是依据这个地面的大小和坐标

2 布置障碍物:新建一个空物体Bars,在Bars下创建一个cube,将cube随便拉伸做成一堵墙,然后复制,摆放到场景上各个位置。

3 创建玩家:场景中新建一个Capsule(为了好控制,就选用胶囊体了),取名为Player,为它挂上Character Controller角色控制器。

4 路线:新建一个Sphere球,涂成红色,改名叫Way,卸载掉Collider(一定要卸载),做成预制体,用来表示计算出来的路线上的每一点。新建一个空物体Ways,用来存储路线

5 层级设置:为了代码中好检测,为地面Plane设立一个单独的层Plane,层级号位9.所有障碍物层级为Bars,层级号为8

脚本编辑:

  脚本没写的太过复杂,就编写了两个脚本:

PlayerCtrl.cs   用来控制玩家移动

AStarRun.cs  用A*算法来计算起点到目标点的最佳路径并返回

    将这两个脚本都挂载在玩家上(Player)

PlayerCtrl.cs 用来控制玩家移动

using UnityEngine;using System.Collections;//玩家控制器public class PlayerCtrl : MonoBehaviour {

public GameObject wayLook;//寻路线的红点

public float moveSpeed = 10f;//角色前进速度

private CharacterController cc;//角色控制器

private Transform waysParent;//寻路线的放置位置

private Ray ray;//射线检测鼠标点击点

private RaycastHit hit;

private bool IsMove = false;//是否正在寻路过程

void Start ()

{

cc = GetComponent<CharacterController>();

waysParent = GameObject.Find("Ways").transform;

}

void Update ()

{

//鼠标单击移动

if(Input.GetMouseButtonDown(0))

{

ray = Camera.main.ScreenPointToRay(Input.mousePosition);//获取主相机到鼠标点击点的射线

//检测射线是否碰撞到地面:地面的层级是9

if(Physics.Raycast(ray,out hit,1 << 9))

{

//往目标点移动过去

//return;

Vector3 starPoint = new Vector3(transform.position.x,0,transform.position.z);//寻路的起点

Vector3 targetPoint = new Vector3(hit.point.x,0,hit.point.z);//寻路的终点

if(!IsMove)

StartCoroutine(AutoMove(starPoint,targetPoint));//开启自动寻路

}

}

}

/// <summary>

/// 自动寻路协程

/// </summary>

/// <returns>The move.</returns>

/// <param name="starPoint">起点.</param>

/// <param name="targetPoint">目标点.</param>    IEnumerator AutoMove(Vector3 starPoint,Vector3 targetPoint)

{

IsMove = true;

yield return new WaitForFixedUpdate();

//运用A星算法计算出到起点到目标点的最佳路径

Vector3[] ways = GetComponent<AStarRun>().AStarFindWay(starPoint,targetPoint);

if(ways.Length == 0)

{

IsMove = false;

yield break;

}

//打印显示出寻路线

foreach(var v in ways)

{

GameObject way = Instantiate<GameObject>(wayLook);

way.transform.parent = waysParent;

way.transform.localPosition = v;

way.transform.rotation = Quaternion.identity;

way.transform.localScale = Vector3.one;

}

//让玩家开始沿着寻路线移动

int i = 0;

Vector3 target = new Vector3(ways[i].x,transform.position.y,ways[i].z);

transform.LookAt(target);

while(true)

{

yield return new WaitForFixedUpdate();

Debug.Log("run run run !!!");

cc.SimpleMove(transform.forward * moveSpeed * Time.deltaTime);

if(Vector3.Distance(transform.position,target) < 1f)

{

Debug.Log("run is ok !!!");

++i;

if(i >= ways.Length)

break;

target = new Vector3(ways[i].x,transform.position.y,ways[i].z);

transform.LookAt(target);

}

}

//移动完毕,删除移动路径

for(int child = waysParent.childCount - 1;child >= 0;--child)

Destroy(waysParent.GetChild(child).gameObject);

//等待执行下一次自动寻路

IsMove = false;

}

}

AStarRun.cs  用A*算法来计算起点到目标点的最佳路径并返回

using UnityEngine;using System.Collections;using System.Collections.Generic;

//A*寻路public class AStarRun : MonoBehaviour {

private Map map = new Map();//格子地图

开启列表

private List<MapPoint> open_List = new List<MapPoint>();

//关闭列表

private List<MapPoint> close_List = new List<MapPoint>();

//定义一个路径数组

private ArrayList way = new ArrayList();

//判断某点是否在开启列表中

private bool IsInOpenList(int x,int z)

{

foreach(var v in open_List)

{

if(v.x == x && v.z == z)

return true;

}

return false;

}

//判断某点是否在关闭列表中

private bool IsInCloseList(int x,int z)

{

foreach(var v in close_List)

{

if(v.x == x && v.z == z)

return true;

}

return false;

}

//从开启列表中找到那个F值最小的格子

private MapPoint FindMinFInOpenList()

{

MapPoint minPoint = null;

foreach(var v in open_List)

{

if(minPoint == null || minPoint.GetF > v.GetF)

minPoint = v;

}

return minPoint;

}

//从开启列表中找到格子

private MapPoint FindInOpenList(int x,int z)

{

foreach(var v in open_List)

{

if(v.x == x && v.z == z)

return v;

}

return null;

}

/// <summary>

/// a星算法寻路

/// </summary>

/// <returns>寻到的路结果.</returns>

/// <param name="starPoint">起点    </param>

/// <param name="targetPoint">终点</param>

///

public Vector3[] AStarFindWay(Vector3 starPoint,Vector3 targetPoint)

{

//清空容器        way.Clear();

open_List.Clear();

close_List.Clear();

//初始化起点格子

MapPoint starMapPoint = new MapPoint();

starMapPoint.x = (int)starPoint.x;

starMapPoint.z = (int)starPoint.z;

//初始化终点格子

MapPoint targetMapPoint = new MapPoint();

targetMapPoint.x = (int)targetPoint.x;

targetMapPoint.z = (int)targetPoint.z;

//将起点格子添加到开启列表中        open_List.Add(starMapPoint);

//寻找最佳路径

//当目标点不在打开路径中时或者打开列表为空时循环执行

while(!IsInOpenList(targetMapPoint.x,targetMapPoint.z) || open_List.Count == 0)

{

//从开启列表中找到那个F值最小的格子

MapPoint minPoint = FindMinFInOpenList();

if(minPoint == null)

return null;

//将该点从开启列表中删除,同时添加到关闭列表中            open_List.Remove(minPoint);

close_List.Add(minPoint);

//检查改点周边的格子            CheckPerPointWithMap(minPoint,targetMapPoint);

}

//在开启列表中找到终点

MapPoint endPoint = FindInOpenList(targetMapPoint.x,targetMapPoint.z);

Vector3 everyWay = new Vector3(endPoint.x,0,endPoint.z);//保存单个路径点

way.Add(everyWay);//添加到路径数组中

//遍历终点,找到每一个父节点:即寻到的路

while(endPoint.fatherPoint != null)

{

everyWay.x = endPoint.fatherPoint.x;

everyWay.z = endPoint.fatherPoint.z;

everyWay.y = 0;

way.Add(everyWay);

endPoint = endPoint.fatherPoint;

}

//将路径数组从倒序变成正序并返回

Vector3[] ways = new Vector3[way.Count];

for(int i = way.Count - 1;i >= 0;--i)

{

ways[way.Count - i - 1] = (Vector3)way[i];

}

//清空容器        way.Clear();

open_List.Clear();

close_List.Clear();

//返回正序的路径数组

return ways;

}

//判断地图上某个坐标点是不是障碍点

private bool IsBar(int x,int z)

{

//判断地图上某个坐标点是不是障碍点

Vector3 p = new Vector3(x,0,z);

//检测该点周边是否有障碍物

//障碍物层级为8

Collider[] colliders = Physics.OverlapSphere(p,1,1 << 8);

if(colliders.Length > 0)

return true;//有障碍物,说明该点不可通过,是障碍物点

return false;

}

//计算某方块的G值

public int GetG(MapPoint p)

{

if(p.fatherPoint == null)

return 0;

if(p.x == p.fatherPoint.x || p.z == p.fatherPoint.z)

return p.fatherPoint.G + 10;

else

return p.fatherPoint.G + 14;

}

//计算某方块的H值

public int GetH(MapPoint p,MapPoint targetPoint)

{

return (Mathf.Abs(targetPoint.x - p.x) + Mathf.Abs(targetPoint.z - p.z)) * 10;

}

//检查某点周边的格子

private void CheckPerPointWithMap(MapPoint _point,MapPoint targetPoint)

{

for(int i = _point.x-1; i <= _point.x + 1;++i)

{

for(int j = _point.z - 1; j <= _point.z + 1; ++j)

{

//剔除超过地图的点

if(i < map.star_X || i > map.end_X || j < map.star_Z || j > map.end_Z)

continue;

//剔除该点是障碍点:即周围有墙的点

if(IsBar(i,j))

continue;

//剔除已经存在关闭列表或者本身点

if(IsInCloseList(i,j) || (i == _point.x && j == _point.z))

continue;

//剩下的就是没有判断过的点了

if(IsInOpenList(i,j))

{

//如果该点在开启列表中

//找到该点

MapPoint point = FindInOpenList(i,j);

int G = 0;

//计算出该点新的移动代价

if(point.x == _point.x || point.z == _point.z)

G = point.G + 10;

else

G = point.G + 14;

//如果该点的新G值比前一次小

if(G < point.G)

{

//更新新的G点

point.G = G;

point.fatherPoint = _point;

}

}

else

{

//如果该点不在开启列表内

//初始化该点,并将该点添加到开启列表中

MapPoint newPoint = new MapPoint();

newPoint.x = i;

newPoint.z = j;

newPoint.fatherPoint = _point;

//计算该点的G值和H值并赋值

newPoint.G = GetG(newPoint);

newPoint.H = GetH(newPoint,targetPoint);

//将初始化完毕的格子添加到开启列表中                    open_List.Add(newPoint);

}

}

}

}

}

//地图类public class Map

{

public int star_X;// 横坐标起点

public int star_Z;// 纵坐标起点

public int end_X;// 横坐标终点

public int end_Z;//纵坐标终点

public Map()

{

star_X = - 100;

star_Z = - 100;

end_X =  100;

end_Z =  100;

}

}

//每一个格子的信息public class MapPoint

{

//F = G + H

//G        从起点A移动到指定方格的移动代价,父格子到本格子代价:直线为10,斜线为14

//H        使用 Manhattan 计算方法,    计算(当前方格到目标方格的横线上+竖线上所经过的方格数)* 10

public int x;//格子的x坐标

public int z;//格子的z坐标

public int G;

public int H;

public int GetF{

get

{

return G + H;

}

}

public MapPoint fatherPoint;//父格子

public MapPoint(){}

public MapPoint(int _x,int _z,int _G,int _H,MapPoint _fatherPoint)

{

this.x = _x;

this.z = _z;

this.G = _G;

this.H = _H;

this.fatherPoint = _fatherPoint;

}

}

更多unity2018的功能介绍请到paws3d爪爪学院查找。链接https://www.paws3d.com/learn/,也可以加入unity学习讨论群935714213

近期更有资深开发人士直播分享unity开发经验,详情请进入官网或加入QQ群了解

A*算法之在U3d下实现简单的自动寻路相关推荐

  1. python比较两个列表的重合度_#源代码#超几何分布算法介绍及python下的实现代码...

    原标题:#源代码#超几何分布算法介绍及python下的实现代码 超几何分布是统计学上一种离散概率分布.它描述了由有限个物件中抽出n个物件,成功抽出指定种类的物件的次数(不归还). 在产品质量的不放回抽 ...

  2. 马尔科夫模型在Gowalla数据集下的简单实践

    马尔科夫模型在Gowalla数据集下的简单实践 马尔科夫模型实践第一战 基础知识 数学知识 代码知识 数据处理 单独一次转移的概率计算函数设计 生成转移概率矩阵 生成初始向量 结论 马尔科夫模型实践第 ...

  3. [Android] Android MVP 架构下 最简单的 代码实现

    Android  MVP 架构下  最简单的 代码实现 首先看图: 上图是MVP,下图是MVC MVP和MVC的区别,在于以前的View层不仅要和model层交互,还要和controller层交互.而 ...

  4. 用C语言编写一个Linux下的简单shell程序

    这是一个简单的C程序,展示了如何进行系统调用执行logout cd ls pwd pid rm mkdir mv cp等命令,这是一个简单的命令解释程序shell,其源代码如下: #include & ...

  5. nginx Win下实现简单的负载均衡(2)站点共享Session

    快速目录: 一.nginx Win下实现简单的负载均衡(1)nginx搭建部署 二.nginx Win下实现简单的负载均衡(2)站点共享Session 三.nginx Win下实现简单的负载均衡(3) ...

  6. ubuntu 运行c++_06_Linux下VSCode简单编程(远程开发WSL_Ubuntu_18.04) | C语言入门

    06_Linux下VSCode简单编程(远程开发WSL_Ubuntu_18.04) 本系列主题 Linux下C语言彩色控制台编程实践_基于gcc,gdb,VSCode,git和WSL_Ubuntu_1 ...

  7. artDialog对话框在PHP下的简单应用-artDialog弹出层篇

    本教程使用的是artDialog 4.1.7版本,由于需要iframe的支持,所以选择这个版本,artDialog 5.0.3不支持iframe. 本教程是基于本站站长在网页设计写代码过程中与PHP页 ...

  8. linux上用的端口转发工具,linux下最简单好用的的端口转发工具

    linux下最简单好用的的端口转发工具 解压安装 tar zxvf rinetd.tar.gz make make install 编辑配置 vi /etc/rinetd.conf 0.0.0.0 8 ...

  9. 算法----最大承载量下的最大价值问题

    算法----最大承载量下的最大价值问题 代码: 栈代码:(存储哪些是需要的价值物) #pragma once #include<stdio.h> #define maxSize 100 t ...

最新文章

  1. 《高效程序员的45个习惯》之体会
  2. 从源码分析DEARGUI之画图和删图
  3. opencv_图像反转
  4. c# winform窗体边框风格的设计
  5. zabbix数据库表结构
  6. 单片机蜂鸣器编程音乐_工程师,还有6个引脚封装的单片机?涨知识了
  7. 【UVA202】Repeating Decimals(模拟除法)
  8. 手机qq2008触屏版_手机版卖家中心在哪里
  9. Fredholm第二类积分方程的MATLAB代码实现(1)
  10. GOM登录器技术研究,闪退、掉线的原因分析和解决
  11. 1916 Problem C 合唱队形
  12. 烤仔TVのCCW | 带宽不可能三角(下)
  13. 2022年山东省安全员A证特种作业证考试题库模拟考试平台操作
  14. 易语言 html 服务器,易语言模拟网页Web服务器源代码
  15. D1net阅闻:Facebook上线求职功能,以挑战LinedIn
  16. 关于@ComponentScan 的使用 和springboot启动类所在位置的关系
  17. linux 下载文件到本地
  18. 记SQL Server实战修复死锁总结
  19. 萌新接触前端的第二课——CSS
  20. Xilinx下载电缆找不到,WARNING:iMPACT:923 - Can not find cable, check cable setup

热门文章

  1. 诚聘java开发工程师(中高级)-base地北京海淀区
  2. linux怎样删除分区合并,linux – 从SSD中删除分区
  3. 改改改, 我实在是烦了
  4. springboot基于微信小程序的选课系统060000
  5. LDO 芯片烫手,问题出在哪里?
  6. port 22: Connection refused
  7. linux/windows查看端口被占用情况
  8. HCIA-Datacom园区网络项目实战 华为认证实验手册 ENSP配置
  9. AngularJS之API ——function
  10. java练习题:宝石和石头(map)