Unity算法——A*(AStar)寻路算法概要及简单应用
非常简陋的版本的GIF图,放在开头。
前言:
再Unity中寻路导航是游戏开发的最基本的需求之一
什么是A*寻路算法:
A*算法呢,*是什么呢,以一个网格为中心点,他周围八个方向的网格就是*,

- A寻路算法的估量代价*
在A*算法中核心的寻路依据就是估量代价,在A*中通常用 F 表示。F = G + H
其中G表示当前点到起始点的估量代价,H表示当前点到终点的代价。
(起始点周围的八个点)
每个点里面的三个数字分别为:1.左下角是距离起始点的估量代价,记为G。A距离中心点的距离为1的直线距离,B距离中心点的距离为根号2,约1.4,在这里基础单位为10的话,A的起始点估量代价G=10,B的起始点估量代价G=14 。 2.右下角是距离终点的估量代价,记为H,为该点到终点的步数既几步可达终点。3.左上角是综合估量代价,记为F=G+H,起始点估量代价与终点估量代价的和;
(网图,其中一些估量代价是错的,仅供参考,侵删)
A*算法的核心是两个集合分别为开放列表与关闭列表:Open List,CloseList
原理:
从中心位置对相邻格子
假设A是起始格子
OpenList CloseList
A
A1
...
A6
A7
从中心位置对相邻8个格子进行判断最小代价,将A移除并添加进CloseList中,并对OpenList进行排序
假设最小代价是是A4,将A4放在OpenList表头,并查找A4周围的8个格子,A41-A48
OpenList CloseList
A4 A
A1
...
A6
A7
A41
A42
...
A48
将A4从Open中移除并添加到CloseList中,
对OpenList进行排序,假设A42的最小代价最小
将A42放在Open的表头,并查询A42相邻8个格子
OpenList CloseList
A42 A
A1 A4
...
A6
A7
A41
...
A48
...........................
OpenList CloseList
A4234 A
A1 A4
... A42
A6 A423
A7 A4234
A41
...
A48
最后可能得到的CloseList如上所示,
A4->A4234就是A*算法得到的最短路径
实现步骤:
1.把起始格添加到开启列表。
2.重复如下的工作:
a) 寻找开启列表中估量代价F值最低的格子。我们称它为当前格。
b) 把它切换到关闭列表。
c) 对相邻的8格中的每一个进行如下操作
* 如果它不可通过或者已经在关闭列表中,略过它。反之如下。
* 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的F,G,和H值。
* 如果它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,并且重新计算这一格的G和F值。如果你保持你的开启列表按F值排序,改变之后你可能需要重新对开启列表排序。
d) 停止,当你
* 把目标格添加进了关闭列表(注解),这时候路径被找到,或者
* 没有找到目标格,开启列表已经空了。这时候,路径不存在。
3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。
代码实现:
1.AStarController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStarController : MonoBehaviour {
//起点坐标
public int startPosX, startPosY;
//终点坐标
public int endPosX, endPosY;
//障碍物比率
public int obstacleRate;
private GameObject gridPrefab;
private List<Grid> openList;
private List<Grid> closeList;
//结果栈
private Stack<Grid> result;
//所有格子数组
private Grid[,] allGrids = null;
void Awake()
{
result = new Stack<Grid> ();
openList = new List<Grid> ();
closeList = new List<Grid> ();
//设置数组长度
allGrids = new Grid[(int)(transform.localScale.x
* 20),(int)(transform.localScale.z * 20)];
}
void Start()
{
gridPrefab = Resources.Load<GameObject> ("Grid");
//遍历生成格子
for (int i = 0; i < transform.localScale.x * 20; i++) {
for (int j = 0; j < transform.localScale.z * 20; j++) {
//生成
Grid currentGrid = Instantiate (gridPrefab).
GetComponent<Grid>();
//计算偏移量
Vector2 offset = new Vector2 (-4.7f * transform.
localScale.x,-4.7f * transform.localScale.z);
//设置方块的世界坐标
currentGrid.transform.position = new Vector3 (
offset.x + i * 0.5f, 0, offset.y + j * 0.5f);
//设置格子坐标
currentGrid.x = i;
currentGrid.y = j;
//存储起来
allGrids[i,j] = currentGrid;
//随机障碍物
int r = Random.Range(1,101);
if (r <= obstacleRate) {
currentGrid.MyGridType = GridType.Obstacle;
}
}
}
//设置起点和终点
allGrids[startPosX,startPosY].MyGridType = GridType.Start;
allGrids [endPosX, endPosY].MyGridType = GridType.End;
//调用AStar计算
AStarCount();
}
/// <summary>
/// A*计算
/// </summary>
void AStarCount()
{
//将起点放置到OpenList
openList.Add (allGrids [startPosX, startPosY]);
//获取当前要发现的中心格子
Grid currentGrid = openList[0];
//循环递归
//开启列表中有对象&&当前的中心不是终点
while (openList.Count > 0 &&
currentGrid.MyGridType != GridType.End) {
//重新排序
openList.Sort();
//获取新的中心格子
currentGrid = openList[0];
//判断最新的格子是否是终点
if (currentGrid.MyGridType == GridType.End) {
///TODO:生成结果
GetParent(currentGrid);
return;
}
//上下左右,左上右上左下右下
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (i != 0 || j != 0) {
//获取新格子的格子坐标
int x = currentGrid.x + i;
int y = currentGrid.y + j;
//判断格子坐标合法
//前四个条件判断坐标的合法性
//新格子不能是障碍物
//新格子没有被遍历过
if (x > 0 && y > 0 && x < allGrids.GetLength (0)
&& y < allGrids.GetLength (1) &&
allGrids [x, y].MyGridType != GridType.Obstacle &&
!closeList.Contains (allGrids [x, y])) {
//计算G值
int g = (int)(currentGrid.G +
Mathf.Sqrt(Mathf.Abs(i) + Mathf.Abs(j))*10);
//判断新格子是否被遍历过
//如果被遍历过,判断当前G值是否比之前的更小
if (allGrids [x, y].G == 0 || g < allGrids [x, y].G) {
//更新G值
allGrids[x,y].G = g;
//更新父格子
allGrids[x,y].parent = currentGrid;
}
//计算H
allGrids[x,y].H = (Mathf.Abs(x - endPosX) + Mathf.Abs(y- endPosY)) * 10;
//计算F
allGrids[x,y].F = allGrids[x,y].G + allGrids[x,y].H;
//加入到开启列表
if (!openList.Contains (allGrids [x, y])) {
Debug.Log (1);
openList.Add (allGrids [x, y]);
}
}
}
}
}
//将当前格子移除OpenList
openList.Remove(currentGrid);
//放到CloseList里
closeList.Add(currentGrid);
//OpenList空了
if (openList.Count == 0) {
Debug.Log ("Can Not Arrave!!!");
}
}
}
private void GetParent(Grid current)
{
//进栈
result.Push (current);
//判断是否继续递归
if (current.parent != null) {
GetParent (current.parent);
} else {
//展示结果
StartCoroutine (ShowResult ());
}
}
IEnumerator ShowResult()
{
//获取总长度
int resultCount = result.Count;
while (result.Count > 0) {
yield return new WaitForSeconds(0.1f);
//出栈
Grid currentResultGrid = result.Pop();
//计算比例
float scale = (resultCount - result.Count)/(float)resultCount;
//上色
Color currentC = Color.Lerp(Color.red,Color.green,scale);
currentResultGrid.SetColor(currentC);
}
}
void Update()
{
if (Input.GetKeyDown (KeyCode.Space)) {
UnityEngine.SceneManagement.SceneManager.LoadScene (0);
}
}
}
2.GridController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public enum GridType {
//正常类型
Normal,
//障碍物类型
Obstacle,
//起点类型
Start,
//终点类型
End
}
public class GridController : MonoBehaviour,IComparable {
//坐标
public int x ,y;
//FGH
public int F, G, H;
//坐标
public GridController parent;
//格子类型
private GridType gridType;
public GridType myGridType {
get {
return gridType;
}
set {
gridType = value;
//设置显示颜色
Color tempColor = Color.white;
switch (gridType) {
case GridType.Start:
tempColor = Color.red;
break;
case GridType.End:
tempColor = Color.green;
break;
case GridType.Obstacle:
tempColor = Color.blue;
break;
default:
break;
}
SetColor(tempColor);
}
}
private MeshRenderer meshRenderer;
private void Awake() {
meshRenderer = GetComponent<MeshRenderer>();
}
public void SetColor(Color c) {
meshRenderer.material.color = c;
}
public int CompareTo(object obj) {
GridController target = obj as GridController;
if (F < target.F) {
return -1;
}else if (F > target.F) {
return 1;
}else {
return 0;
}
}
}
更多推荐



所有评论(0)