我一直在关注一些指南,并使用A *算法组合了一个探路者。代码队列单元进入寻路队列并将单元移向目标。这样可以正常工作,但它仅在初始化时占据目标的位置,如果我在单元周围移动,则只走向该初始位置并停止。
我想要的是让他们能够在玩家移动时跟随玩家,并像任何僵尸游戏一样选择最适合玩家的路径。
我想过几种方法可以做到这一点,但我不确定当有数百个僵尸时,处理能力会如何反应。每次目标移动并重新初始化它们时,我都可以将所有寻路单位出列,但我不知道这样做有多好。
如果有人能给我一个很好的方法,那就太好了。这里有很多代码,但无论如何我会提供它作为我正在做的事情的参考。
PathRequestManager.cs:
public class PathRequestManager : MonoBehaviour {
Queue<PathRequest> pathRequestQueue = new Queue<PathRequest>();
PathRequest currentPathRequest;
static PathRequestManager instance;
Pathfinding pathfinding;
bool isProcessingPath;
void Awake() {
instance = this;
pathfinding = GetComponent<Pathfinding>();
}
public static void RequestPath(Vector3 pathStart, Vector3 pathEnd, Action<Vector3[], bool> callback) {
PathRequest newRequest = new PathRequest(pathStart,pathEnd,callback);
instance.pathRequestQueue.Enqueue(newRequest);
instance.TryProcessNext();
}
void TryProcessNext() {
if (!isProcessingPath && pathRequestQueue.Count > 0) {
currentPathRequest = pathRequestQueue.Dequeue();
isProcessingPath = true;
pathfinding.StartFindPath(currentPathRequest.pathStart, currentPathRequest.pathEnd);
}
}
public void FinishedProcessingPath(Vector3[] path, bool success) {
currentPathRequest.callback(path,success);
isProcessingPath = false;
TryProcessNext();
}
struct PathRequest {
public Vector3 pathStart;
public Vector3 pathEnd;
public Action<Vector3[], bool> callback;
public PathRequest(Vector3 _start, Vector3 _end, Action<Vector3[], bool> _callback) {
pathStart = _start;
pathEnd = _end;
callback = _callback;
}
}
PathFinding.cs:
public class Pathfinding : MonoBehaviour {
PathRequestManager requestManager;
Grid grid;
void Awake() {
requestManager = GetComponent<PathRequestManager>();
grid = GetComponent<Grid>();
}
public void StartFindPath(Vector3 startPos, Vector3 targetPos) {
StartCoroutine(FindPath(startPos,targetPos));
}
IEnumerator FindPath(Vector3 startPos, Vector3 targetPos) {
Stopwatch sw = new Stopwatch();
sw.Start();
Vector3[] waypoints = new Vector3[0];
bool pathSuccess = false;
Node startNode = grid.NodeFromWorldPoint(startPos);
Node targetNode = grid.NodeFromWorldPoint(targetPos);
if (startNode.walkable && targetNode.walkable) {
Heap<Node> openSet = new Heap<Node>(grid.MaxSize);
HashSet<Node> closedSet = new HashSet<Node>();
openSet.Add(startNode);
while (openSet.Count > 0) {
Node currentNode = openSet.RemoveFirst();
closedSet.Add(currentNode);
if (currentNode == targetNode) {
sw.Stop();
print ("Path found: " + sw.ElapsedMilliseconds + " ms");
pathSuccess = true;
break;
}
foreach (Node neighbour in grid.GetNeighbours(currentNode)) {
if (!neighbour.walkable || closedSet.Contains(neighbour)) {
continue;
}
int newMovementCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour);
if (newMovementCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) {
neighbour.gCost = newMovementCostToNeighbour;
neighbour.hCost = GetDistance(neighbour, targetNode);
neighbour.parent = currentNode;
if (!openSet.Contains(neighbour))
openSet.Add(neighbour);
}
}
}
}
yield return null;
if (pathSuccess) {
waypoints = RetracePath(startNode,targetNode);
}
requestManager.FinishedProcessingPath(waypoints,pathSuccess);
}
Vector3[] RetracePath(Node startNode, Node endNode) {
List<Node> path = new List<Node>();
Node currentNode = endNode;
while (currentNode != startNode) {
path.Add(currentNode);
currentNode = currentNode.parent;
}
Vector3[] waypoints = SimplifyPath(path);
Array.Reverse(waypoints);
return waypoints;
}
Vector3[] SimplifyPath(List<Node> path) {
List<Vector3> waypoints = new List<Vector3>();
Vector2 directionOld = Vector2.zero;
for (int i = 1; i < path.Count; i ++) {
Vector2 directionNew = new Vector2(path[i-1].gridX - path[i].gridX,path[i-1].gridY - path[i].gridY);
if (directionNew != directionOld) {
waypoints.Add(path[i].worldPosition);
}
directionOld = directionNew;
}
return waypoints.ToArray();
}
int GetDistance(Node nodeA, Node nodeB) {
int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX);
int dstY = Mathf.Abs(nodeA.gridY - nodeB.gridY);
if (dstX > dstY)
return 14*dstY + 10* (dstX-dstY);
return 14*dstX + 10 * (dstY-dstX);
}
}