U_A*算法
A*算法参考博客:
https://bbs.huaweicloud.com/blogs/294512
算法可视化:
https://github.com/zhm-real/PathPlanning
算法拆解参考博客:(推荐)
https://blog.csdn.net/u014361280/article/details/104740876
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class FileInfo : MonoBehaviour
{
public List<点> 所有点;
public List<点> 活跃列表;
public List<点> 锁定列表;
private List<偏移点> 偏移列表;
private 点 当前点;
private 点 开始点;
private 点 结束点;
void Start()
{
变量初始化();
设置点();
计算所有点的H值();
}
private void 变量初始化()
{
活跃列表 = new List<点>();
锁定列表 = new List<点>();
}
private void 设置点()
{
当前点 = new 点(new Vector2Int(0, 0),3, 1);
结束点 = new 点(new Vector2Int(4, 4), 2, 1);
开始点 = 当前点;
所有点 = new List<点>() {
当前点,
new 点(new Vector2Int(1,0), 1, 1),
new 点(new Vector2Int(2,0), 1, 1),
new 点(new Vector2Int(3,0), 1, 1),
new 点(new Vector2Int(4,0), 1, 1),
// new 点(new Vector2Int(0,1), 1, 1),
new 点(new Vector2Int(1,1), 1, 1),
// new 点(new Vector2Int(2,1), 1, 1),
// new 点(new Vector2Int(3,1), 1, 1),
new 点(new Vector2Int(4,1), 1, 1),
new 点(new Vector2Int(0,2), 1, 1),
new 点(new Vector2Int(1,2), 1, 1),
new 点(new Vector2Int(2,2), 1, 1),
new 点(new Vector2Int(3,2), 1, 1),
new 点(new Vector2Int(4,2), 1, 1),
// new 点(new Vector2Int(0,3), 1, 1),
// new 点(new Vector2Int(1,3), 1, 1),
new 点(new Vector2Int(2,3), 1, 1),
// new 点(new Vector2Int(3,3), 1, 1),
new 点(new Vector2Int(4,3), 1, 1),
new 点(new Vector2Int(0,4), 1, 1),
new 点(new Vector2Int(1,4), 1, 1),
new 点(new Vector2Int(2,4), 1, 1),
new 点(new Vector2Int(3,4), 1, 1),
结束点,
};
偏移列表 = new List<偏移点>() {
new 偏移点(new Vector2Int(0, 1), 1),
new 偏移点(new Vector2Int(0, -1), 1),
new 偏移点(new Vector2Int(1, 0), 1),
new 偏移点(new Vector2Int(-1, 0), 1),
new 偏移点(new Vector2Int(1, 1), 3),
new 偏移点(new Vector2Int(1, -1), 3),
new 偏移点(new Vector2Int(-1, 1), 3),
new 偏移点(new Vector2Int(-1, -1), 3),
};
}
private void 计算所有点的H值()
{
foreach (var 点 in 所有点)
{
点.H = Mathf.Abs(结束点.点位置.x - 点.点位置.x) + Mathf.Abs(结束点.点位置.y - 点.点位置.y);
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Debug.LogError("开始检查");
点 缓存点 = null;
var 日志 = "";
for (var i = 0; i < 偏移列表.Count; i++)
{
// 获取缓存点
var 目标位置 = 当前点.点位置 + 偏移列表[i].位置;
缓存点 = 获取点(所有点, 目标位置);
// 缓存点为空
if (null == 缓存点)
{
日志 += $"{目标位置} 缓存点为空 \n";
continue;
}
// 缓存点已被锁定不参与计算
if (锁定列表.Contains(缓存点))
{
continue;
}
// 缓存点在活跃列表里 判断F是否更低 刷新F值
if (活跃列表.Contains(缓存点))
{
int 预估G值 = 10 * 偏移列表[i].权重 + 当前点.G;
// 如果F值更低则刷新否则不刷新
if (缓存点.G > 预估G值)
{
缓存点.G = 预估G值;
缓存点.F = 缓存点.G + 缓存点.H;
缓存点.父节点 = 当前点;
}
}
// 缓存点不在活跃列表里 计算后放入活跃列表
else
{
缓存点.G = 10 * 偏移列表[i].权重 + 当前点.G;
缓存点.F = 缓存点.G + 缓存点.H;
缓存点.父节点 = 当前点;
日志 += $"缓存点:{缓存点.点位置} F:{缓存点.F} G:{缓存点.G} H:{缓存点.H} \n";
活跃列表.Add(缓存点);
}
}
var 缓存F = 99999;
缓存点 = null;
foreach (var p in 活跃列表)
{
日志 += $"活跃列表 :F:{p.F} 位置:{p.点位置}\n";
}
foreach (var 点 in 活跃列表)
{
if (点.F < 缓存F)
{
缓存F = 点.F;
缓存点 = 点;
}
}
if (缓存点 == 结束点)
{
Debug.Log("寻路完成");
展示路径();
EditorApplication.isPaused = true;
return;
}
Debug.Log(日志 + "下一个curPoint点:" + 缓存点.点位置);
活跃列表.Remove(缓存点);
锁定列表.Add(当前点);
当前点 = 缓存点;
当前点.点物体.GetComponent<MeshRenderer>().material.color = Color.red;
}
}
void 展示路径()
{
点 缓存点 = 结束点;
while (缓存点 != 开始点)
{
缓存点 = 缓存点.父节点;
if (null != 缓存点 && 缓存点 != 开始点)
{
变色(缓存点, Color.white);
}
}
}
点 获取点(List<点> list, Vector2Int value)
{
foreach (var l in list)
{
if (l.点位置 == value)
{
return l;
}
}
return null;
}
void 变色(点 点, Color 颜色)
{
点.点物体.GetComponent<MeshRenderer>().material.color = 颜色;
}
}
public class 偏移点
{
public Vector2Int 位置;
public int 权重;
public 偏移点(Vector2Int 位置, int 权重)
{
this.位置 = 位置;
this.权重 = 权重;
}
}
public class 点
{
public Vector2Int 点位置;
public int 类型;
public Transform 点物体;
public 点 父节点;
public int F = 0;//F值
public int G = 0;
public int H = 0;
public 点(Vector2Int 点位置, int 类型, int 代价)
{
this.点位置 = 点位置;
this.类型 = 类型;
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
点物体 = go.transform;
点物体.position = new Vector3(点位置.x, 0, 点位置.y);
var color = Color.white;
if (类型 == 0)
{
color = Color.black;// 墙
}
else if (类型 == 1)
{
color = Color.cyan;// 路
}
else if (类型 == 2)
{
color = Color.yellow;// 目的地
}
else if (类型 == 3)
{
color = Color.green;// 开始点
}
点物体.GetComponent<MeshRenderer>().material.color = color;
}
}
演示: