这是之前做过的一个关于A*寻路算法的小Demo ,现在分享出来 大家一起学习!
注意:在运行测试的时候 请将Cube的Scale设置成(0.9,0.9,0.9)
在运行的时候 行列尽量设置的小点 不要超过35 (我设置40*40(1600个盒子)已经报出内存缓存不够的错误)
这是一种代价值的计算方式,也就是计算所需点的消耗值F 然后取出代价最低的点依次进行计算。
实现步骤:
G值和H值的计算方式
F=G+H
G=父格子的G+(当前位置在父格子的正【前后左右】+10否则+14)
H=从起点到重点的水平距离加上竖直距离*10
【0】然后计算周围的非障碍非关闭格子的F值 计算过程=【1】【2】【3】
【1】计算G值 判断是否更新G值 (新G值<原G值||原G值==0)更新
【2】计算H F值 并更新
【3】判断如过当前计算的格子不在开启列表中 加入开启列表
【4】将当前父格子(就是中心点的盒子 由中心开启周围的盒子的那个盒子)移除开启列表 加入关闭列表
【5】按照F值从小到大的顺序进行排序
【6】判断当前最小的F值是不是终点 不是的话 回到【0】步,是的话 寻路成功
【7】取出路线:从重点死循环取出父格子,判断是起点跳出循环
下面是具体代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public enum CubeType
{
/// <summary>
/// 正常
/// </summary>
Normal,
/// <summary>
/// 障碍
/// </summary>
Barrier,
/// <summary>
/// 开始
/// </summary>
Start,
/// <summary>
/// 结束
/// </summary>
End
}
public class AStar : MonoBehaviour {
/// <summary>
/// 是否可以斜着走
/// </summary>
public bool MayInclinedToWalk;
/// <summary>
/// 生成路障的几率
/// </summary>
public int jiLv;
/// <summary>
/// 行
/// </summary>
public int Row=20;
/// <summary>
/// 列
/// </summary>
public int Line=20;
/// <summary>
/// 用来生成路面的Cube
/// </summary>
public GameObject Prefab;
/// <summary>
/// 存储路面信息
/// </summary>
public Grid[,] Cubes;
/// <summary>
/// 开始点的位置
/// </summary>
public Vector3 start;
/// <summary>
/// 结束点的位置
/// </summary>
public Vector3 end;
/// <summary>
/// 终点的格子信息
/// </summary>
public Grid End;
/// <summary>
/// 用于寻路时对计算次数的记录
/// </summary>
public int count;
/// <summary>
///
/// </summary>
public Stack<Grid> stack;
/// <summary>
/// 开放列表
/// </summary>
public List<Grid> Open=new List<Grid>();
/// <summary>
/// 闭合列表
/// </summary>
public List<Grid> Close=new List<Grid>();
/// <summary>
/// 寻路算法
/// </summary>
void WayFinding()
{
while (true)//死循环遍历 直到找到寻路结果或者寻路失败的时候跳出
{
if (Open.Count == 0)//当开启列表没有值时 寻路失败
{
Debug.Log("寻路失败" + count);
break;
}
if (Open[0].type == CubeType.End)//当到达重点的F值最小的格子是终点时 到达重点
{
Debug.Log("寻路成功" + count);
if (End.parent)
{
Grid grid = End;
while (true)
{
if (grid.parent.type == CubeType.Start)
{
stack.Push(grid);
break;
}
else
{
stack.Push(grid);
grid = grid.parent;
}
}
}
break;
}
//遍历次数计数器
count++;
//遍历当前格子周围的8个格子
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
//如果不可以斜着走
if (!MayInclinedToWalk)
{
if (Mathf.Abs(i) == 1 && Mathf.Abs(j) == 1)//跳过不是上下左右的路径
continue;
}
if (i == 0 && j == 0)//如果检测的是当前格子就跳过
continue;
//计算当前检测的格子
int x = (int)Open[0].transform.position.x + i;
int y = (int)Open[0].transform.position.z + j;
//计算条件(在范围内 不是障碍 ***不在关闭列表)
if (x >= 0 && y >= 0 && x < Row && y < Line
&& Cubes[x, y].type != CubeType.Barrier
&& !Close.Contains(Cubes[x, y]))
{
//计算G
int g = (int)(Vector3.Distance(Cubes[x, y].gameObject.transform.position
, Open[0].transform.position) * 10) + Open[0].g;
//判断更新G和父格子 (没赋值过 或者新值小的时候)
if (Cubes[x, y].g == 0 || Cubes[x, y].g > g)
{
Cubes[x, y].parent = Open[0];//设置父格子
Cubes[x, y].g = g;//设置G值
}
Cubes[x, y].h = (Mathf.Abs((int)end.x - x) + Mathf.Abs((int)end.z - y)) * 10;//更新H值
Cubes[x, y].f = Cubes[x, y].h + Cubes[x, y].g;//更新F值
if (!Open.Contains(Cubes[x, y]))//不在开启列表的时候加入开启列表 相当于在开启列表更新值 不在的话计算后加入
Open.Add(Cubes[x, y]);//加入开启列表
}
}
}
//遍历结束 加入关闭列表
Close.Add(Open[0]);
//移除开始列表
Open.Remove(Open[0]);
//重新排序计算
Open.Sort();
}
}
void Start () {
Cubes = new Grid[Row, Line];
stack = new Stack<Grid>();
///按行列生成盒子
for (int i = 0; i < Row; i++)
{
for (int j = 0; j < Line; j++)
{
GameObject cube= GameObject.Instantiate(Prefab, new Vector3((float)i, 0, (float)j), Quaternion.identity);
if (Random.Range(1, 101) < jiLv)
{
cube.GetComponent<Grid>().type = CubeType.Barrier;
cube.GetComponent<MeshRenderer>().material.color = Color.blue;
}
if (Vector3.Distance(cube.transform.position, start) <= 0.1f)
{
cube.GetComponent<Grid>().type = CubeType.Start;
cube.GetComponent<MeshRenderer>().material.color = Color.green;
Open.Add(cube.GetComponent<Grid>());
}
if (Vector3.Distance(cube.transform.position, end) <= 0.1f)
{
cube.GetComponent<Grid>().type = CubeType.End;
End = cube.GetComponent<Grid>();
cube.GetComponent<MeshRenderer>().material.color = Color.red;
}
cube.name += "_" + i + "|" + j;
Cubes[i,j]=cube.GetComponent<Grid>();
}
}
WayFinding();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(Move());
}
if (Input.GetKeyDown(KeyCode.A))
{
SceneManager.LoadScene(0);
}
if (Input.GetKeyDown(KeyCode.Z))
{
zanTing = false;
}
}
/// <summary>
/// 用于协成里的暂停标识位
/// </summary>
public bool zanTing;
/// <summary>
/// 走路效果实现//用盒子变黑模拟走路
/// </summary>
/// <returns></returns>
IEnumerator Move()
{
while(stack.Count!=0)
{
stack.Pop().GetComponent<MeshRenderer>().material.color = Color.black;
yield return new WaitForSeconds(0.2f);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[System.Serializable]
public class Grid : MonoBehaviour, IComparable//用于该类(Grid)实现比较的方法:CompareTo
{
//对当前盒子所在坐标初始化 用于计算g,h,f的值
private void Start()
{
x = (int)transform.position.x;
y = (int)transform.position.z;
}
/// <summary>
/// 坐标x
/// </summary>
public int x;
/// <summary>
/// 坐标y
/// </summary>
public int y;
/// <summary>
/// F=G+H
/// G=父格子的G+(当前位置在父格子的正【前后左右】+10否则+14)
/// H=从起点到终点的水平距离加上竖直距离*10
/// </summary>
public int f;
public int g;
public int h;
/// <summary>
/// 盒子类型 默认路面=Normal
/// </summary>
public CubeType type= CubeType.Normal;
/// <summary>
/// 盒子的父盒子 就是从父亲便利 发现的该盒子
/// </summary>
public Grid parent;
/// <summary>
/// 比较接口的实现
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int CompareTo(object obj)
{
Grid grid = (Grid)obj;
if (f < grid.f)
{
return -1;
}
else if (f == grid.f)
{
return 0;
}
else
return 1;
}
}
这个是可以斜着走路的版本 也就是勾选第一个选项的时候
下面 是不可以斜着走的的情况,也就是在遍历周围格子的时候不要吧斜着方向的盒子加入开启列表 ;这里路障产生几率较大 不是每次都有能到终点的路线