运行后点击透明格子empty即执行从(0,0)起点到点击为止终点(测试是(5,5))如下图
UICamera深度要比MainCamera大,Clear Flags:Depth only,正交视野
MainCamera保持原样;注意Line绘线物体的位置大小旋转信息,不然无法看到线条。
empty是透明图片格子,代表可通过路径节点;wall是白色图片格子,代表墙体。
using UnityEngine;
public class Empty : MonoBehaviour
{
[HideInInspector]
public Vector2Int pos;
public void OnButtonClick()
{
AStarTest.Instance.PlayAstar(pos);
}
}
A*寻路原理:Unity人工智能AI编程知识_实验2-2 群组行为-CSDN博客
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class AStarTest : MonoBehaviour
{
private static AStarTest _instance;
public static AStarTest Instance
{
get
{
return _instance;
}
}
private int[,] map = {
{0,0,0,1,0,0 },
{0,1,1,1,0,1 },
{0,0,1,0,1,0 },
{0,0,0,0,1,0 },
{1,1,1,0,0,0 },
{0,0,1,1,0,0 },
};
private Vector2 wallSize;
public Transform wallParent;
public GameObject wallPrefab;
public GameObject emptyPrefab;
public class Point
{
public Vector2Int pos;
public Point parent;
public float F { get { return G + H; } } //F = G + H
public float G; //G = parent.G + Distance(parent,self)
public float H; //H = Distance(self, end)
public string GetString()
{
return "pos:" + pos + ",F:" + F + ",G:" + G + ",H:" + H + "\n";
}
}
private List<Point> openList = new List<Point>();
private List<Point> closeList = new List<Point>();
public LineRenderer lineRenderer;
public RectTransform[,] rectTransformMap;
private void Awake()
{
_instance = this;
}
void Start()
{
wallSize = wallPrefab.GetComponent<RectTransform>().rect.size;
int mapWidth = map.GetLength(0);
int mapHeight = map.GetLength(1);
Vector2 offsetPos = new Vector2(mapWidth / 2.0f * wallSize.x, -mapHeight / 2.0f * wallSize.y);
rectTransformMap = new RectTransform[mapWidth, mapHeight];
for (int i = 0; i < mapWidth; i++)
{
for (int j = 0; j < mapHeight; j++)
{
GameObject go;
int num = map[i, j];
if (num == 1)
{
//墙
go = GameObject.Instantiate(wallPrefab, wallParent);
go.name = $"wall {i}_{j}";
}
else
{
//空
go = GameObject.Instantiate(emptyPrefab, wallParent);
go.name = $"empty {i}_{j}";
go.GetComponent<Empty>().pos = new Vector2Int(i, j);
}
rectTransformMap[i, j] = go.GetComponent<RectTransform>();
rectTransformMap[i, j].anchoredPosition = new Vector2(i * wallSize.x, -j * wallSize.y) - offsetPos;
}
}
}
public void PlayAstar(Vector2Int endPos)
{
Debug.Log(endPos);
openList.Clear();
closeList.Clear();
openList.Add(new Point()
{
G = 0f,
H = GetC(new Vector2Int(0, 0), endPos),
parent = null,
pos = new Vector2Int(0, 0),
});
List<Vector2Int> resultList = CalculateAstar(endPos);
if (resultList != null)
{
lineRenderer.positionCount = resultList.Count;
for (int i = 0; i < resultList.Count; i++)
{
Vector2Int pos = resultList[i];
lineRenderer.SetPosition(i, rectTransformMap[pos.x, pos.y].anchoredPosition);
}
}
else
{
Debug.LogError("寻路失败;");
}
}
private List<Vector2Int> CalculateAstar(Vector2Int endPos)
{
int cnt = 0;
while (true)
{
//存在父节点说明已经结束
if (openList.Exists(x => x.pos.Equals(endPos)))
{
Debug.Log("找到父节点~" + endPos + ",迭代次数:" + cnt);
List<Vector2Int> resultList = new List<Vector2Int>();
Point endPoint = openList.Find(x => x.pos.Equals(endPos));
resultList.Add(endPoint.pos);
Point parent = endPoint.parent;
while (parent != null)
{
resultList.Add(parent.pos);
parent = parent.parent;
}
return resultList;
}
cnt++;
if (cnt > 100 * map.GetLength(0) * map.GetLength(1))
{
Debug.LogError(cnt);
return null;
}
//从列表取最小F值的Point开始遍历
Point currentPoint = openList.OrderBy(x => x.F).FirstOrDefault();
string str = "";
foreach(var v in openList)
{
str += v.GetString();
}
Debug.Log("最小F:" + currentPoint.GetString() + "\n" + str);
Vector2Int pos = currentPoint.pos;
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if (i == 0 && j == 0)
{
continue;
}
//过滤越界、墙体、已处理节点(存在闭合列表的节点)
Vector2Int tempPos = new Vector2Int(i + pos.x, j + pos.y);
if (tempPos.x < 0 || tempPos.x >= map.GetLength(0) || tempPos.y < 0 || tempPos.y >= map.GetLength(1)
|| map[tempPos.x, tempPos.y] == 1
|| closeList.Exists(x => x.pos.Equals(tempPos)))
{
continue;
}
//判断tempPos该节点是否已经计算, 在openList的就是已经计算的
Point tempPoint = openList.Find(x => x.pos.Equals(tempPos));
float newG = currentPoint.G + Vector2.Distance(currentPoint.pos, tempPos);
if (tempPoint != null)
{
//H固定不变,因此判断旧的G值和当前计算出的G值,如果当前G值更小,需要改变节点数据的父节点和G值为当前的,否则保持原样
float oldG = tempPoint.G;
if (newG < oldG)
{
tempPoint.G = newG;
tempPoint.parent = currentPoint;
Debug.Log("更新节点:" + tempPoint.pos + ", newG:" + newG + ", oldG:" + oldG + ",parent:" + tempPoint.parent.pos);
}
}
else
{
tempPoint = new Point()
{
G = newG,
H = GetC(tempPos, endPos),
pos = tempPos,
parent = currentPoint
};
Debug.Log("新加入节点:" + tempPoint.pos + ", newG:" + newG + ", parent:" + currentPoint.pos);
openList.Add(tempPoint);
}
}
}
//已处理过的当前节点从开启列表移除,并放入关闭列表
openList.Remove(currentPoint);
closeList.Add(currentPoint);
}
}
private float GetC(Vector2Int a, Vector2Int b)
{
return Math.Abs(a.x - b.x) + Math.Abs(a.y - b.y);
}
}