//节点
public class Node {
//节点位置
public Vector3 position;
//在地图中的X轴位置下标
public int indexX;
//在地图中的Z轴位置下标
public int indexZ;
//该节点是否可以行走
public bool canWalk;
//行走代价
public float gCost;
//距离目标的代价
public float hCost;
public float fCost { get { return gCost + hCost; } }
//父节点 方便回溯路径
public Node parent;
public Node(Vector3 position, int indexX, int indexZ, bool canWalk)
{
this.position = position;
this.indexX = indexX;
this.indexZ = indexZ;
this.canWalk = canWalk;
}
}
public class Grid : MonoBehaviour {
//地图 储存所有的节点信息
public Node[,] maps;
//地图的长和宽
public float mapSizeX;
public float mapSizeZ;
//每一个节点的大小
public float nodeSize;
//地图长宽各占几个节点
public int countX;
public int countZ;
//障碍物层
public LayerMask canNotWalk;
public static Grid instance;
public List<Node> path;
public void Awake()
{
instance = this;
Init();
}
//初始化 生成地图
public void Init()
{
//地图大小除以节点大小 算出个数
countX = Mathf.CeilToInt(mapSizeX / nodeSize);
countZ = Mathf.CeilToInt(mapSizeZ / nodeSize);
//初始化数组
maps = new Node[countX, countZ];
//地图赋值
for (int x = 0; x < countX; x++)
{
for (int z = 0; z < countZ; z++)
{
float posX = (this.transform.position.x - mapSizeX / 2) + nodeSize * (x + 0.5f);
float posY = this.transform.position.y;
float posZ = (this.transform.position.z - mapSizeZ / 2) + nodeSize * (z + 0.5f);
Vector3 nodePos = new Vector3(posX, posY, posZ);
bool canWalk = CheckNodeCanWalk(nodePos);
Node node = new Node(nodePos, x, z, canWalk);
maps[x, z] = node;
}
}
}
//检查节点是否可以行走
public bool CheckNodeCanWalk(Vector3 nodePos)
{
return !Physics.CheckSphere(nodePos, nodeSize / 2, canNotWalk);
}
public void OnDrawGizmos()
{
if(maps != null)
{
//画出地图
foreach (Node node in maps)
{
if( path.Contains(node))
{
Gizmos.color = Color.black;
Gizmos.DrawCube(node.position, Vector3.one * nodeSize * 0.8f);
}
else
{
if (node.canWalk == true)
{
Gizmos.color = Color.gray;
Gizmos.DrawCube(node.position, Vector3.one * nodeSize * 0.8f);
}
else
{
Gizmos.color = Color.red;
Gizmos.DrawCube(node.position, Vector3.one * nodeSize * 0.8f);
}
}
}
}
}
//通过一个位置获取一个节点信息
public Node GetNodeFromVector3(Vector3 pos)
{
Node result = maps[0,0];
float mixDistance;
mixDistance = Vector3.Distance(pos, maps[0, 0].position);
for (int x = 0; x < countX; x++)
{
for (int z = 0; z < countZ; z++)
{
float distance = Vector3.Distance(pos, maps[x, z].position);
if(distance < mixDistance)
{
mixDistance = distance;
result = maps[x, z];
}
}
}
return result;
}
//通过一个节点获取周围8个节点的方法
public List<Node> GetNeighbourNode(Node node)
{
List<Node> list = new List<Node>();
for (int x = -1; x <= 1; x++)
{
for (int z = -1; z <= 1; z++)
{
if (x == 0 && z == 0) continue;
int indexX = node.indexX + x;
int indexZ = node.indexZ + z;
if (indexX >= 0 && indexX < countX && indexZ >= 0 && indexZ < countZ)
list.Add(maps[indexX, indexZ]);
}
}
return list;
}
}
public class AIPath : MonoBehaviour {
//目标点
public GameObject target;
//存放路径
public List<Node> path = new List<Node>();
// Update is called once per frame
void Update () {
FindingPath(this.transform.position, target.transform.position);
}
//寻路
void FindingPath(Vector3 startPos, Vector3 endPos)
{
//查找开始点和终点所处于的节点
Node startNode = Grid.instance.GetNodeFromVector3(startPos);
Node endNode = Grid.instance.GetNodeFromVector3(endPos);
//开关列表 开列表存放待搜索的所有节点 闭列表存放所有不需要计算的节点(例如 障碍物)
List<Node> openList = new List<Node>();
List<Node> closeList = new List<Node>();
//初始点作为待搜索的第一个点 开始搜索
openList.Add(startNode);
while(openList.Count > 0)
{
//以待搜索的第一个为起始点
Node currentNode = openList[0];
for (int i = 0; i < openList.Count; i++)
{
//判断代价F最小的点 或者 F相同判定H较小的点
if (openList[i].fCost < currentNode.fCost ||
(openList[i].fCost == currentNode.fCost && openList[i].hCost < currentNode.hCost))
{
//得出最优点
currentNode = openList[i];
}
}
//移除这个点
openList.Remove(currentNode);
//添加这个节点 不再计算
closeList.Add(currentNode);
//如果得出的最优点为终点 则算出了最终路径 结束
if(currentNode == endNode)
{
//计算路径
GetPath(startNode, endNode);
return;
}
//判断邻居点 哪些符合条件需要添加到待搜索列表中
foreach (Node node in Grid.instance.GetNeighbourNode(currentNode))
{
//不符合条件的节点
if (node.canWalk == false || closeList.Contains(node)) { continue; }
if(!openList.Contains(node))
{
//符合条件的节点需要计算
node.gCost = GetDistance(node, currentNode);
node.hCost = GetDistance(node, endNode);
//设置父节点 方便回溯求路径
node.parent = currentNode;
openList.Add(node);
}
}
}
}
//计算移动代价或者到达目标点的代价 (采用几何方式计算真实距离)
public float GetDistance(Node a,Node b)
{
return Vector3.Distance(a.position, b.position);
}
//计算最终路径
public void GetPath(Node startNode,Node endNode)
{
//清除当前路径
path.Clear();
Node temp = endNode;
//开始回溯
while(temp != startNode)
{
path.Add(temp);
temp = temp.parent;
}
//反转 得出 由初始点到终点的路径
path.Reverse();
//传递路径
Grid.instance.path = path;
}
}