几个月前开始做那个小游戏时,发现需要用到A寻路,看了很多讲解,终于把A搞懂了。过程描述:
①:通过循环提取Open-List中的值并不断地比较寻找此时列表中代价最低的路径点,将代价最低点移除Open-List,加入Close-List后进入②。持续循环此步骤直到Open-List中的路径点个数为0。
②:判断此路径点是否为寻路终点,若是则计算路径,直接进入④。否则,获得此点周围的非障碍点,进入③。
③:判断周围非障碍点是否已经存在于Open-List中,如果存在,重新计算并更新路径点代价函数值。如若不存在,计算路径点代价函数值后并将路径点储存在Open-List中。后将①中的提取点与此周围非障碍点设为父子关系,方便于最终路径点的提取。
④:得到最终点,依次根据节点的父子关系寻找父节点并存入数列中,直至寻找到初始路径点。数列中所有点的集合,即为寻路路径。
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//传说中的最优寻路方法,A*
public class Navigation : MonoBehaviour
{
private Vector2 chushiposition;
public GameObject gametest;
public GameObject gametestlujing;
[Range(0.5f, 1f)]
public float lengthofbox;//我的tilemap每个格子是1所以我设置的是1,这个可以根据实际情况改
public LayerMask NodeLayer;//选择障碍物所在的层
public Transform player;//拖上目标对象(被追踪者)
private Transform startPos;
public Transform meshOrigin;//格子起点对象
public class NodeItem
{//单位格子类
public bool iswall;
public Vector2 worldpos;
public int x, y;
public int g;
public int h;
public int f
{
get { return g + h; }
}
public NodeItem parentNode;
public NodeItem(bool iswall, Vector2 worldpos, int x, int y)
{
this.iswall = iswall;
this.worldpos = worldpos;
this.x = x;
this.y = y;
}
}
private NodeItem[,] nodeItems;
private int w, h;
private void Awake()
{//初始化所有格子
chushiposition = this.transform.position;
w = 40; h = 40;
nodeItems = new NodeItem[w, h];
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
Vector2 pos = new Vector2(i * lengthofbox, j * lengthofbox) + (Vector2)meshOrigin.position;
bool iswall = false;
// bool iswall = Physics2D.BoxCast(pos, new Vector2(lengthofbox-0.5f, lengthofbox-0.5f), 0, new Vector2(0, 0), NodeLayer);
RaycastHit2D hit = Physics2D.BoxCast(pos, new Vector2(lengthofbox - 0.5f, lengthofbox - 0.5f), 0, new Vector2(0, 0), NodeLayer);
if (hit)
{
iswall = true;
if (hit.collider.gameObject.tag == "Player" || hit.collider.gameObject.tag == "guai")
{
iswall = false;
}
}
nodeItems[i, j] = new NodeItem(iswall, pos, i, j);
//if (nodeItems[i, j].iswall)
//{
// GameObject.Instantiate(gametest, nodeItems[i, j].worldpos,Quaternion.identity);
//}
}
}
}
private void Start()
{
startPos = this.transform;
}
public NodeItem GetIdem(Vector2 pos)
{//获取坐标的格子坐标
int i = Mathf.RoundToInt((pos.x - ((Vector2)meshOrigin.position).x) / lengthofbox);
int j = Mathf.RoundToInt((pos.y - ((Vector2)meshOrigin.position).y) / lengthofbox);
i = Mathf.Clamp(i, 0, w - 1);
j = Mathf.Clamp(j, 0, h - 1);
return nodeItems[i, j];
}
public List Idemaround(NodeItem node)
{//获取周围8个格子信息
List aroundlist = new List();
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if (i != 0 || j != 0)
{
int x = node.x + i;
int y = node.y + j;
if (x < w && x >= 0 && y < h && y >= 0)
{
aroundlist.Add(nodeItems[node.x + i, node.y + j]);
}
}
}
}
// for(int c = 0; c < aroundlist.Count; c++)
// {
// print(aroundlist[c].x + " " + aroundlist[c].y);
// }
return aroundlist;
} private void Update()
{
if (Input.GetKeyDown(KeyCode.B))
{
findingpath((Vector2)startPos.position, (Vector2)player.position);
}
}
private void findingpath(Vector2 startpos, Vector2 distpos)
{//寻路
NodeItem startnode = GetIdem(startpos);
NodeItem distnode = GetIdem(distpos);
List Openlist = new List();
HashSet Closelist = new HashSet();
Openlist.Add(startnode);
while (Openlist.Count > 0)
{
NodeItem current = Openlist[0];
for (int i = 0, max = Openlist.Count; i < max; i++)
{
// print(Openlist[i].x + " " + Openlist[i].y + " " + Openlist[i].f);
if (current.f >= Openlist[i].f && current.h > Openlist[i].h)
{
current = Openlist[i];
}
}
Openlist.Remove(current);
Closelist.Add(current);
//Openlist.Clear();
if (current == distnode)
{
generatepath(startnode, distnode);
return;
}
foreach (NodeItem item in Idemaround(current))
{
if (item.iswall == false)
{
GameObject.Instantiate(gametest, item.worldpos, Quaternion.identity);
}
//print(item.x + " " + item.y + " " + item.f);
if (item.iswall || Closelist.Contains(item)) continue;
int newcost = current.g + getdistance(current, item);
if (newcost < item.g || !Openlist.Contains(item))
{
item.g = newcost;
item.h = getdistance(item, distnode);
item.parentNode = current;
if (!Openlist.Contains(item))
{
Openlist.Add(item);
}
}
}
}
generatepath(startnode, null);
}
private void generatepath(NodeItem A, NodeItem B)
{//生成路径泛型队列
List path = new List();
if (B != null)
{
NodeItem node = B;
while (node != A)
{
path.Add(node);
node = node.parentNode;
}
path.Add(A);
path.Reverse();
print(path.Count);
for(int i = 0; i < path.Count; i++)
{
GameObject.Instantiate(gametestlujing, path[i].worldpos, Quaternion.identity);
}
}
}
private int getdistance(NodeItem curr, NodeItem item)
{//估算权值,对角线算法
//int costx = Mathf.Abs(curr.x - item.x);
//int costy = Mathf.Abs(curr.y - item.y);
//if (costx > costy)
//{
// return 10 * costx ;
//}
//else
//{
// return 10 *costy;
//}
//曼哈顿算法
return Mathf.Abs(curr.x - item.x) * 10 + Mathf.Abs(curr.y - item.y) * 10;
}
}
这是PartB部分
可能避免不了有错误。hh~
推荐几个厉害的讲解:
https://blog.csdn.net/dengheCSDN/article/details/78778769
https://blog.csdn.net/codingriver/article/details/83186067
https://blog.csdn.net/u012234115/article/details/47152137
这几个很厉害,
我的也有自己的样子
还改进了一个搜索距离大的。