您的位置:首页 > 其它

理解A*寻路算法具体过程

2016-05-20 18:53 405 查看
A*寻路




创建网格节点类

using UnityEngine;
using System.Collections;

public class Node {

public float radium; // 半径
public int x, y; // 下标
public float gCost = 0, hCost = 0, fCost = 0;   // 估价值
public Node parent;  // 父节点
public bool canWalk;// 能否行走,是否有障碍物
public Vector3 worldPos;// 世界坐标

public Node(int x, int y, bool canWalk, Vector3 worldPos)
{// 初始化变量
this.x = x;
this.y = y;
this.canWalk = canWalk;
this.worldPos = worldPos;
}

}


创建网格

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Grid : MonoBehaviour
{
public float gridSize;// 网格大小
public float nodeRadius;// 每一个小格子的半径
float nodeDia;// 小格子的直径
int countX, countY;// 水平格子数量和垂直格子数量
Node[,] grid;// 网格
public LayerMask obsLayer;// 障碍物所在层
public List<Node> paths;// 最终可行路径
public void Start()
{// 初始化变量
nodeDia = nodeRadius * 2;
countX = Mathf.CeilToInt(gridSize / nodeDia);
countY = countX;
grid = new Node[countX, countY];
initGrid();
}

/// <summary>
/// 初始化网格矩阵
/// </summary>
private void initGrid()
{
Vector3 startPos = transform.position - new Vector3(gridSize / 2, 0, gridSize / 2);
for (int i = 0; i < countX; i++)
{
for (int j = 0; j < countY; j++)
{
// 获取小方格的世界坐标点
Vector3 pos = startPos + new Vector3(i * nodeDia + nodeRadius, 0, j * nodeDia + nodeRadius);
// 判断小方格周围是否有障碍物
bool canWalk = !Physics.CheckSphere(pos, nodeRadius, obsLayer);
Node node = new Node(i, j, canWalk, pos);
grid[i, j] = node;
}
}
}

public void OnDrawGizmos()
{
//Gizmos.DrawCube(transform.position, new Vector3(gridSize, 1, gridSize));
// 绘制网格矩阵
if (grid != null)
{
for (int i = 0; i < countX; i++)
{
for (int j = 0; j < countY; j++)
{
if (grid[i, j].canWalk)
{// 如果可以行走,绘制为绿色或白色
if(i>countX/2 && j>countY/2 || i<countX/2 && j<countY/2){
Gizmos.color = Color.green;
}
else Gizmos.color = Color.white;

}
else Gizmos.color = Color.red;// 如果不可以行走,绘制为红色
Gizmos.DrawCube(grid[i,j].worldPos, new Vector3(nodeDia - 0.2f, 0.2f, nodeDia - 0.2f));
}
}
}
// 绘制最终路径网格
if(paths!=null && paths.Count>0){
Gizmos.color = Color.cyan;
foreach (var item in paths)
{
Gizmos.DrawCube(item.worldPos, new Vector3(nodeDia - 0.2f, 0.2f, nodeDia - 0.2f));

}

}

}

/// <summary>
/// 通过世界坐标点,获取对应网格的坐标值
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public Node GetNodeByPos(Vector3 pos) {
Vector3 reaPos = pos - transform.position;//相对坐标位置
//Debug.Log("reaPosl" + reaPos);
float percentX = (reaPos.x + gridSize / 2) / gridSize;
float percentY = (reaPos.z + gridSize / 2) / gridSize;
int X = Mathf.CeilToInt(countX * percentX)-1;
int Y = Mathf.CeilToInt(countY * percentY)-1;
if (grid != null)
{
return grid[X, Y];
}
else return null;
}

/// <summary>
/// 通过一个格子,根据九宫格,获取周围8个相邻格子
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public List<Node> GetNeighborNodes(Node node)
{
List<Node> nodeList = new List<Node>();
int x = node.x;
int y = node.y;
for (int i = -1; i <=1; i++)
{
for (int j = -1; j <= 1; j++)
{
if(x+i>=0 && y+j>=0&& x+i<countX && y+j<countY){
nodeList.Add(grid[x + i, y + j]);
}
}
}

return nodeList;
}

}


寻路

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Paths : MonoBehaviour
{
float cost1;// 相邻两个格子的水平估价
float cost2;//相邻两个格子的对角线估价
Grid grid;
bool isMove = false;
int index = 0;
Vector3 des;
public Transform moveTarget;//移动对象
public static Paths Instance;//创建单例

public void Awake()
{
Instance = this;
}

void Start()
{

grid = GetComponent<Grid>();
cost1 = grid.nodeRadius * 2;
cost2 = Mathf.Sqrt(grid.nodeRadius * 2 * grid.nodeRadius * 2 * 2);//对角线
//setDis(enemy.position);
}
// Update is called once per frame
void Update()
{

}

//设置寻路目标点
public  void setDis(Vector3 destination)
{
findPath(moveTarget.position, destination);

}

//停止寻路
public void StopDis() {
grid.paths = null;
}

/// <summary>
/// 获取从起始点到终点的最近路程
/// </summary>
/// <param name="beginPos">起始点</param>
/// <param name="destination">终点</param>
private  void findPath(Vector3 beginPos, Vector3 destination)
{
List<Node> openList = new List<Node>();//创建开启列表
List<Node> closeList = new List<Node>();//创建关闭列表

Node beginNode = grid.GetNodeByPos(beginPos);
Node EndNode = grid.GetNodeByPos(destination);
Node currentNode = beginNode;//将当前节点保存到开启列表
openList.Add(currentNode);
while (currentNode != EndNode)
{
if (openList.Count <= 0) { break; }

currentNode = openList[0];

//从开启列表寻找估价最小的节点
for (int i = 0; i < openList.Count; i++)
{

if (openList[i].fCost < currentNode.fCost || (openList[i].fCost == currentNode.fCost && openList[i].hCost < currentNode.hCost))
{
currentNode = openList[i];
}
}

closeList.Add(currentNode);//将当前节点添加到关闭列表
openList.Remove(currentNode);//将当前节点从开启列表移出
if (currentNode == EndNode)
{//如果寻路到达终点,生成最终路径
grid.paths = GeneratePath(beginNode, EndNode);
return;
}
//获取当前节点周围8个点,如果它们不在关闭集合并且可以行走,计算它们的估价,并添加到开启集合

List<Node> neiborList = grid.GetNeighborNodes(currentNode);
foreach (Node item in neiborList)
{
float gCost = GetNodesCost(item, currentNode) + currentNode.gCost;
float hCost = GetNodesCost(item, EndNode);

if (item == null || closeList.Contains(item) || !item.canWalk)
{
continue;
}
//如果已经保存在开启集合,计算新的g估价大于原先估价,更新其估计和父节点
if (openList.Contains(item))
{
if (gCost < item.gCost)
{
item.gCost = gCost;
item.parent = currentNode;
}
}
else
{
item.gCost = gCost;
item.hCost = hCost;
item.parent = currentNode;
openList.Add(item);

}
}

}
}

/// <summary>
/// 生成最终路径
/// </summary>
/// <param name="beginNode">起始节点</param>
/// <param name="EndNode">终止节点</param>
/// <returns></returns>
private List<Node> GeneratePath(Node beginNode, Node EndNode)
{//通过寻找父节点的方式,生成从终点到起始点的路径,然后将集合取反,生成最终路径
List<Node> path = new List<Node>();
Node temp = EndNode;
while (temp != beginNode)
{
path.Add(temp);
temp = temp.parent;
}
path.Reverse();//取反
return path;
}

/// <summary>
/// 获取两个节点之间的估价值
/// </summary>
/// <param name="item"></param>
/// <param name="currentNode"></param>
/// <returns></returns>
private float GetNodesCost(Node item, Node currentNode)
{
//计算节点坐标水平和垂直的差值
int offsetX = Mathf.Abs(item.x - currentNode.x);
int offsetY = Mathf.Abs(item.y - currentNode.y);
float dis;
//让小的差值乘于对角线估价,大的差值与小的差值的差乘于水平估价
if (offsetX >= offsetY)
{
dis = offsetY * cost2 + (offsetX - offsetY) * cost1;
}
else
{
dis = offsetX * cost2 + (offsetY - offsetX) * cost1;
}
return dis;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: