A *使用队列

时间:2015-10-15 18:20:40

标签: c# unity3d path-finding a-star

我一直在关注一些指南,并使用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);
}


}

0 个答案:

没有答案