众所周知NavMesh经过一轮又一轮的优化,它的效率已经完爆大多数A*算法,为确保项目稳定性,还是建议能用就用,用不了能使DOTS就用DOTS,反正多线程这玩意unity2019就有了
本文资料参考:unity CodeMonkey的教学视频
1、整个脚本不使用class,而使用struct
用途:给unity版本不支持DOTS或多线程的替代方案,用不了Native类型的数组就用普通的换下,效率也会高不少,尽管避免不了内存的剧烈波动问题。
特点:全部都为值类形的对象数据,对栈极为友好,方便CPU处理数据效率高。采用Native类型的集合数组减少内存波动效率高。
代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using CodeMonkey.Utils;
using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
using Unity.VisualScripting;
public class Pathfinding : MonoBehaviour
{
private const int MOVE_STRAIGHT_COST = 10;
private const int MOVE_DIAGONAL_COST = 14;
private void Start()
{
// FindPath(new int2(0,0),new int2(3,1));
FunctionPeriodic.Create(() =>
{
float startTime = Time.realtimeSinceStartup;
for (int i = 0; i < 5; i++)
{
FindPath(new int2(0,0),new int2(19,19));
}
Debug.Log("时间"+(Time.realtimeSinceStartup-startTime)*1000f);
},1f);
}
private void FindPath(int2 startPosition, int2 endPosition)
{
//设置建立网格大小
int2 gridSize = new int2(20, 20); //建立一个4*4的网格
//建立高性能数组
NativeArray pathNodeArray = new NativeArray(gridSize.x * gridSize.y, Allocator.Temp);
for (int x = 0; x < gridSize.x; x++)
{
for (int y = 0; y < gridSize.y; y++)
{
//创建一个新节点
//设置起始点
PathNode pathNode = new PathNode();
pathNode.x = x;
pathNode.y = y;
pathNode.index = CalculateIndex(x,y,gridSize.x);
pathNode.gCost = int.MaxValue;
pathNode.hCost = CalculateDistanceCost(new int2(x,y),endPosition);
pathNode.CalculateFCost();
pathNode.isWalkble = true;
pathNode.cameFromNodeIndex = -1;
pathNodeArray[pathNode.index] = pathNode;
}
}
//阻挡节点
{
PathNode walkablePathNode = pathNodeArray[CalculateIndex(1,0,gridSize.x)];
walkablePathNode.SetIsWalkable(false);
pathNodeArray[CalculateIndex(1, 0, gridSize.x)] = walkablePathNode;
walkablePathNode = pathNodeArray[CalculateIndex(1,1,gridSize.x)];
walkablePathNode.SetIsWalkable(false);
pathNodeArray[CalculateIndex(1, 1, gridSize.x)] = walkablePathNode;
walkablePathNode = pathNodeArray[CalculateIndex(1,2,gridSize.x)];
walkablePathNode.SetIsWalkable(false);
pathNodeArray[CalculateIndex(1, 2, gridSize.x)] = walkablePathNode;
}
//临近节点的偏移数组
NativeArray neighbourOffsetArray = new NativeArray(new int2[]
{
new int2(-1,0),
new int2(1,0),
new int2(0,1),
new int2(0,-1),
new int2(-1,-1),
new int2(-1,1),
new int2(1,-1),
new int2(1,1),
},Allocator.Temp);
int endNodeIndex = CalculateIndex(endPosition.x,endPosition.y,gridSize.x);
PathNode startNode = pathNodeArray[CalculateIndex(startPosition.x,startPosition.y,gridSize.x)];
startNode.gCost = 0;
startNode.CalculateFCost();
pathNodeArray[startNode.index] = startNode;
NativeList openList = new NativeList(Allocator.Temp);
NativeList closeList = new NativeList(Allocator.Temp);
openList.Add(startNode.index);
while (openList.Length>0)
{
int currentNodeIndex = GetLowestCostFNodeIndex(openList,pathNodeArray);
PathNode currentNode = pathNodeArray[currentNodeIndex];
if (currentNodeIndex==endNodeIndex)
{
//到达了目的地
break;//打破循环
}
//如果没有到达目的地的
for (int i = 0; i < openList.Length; i++)
{
if (openList[i]==currentNodeIndex)
{
openList.RemoveAtSwapBack(i);
break;
}
}
closeList.Add(currentNodeIndex);
for (int i = 0; i < neighbourOffsetArray.Length; i++)
{
int2 neighbourOffset = neighbourOffsetArray[i];
int2 neighbourPosition = new int2(currentNode.x+neighbourOffset.x,currentNode.y+neighbourOffset.y);
if (!IsPositionInsideGrid(neighbourPosition,gridSize))
{
//如果不在则继续测试
continue;
}
int neighbourNodeIndex = CalculateIndex(neighbourPosition.x,neighbourPosition.y,gridSize.x);
if (closeList.Contains(neighbourNodeIndex))
{
//该节点已被搜寻过
continue;
}
PathNode neighbourNode = pathNodeArray[neighbourNodeIndex];
if (!neighbourNode.isWalkble)
{
//如果不通过
continue;
}
int2 currentNodePosition = new int2(currentNode.x,currentNode.y);
int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNodePosition,neighbourPosition);
if (tentativeGCost path =CalculatePath(pathNodeArray,endNode);
foreach (var pathPosition in path)
{
Debug.Log(pathPosition);
}
path.Dispose();
}
//清除高性能数组
pathNodeArray.Dispose();
neighbourOffsetArray.Dispose();
openList.Dispose();
closeList.Dispose();
}
private NativeList CalculatePath(NativeArray pathNodeArray,PathNode endNode)
{
if (endNode.cameFromNodeIndex==-1)
{
//没有找到
return new NativeList(Allocator.Temp);
}
else
{
//找到路径
NativeList path = new NativeList(Allocator.Temp);
path.Add(new int2(endNode.x,endNode.y));
PathNode currentNode = endNode;
while (currentNode.cameFromNodeIndex!=-1)
{
PathNode cameFromNode = pathNodeArray[currentNode.cameFromNodeIndex];
path.Add(new int2(cameFromNode.x,cameFromNode.y));
currentNode = cameFromNode;
}
return path;
}
}
private bool IsPositionInsideGrid(int2 gridPostion,int2 gridSize)
{
return
gridPostion.x >= 0 &&
gridPostion.y >= 0 &&
gridPostion.x < gridSize.x &&
gridPostion.y < gridSize.y;
}
//计算对角线权重
private int CalculateDistanceCost(int2 aPosition,int2 bPosition)
{
int xDistance = math.abs(aPosition.x-bPosition.x);
int yDistance = math.abs(aPosition.y-bPosition.y);
int remaining = math.abs(xDistance-yDistance);
return MOVE_DIAGONAL_COST * math.min(xDistance,yDistance)+MOVE_STRAIGHT_COST*remaining;
}
private int GetLowestCostFNodeIndex(NativeList openList,NativeArray pathNodeArray)
{
PathNode lowestCostPathNode = pathNodeArray[openList[0]];
for (int i = 1; i < openList.Length; i++)
{
PathNode testPathNode = pathNodeArray[openList[i]];
if (testPathNode.fCost
2、unityDOTS方案的A*寻路,需要先配置DOTS环境
用途:解决性能瓶颈,充分利用设备的计算性能
特点:快!(但目前估计项目组都不会用)
这篇代码能给在学DOTS的人不少帮助
代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using CodeMonkey.Utils;
using UnityEngine;
using Unity.Mathematics;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using Unity.VisualScripting;
public class Pathfinding1 : MonoBehaviour
{
private const int MOVE_STRAIGHT_COST = 10;
private const int MOVE_DIAGONAL_COST = 14;
private void Start()
{
// FindPath(new int2(0,0),new int2(3,1));
FunctionPeriodic.Create(() =>
{
float startTime = Time.realtimeSinceStartup;
int findPathJobCount = 5;
NativeArray jobHandleArray = new NativeArray(findPathJobCount, Allocator.Temp);
for (int i = 0; i < findPathJobCount; i++)
{
FindPathJob findPathJob = new FindPathJob
{
startPosition =new int2(0,0),//起点
endPosition = new int2(19,19)//终点
};
//findPathJob.Run();//主线程
//findPathJob.Schedule();//并发多线程
jobHandleArray[i]=findPathJob.Schedule();
}
JobHandle.CompleteAll(jobHandleArray);
jobHandleArray.Dispose();
Debug.Log("时间"+(Time.realtimeSinceStartup-startTime)*1000f);
},1f);
}
//建立多线程
// [BurstCompile]//开启DOTSBurst编译器
private struct FindPathJob:IJob
{
// private IJob _jobImplementation;
public int2 startPosition;
public int2 endPosition;
public void Execute()
{
//设置建立网格大小
int2 gridSize = new int2(20, 20); //建立一个4*4的网格
//建立高性能数组
NativeArray pathNodeArray = new NativeArray(gridSize.x * gridSize.y, Allocator.Temp);
for (int x = 0; x < gridSize.x; x++)
{
for (int y = 0; y < gridSize.y; y++)
{
//创建一个新节点
//设置起始点
PathNode pathNode = new PathNode();
pathNode.x = x;
pathNode.y = y;
pathNode.index = CalculateIndex(x,y,gridSize.x);
pathNode.gCost = int.MaxValue;
pathNode.hCost = CalculateDistanceCost(new int2(x,y),endPosition);
pathNode.CalculateFCost();
pathNode.isWalkble = true;
pathNode.cameFromNodeIndex = -1;
pathNodeArray[pathNode.index] = pathNode;
}
}
// //阻挡节点
// {
// PathNode walkablePathNode = pathNodeArray[CalculateIndex(1,0,gridSize.x)];
// walkablePathNode.SetIsWalkable(false);
// pathNodeArray[CalculateIndex(1, 0, gridSize.x)] = walkablePathNode;
//
// walkablePathNode = pathNodeArray[CalculateIndex(1,1,gridSize.x)];
// walkablePathNode.SetIsWalkable(false);
// pathNodeArray[CalculateIndex(1, 1, gridSize.x)] = walkablePathNode;
//
// walkablePathNode = pathNodeArray[CalculateIndex(1,2,gridSize.x)];
// walkablePathNode.SetIsWalkable(false);
// pathNodeArray[CalculateIndex(1, 2, gridSize.x)] = walkablePathNode;
//
// }
//临近节点的偏移数组
NativeArray neighbourOffsetArray = new NativeArray(8, Allocator.Temp);
neighbourOffsetArray[0] = new int2(-1, 0);
neighbourOffsetArray[1] = new int2(1, 0);
neighbourOffsetArray[2] = new int2(0, 1);
neighbourOffsetArray[3] = new int2(0, -1);
neighbourOffsetArray[4] = new int2(-1, -1);
neighbourOffsetArray[5] = new int2(-1, 1);
neighbourOffsetArray[6] = new int2(1, -1);
neighbourOffsetArray[7] = new int2(1, 1);
int endNodeIndex = CalculateIndex(endPosition.x,endPosition.y,gridSize.x);
PathNode startNode = pathNodeArray[CalculateIndex(startPosition.x,startPosition.y,gridSize.x)];
startNode.gCost = 0;
startNode.CalculateFCost();
pathNodeArray[startNode.index] = startNode;
NativeList openList = new NativeList(Allocator.Temp);
NativeList closeList = new NativeList(Allocator.Temp);
openList.Add(startNode.index);
while (openList.Length>0)
{
int currentNodeIndex = GetLowestCostFNodeIndex(openList,pathNodeArray);
PathNode currentNode = pathNodeArray[currentNodeIndex];
if (currentNodeIndex==endNodeIndex)
{
//到达了目的地
break;//打破循环
}
//如果没有到达目的地的
for (int i = 0; i < openList.Length; i++)
{
if (openList[i]==currentNodeIndex)
{
openList.RemoveAtSwapBack(i);
break;
}
}
closeList.Add(currentNodeIndex);
for (int i = 0; i < neighbourOffsetArray.Length; i++)
{
int2 neighbourOffset = neighbourOffsetArray[i];
int2 neighbourPosition = new int2(currentNode.x+neighbourOffset.x,currentNode.y+neighbourOffset.y);
if (!IsPositionInsideGrid(neighbourPosition,gridSize))
{
//如果不在则继续测试
continue;
}
int neighbourNodeIndex = CalculateIndex(neighbourPosition.x,neighbourPosition.y,gridSize.x);
if (closeList.Contains(neighbourNodeIndex))
{
//该节点已被搜寻过
continue;
}
PathNode neighbourNode = pathNodeArray[neighbourNodeIndex];
if (!neighbourNode.isWalkble)
{
//如果不通过
continue;
}
int2 currentNodePosition = new int2(currentNode.x,currentNode.y);
int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNodePosition,neighbourPosition);
if (tentativeGCost path =CalculatePath(pathNodeArray,endNode);
foreach (var pathPosition in path)
{
Debug.Log(pathPosition);
}
path.Dispose();
}
//清除高性能数组
pathNodeArray.Dispose();
neighbourOffsetArray.Dispose();
openList.Dispose();
closeList.Dispose();
}
private NativeList CalculatePath(NativeArray pathNodeArray,PathNode endNode)
{
if (endNode.cameFromNodeIndex==-1)
{
//没有找到
return new NativeList(Allocator.Temp);
}
else
{
//找到路径
NativeList path = new NativeList(Allocator.Temp);
path.Add(new int2(endNode.x,endNode.y));
PathNode currentNode = endNode;
while (currentNode.cameFromNodeIndex!=-1)
{
PathNode cameFromNode = pathNodeArray[currentNode.cameFromNodeIndex];
path.Add(new int2(cameFromNode.x,cameFromNode.y));
currentNode = cameFromNode;
}
return path;
}
}
private bool IsPositionInsideGrid(int2 gridPostion,int2 gridSize)
{
return
gridPostion.x >= 0 &&
gridPostion.y >= 0 &&
gridPostion.x < gridSize.x &&
gridPostion.y < gridSize.y;
}
//计算对角线权重
private int CalculateDistanceCost(int2 aPosition,int2 bPosition)
{
int xDistance = math.abs(aPosition.x-bPosition.x);
int yDistance = math.abs(aPosition.y-bPosition.y);
int remaining = math.abs(xDistance-yDistance);
return MOVE_DIAGONAL_COST * math.min(xDistance,yDistance)+MOVE_STRAIGHT_COST*remaining;
}
private int GetLowestCostFNodeIndex(NativeList openList,NativeArray pathNodeArray)
{
PathNode lowestCostPathNode = pathNodeArray[openList[0]];
for (int i = 1; i < openList.Length; i++)
{
PathNode testPathNode = pathNodeArray[openList[i]];
if (testPathNode.fCost