实现效果
[a*寻路scene场景绘制效果]
脚本绑定:
源码:
1 .FindPath
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FindPath : MonoBehaviour
{
public Transform player, EndPoint;
Grid grid;
void Start()
{
grid = GetComponent<Grid>();
}
void Update()
{
FindingPath(player.position, EndPoint.position);
Control();
}
/// <summary>
/// 控制移动方法(测试用)
/// </summary>
public virtual void Control() {
if (Input.GetKey(KeyCode.W))
{
if (grid.path.Count > 0)
{
player.position = grid.path[0].WorldPos;
}
}
}
/// <summary>
/// 获取两点之间的距离
/// </summary>
/// <param name="StartPos"></param>
/// <param name="EndPos"></param>
void FindingPath(Vector3 StartPos, Vector3 EndPos)
{
Node startNode = grid.GetFromPostion(StartPos);
Node EndNode = grid.GetFromPostion(EndPos);
List<Node> openSet = new List<Node>();
HashSet<Node> closeSet = new HashSet<Node>();
openSet.Add(startNode);
//当开启列表中还有元素的情况下不断的查找最优点
while (openSet.Count > 0)
{
Node currentNode = openSet[0];
for (int i = 0; i < openSet.Count; i++)
{
//如果openSet[i]的费用低于currentNode或者.........
if (openSet[i].FCost < currentNode.FCost || openSet[i].FCost == currentNode.FCost && openSet[i].hCost < currentNode.hCost)
{
currentNode = openSet[i];
}
}
//如果该节点是最优点,从开始列表中移除
openSet.Remove(currentNode);
//添加到关闭列表
closeSet.Add(currentNode);
if (currentNode == EndNode)
{
GeneratePath(startNode, EndNode);
return;
}
foreach (var node in grid.GetNeibourhood(currentNode))
{
if (!node.CanWalk || closeSet.Contains(node)) continue;
//计算相邻格子的消费
int newCost = currentNode.gCost + GetDistanceNodes(currentNode, node);
if (newCost < node.gCost || !openSet.Contains(node))
{
node.gCost = newCost;
node.hCost = GetDistanceNodes(node, EndNode);
node.parent = currentNode;
if (!openSet.Contains(node))
{
openSet.Add(node);
}
}
}
}
}
//递归Node的父节点 得到最终的寻路路径
private void GeneratePath(Node StartNode, Node EndNode)
{
List<Node> path = new List<Node>();
Node temp = EndNode;
//当目标节点不是开始节点的话循环
while (temp != StartNode)
{
path.Add(temp);
temp = temp.parent;
}
path.Reverse();
grid.path = path;
}
int GetDistanceNodes(Node a, Node b)
{
int cntX = Mathf.Abs(a.gridX - b.gridX);
int cntY = Mathf.Abs(a.gridY - b.gridY);
if (cntX > cntY)
{
return 14 * cntY + 10 * (cntX - cntY);
}
else
{
return 14 * cntX + 10 * (cntY - cntX);
}
}
}
2.Grid
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Grid : MonoBehaviour
{
private Node[,] grid;
/// <summary>
/// 网格大小
/// </summary>
public Vector2 gridSize;
/// <summary>
/// 每个网格的半径
/// </summary>
public float nodeRadius;
/// <summary>
/// 每个网格的直径
/// </summary>
public float nodeDiameter;
/// <summary>
/// 获得不可行走的层
/// </summary>
public LayerMask WhatLayer;
/// <summary>
/// 网格的长度
/// </summary>
public int gridCntX;
public int gridCntY;
public Transform player;
public List<Node> path = new List<Node>();
void Start()
{
nodeDiameter = nodeRadius * 2;
gridCntX = Mathf.RoundToInt(gridSize.x / nodeDiameter);
gridCntY = Mathf.RoundToInt(gridSize.y / nodeDiameter);
grid = new Node[gridCntX, gridCntY];
CreatGrid();
}
/// <summary>
/// 初始化网格数据
/// </summary>
private void CreatGrid()
{
//获取初始位置
Vector3 startPoint = transform.position - gridSize.x / 2 * Vector3.right - gridSize.y / 2 * Vector3.forward;
for (int i = 0; i < gridCntX; i++)
{
for (int j = 0; j < gridCntY; j++)
{
Vector3 worldPoint = startPoint + Vector3.right * (i * nodeDiameter + nodeRadius) + Vector3.forward * (j * nodeDiameter + nodeRadius);
bool walkable = SelectUnWalkable(worldPoint);
grid[i, j] = new Node(walkable, worldPoint, i, j);
}
}
}
/// <summary>
/// 获取格子相对应该的节点位置
/// </summary>
/// <param name="postion">格子的V3位置</param>
/// <returns></returns>
public Node GetFromPostion(Vector3 postion)
{
//获取该位置在X,Y轴上的百分比
float percentX = (postion.x + gridSize.x / 2) / gridSize.x;
float percentY = (postion.z + gridSize.y / 2) / gridSize.y;
percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY);
int x = Mathf.RoundToInt((gridCntX - 1) * percentX);
int y = Mathf.RoundToInt((gridCntY - 1) * percentY);
return grid[x, y];
}
//绘制辅助显示线框
void OnDrawGizmos()
{
//绘制一个线框Cuble 参数1:位置 参数2:线框大小
Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, 1, gridSize.y));
if (grid == null) return;
//绘制基础地图点
foreach (var node in grid)
{
Gizmos.color = node.CanWalk ? Color.white : Color.red;
Gizmos.DrawCube(node.WorldPos, Vector3.one * (nodeDiameter - 0.05f));
}
Node playerNode = GetFromPostion(player.position);
//绘制寻路点
foreach (var node in path)
{
Gizmos.color = Color.black;
Gizmos.DrawCube(node.WorldPos, Vector3.one * (nodeDiameter - 0.05f));
}
//绘制起始点
if (playerNode != null && playerNode.CanWalk)
{
Gizmos.color = Color.cyan;
Gizmos.DrawCube(playerNode.WorldPos, Vector3.one * (nodeDiameter - 0.05f));
}
}
/// <summary>
/// 获取选定节点的相邻节点(此处可优化,建议处理成在初始化的时候就记录好相邻格子的数据列表)
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public List<Node> GetNeibourhood(Node node)
{
List<Node> neibourhood = new List<Node>();
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if (i == 0 && j == 0)
{
continue;
}
int tempX = node.gridX + i;
int tempY = node.gridY + j;
if (tempX < gridCntX && tempX > 0 && tempY > 0 && tempY < gridCntY)
{
neibourhood.Add(grid[tempX, tempY]);
}
}
}
return neibourhood;
}
/// <summary>
/// 设置选择遮挡路径的方式
/// </summary>
/// <param name="worldPoint"></param>
/// <returns></returns>
public virtual bool SelectUnWalkable(Vector3 worldPoint)
{
bool walkable = !Physics.CheckSphere(worldPoint, nodeRadius, WhatLayer);
return walkable;
}
}
3.Node
using UnityEngine;
using System.Collections;
public class Node
{
//是否可以行走
public bool CanWalk;
//世界坐标
public Vector3 WorldPos;
//相对坐标 X,Y
public int gridX, gridY;
//从跟节点到此节点的总费用
public int gCost;
//从父节点走到此节点花费的费用
public int hCost;
//父节点
public Node parent;
public int FCost
{
get { return gCost + hCost; }
}
public Node(bool canWalk, Vector3 worldPos, int x, int y)
{
CanWalk = canWalk;
WorldPos = worldPos;
gridX = x;
gridY = y;
}
}