Beispiel #1
0
        /// <summary>
        /// Find a path from the start polygon to the end polygon.
        /// -If the end polygon can't be reached, the last polygon will be nearest the end polygon
        /// -If the path array is too small, it will be filled as far as possible 
        /// -start and end positions are used to calculate traversal costs
        /// </summary>
        /// <param name="startPt">The start point.</param>
        /// <param name="endPt">The end point.</param>
        /// <param name="filter">A filter for the navmesh data.</param>
        /// <param name="path">The path of polygon references</param>
        /// <returns>True, if path found. False, if otherwise.</returns>
        public bool FindPath(ref NavPoint startPt, ref NavPoint endPt, NavQueryFilter filter, Path path)
        {
            //reset path of polygons
            path.Clear();

            NavPolyId startRef = startPt.Polygon;
            Vector3 startPos = startPt.Position;
            NavPolyId endRef = endPt.Polygon;
            Vector3 endPos = endPt.Position;

            if (startRef == NavPolyId.Null || endRef == NavPolyId.Null)
                return false;

            //validate input
            if (!nav.IsValidPolyRef(startRef) || !nav.IsValidPolyRef(endRef))
                return false;

            //special case: both start and end are in the same polygon
            if (startRef == endRef)
            {
                path.Add(startRef);
                return true;
            }

            nodePool.Clear();
            openList.Clear();

            //initial node is located at the starting position
            NavNode startNode = nodePool.GetNode(startRef);
            startNode.Position = startPos;
            startNode.ParentIndex = 0;
            startNode.PolyCost = 0;
            startNode.TotalCost = (startPos - endPos).Length() * HeuristicScale;
            startNode.Id = startRef;
            startNode.Flags = NodeFlags.Open;
            openList.Push(startNode);

            NavNode lastBestNode = startNode;
            float lastBestTotalCost = startNode.TotalCost;

            while (openList.Count > 0)
            {
                //remove node from open list and put it in closed list
                NavNode bestNode = openList.Pop();
                SetNodeFlagClosed(ref bestNode);

                //reached the goal. stop searching
                if (bestNode.Id == endRef)
                {
                    lastBestNode = bestNode;
                    break;
                }

                //get current poly and tile
                NavPolyId bestRef = bestNode.Id;
                NavTile bestTile;
                NavPoly bestPoly;
                nav.TryGetTileAndPolyByRefUnsafe(bestRef, out bestTile, out bestPoly);

                //get parent poly and tile
                NavPolyId parentRef = NavPolyId.Null;
                NavTile parentTile = null;
                NavPoly parentPoly = null;
                if (bestNode.ParentIndex != 0)
                    parentRef = nodePool.GetNodeAtIdx(bestNode.ParentIndex).Id;
                if (parentRef != NavPolyId.Null)
                    nav.TryGetTileAndPolyByRefUnsafe(parentRef, out parentTile, out parentPoly);

                //examine neighbors
                foreach (Link link in bestPoly.Links)
                {
                    NavPolyId neighborRef = link.Reference;

                    //skip invalid ids and do not expand back to where we came from
                    if (neighborRef == NavPolyId.Null || neighborRef == parentRef)
                        continue;

                    //get neighbor poly and tile
                    NavTile neighborTile;
                    NavPoly neighborPoly;
                    nav.TryGetTileAndPolyByRefUnsafe(neighborRef, out neighborTile, out neighborPoly);

                    NavNode neighborNode = nodePool.GetNode(neighborRef);
                    if (neighborNode == null)
                        continue;

                    //if node is visited the first time, calculate node position
                    if (neighborNode.Flags == 0)
                    {
                        GetEdgeMidPoint(bestRef, bestPoly, bestTile, neighborRef, neighborPoly, neighborTile, ref neighborNode.Position);
                    }

                    //calculate cost and heuristic
                    float cost = 0;
                    float heuristic = 0;

                    //special case for last node
                    if (neighborRef == endRef)
                    {
                        //cost
                        float curCost = filter.GetCost(bestNode.Position, neighborNode.Position,
                            parentRef, parentTile, parentPoly,
                            bestRef, bestTile, bestPoly,
                            neighborRef, neighborTile, neighborPoly);

                        float endCost = filter.GetCost(neighborNode.Position, endPos,
                            bestRef, bestTile, bestPoly,
                            neighborRef, neighborTile, neighborPoly,
                            NavPolyId.Null, null, null);

                        cost = bestNode.PolyCost + curCost + endCost;
                        heuristic = 0;
                    }
                    else
                    {
                        //cost
                        float curCost = filter.GetCost(bestNode.Position, neighborNode.Position,
                            parentRef, parentTile, parentPoly,
                            bestRef, bestTile, bestPoly,
                            neighborRef, neighborTile, neighborPoly);

                        cost = bestNode.PolyCost + curCost;
                        heuristic = (neighborNode.Position - endPos).Length() * HeuristicScale;
                    }

                    float total = cost + heuristic;

                    //the node is already in open list and new result is worse, skip
                    if (IsInOpenList(neighborNode) && total >= neighborNode.TotalCost)
                        continue;

                    //the node is already visited and processesd, and the new result is worse, skip
                    if (IsInClosedList(neighborNode) && total >= neighborNode.TotalCost)
                        continue;

                    //add or update the node
                    neighborNode.ParentIndex = nodePool.GetNodeIdx(bestNode);
                    neighborNode.Id = neighborRef;
                    neighborNode.Flags = RemoveNodeFlagClosed(neighborNode);
                    neighborNode.PolyCost = cost;
                    neighborNode.TotalCost = total;

                    if (IsInOpenList(neighborNode))
                    {
                        //already in open, update node location
                        openList.Modify(neighborNode);
                    }
                    else
                    {
                        //put the node in the open list
                        SetNodeFlagOpen(ref neighborNode);
                        openList.Push(neighborNode);
                    }

                    //update nearest node to target so far
                    if (heuristic < lastBestTotalCost)
                    {
                        lastBestTotalCost = heuristic;
                        lastBestNode = neighborNode;
                    }
                }
            }

            //save path
            NavNode node = lastBestNode;
            do
            {
                path.Add(node.Id);
                node = nodePool.GetNodeAtIdx(node.ParentIndex);
            }
            while (node != null);

            //reverse the path since it's backwards
            path.Reverse();

            return true;
        }
Beispiel #2
0
        public bool Raycast(ref NavPoint startPoint, ref Vector3 endPos, NavPolyId prevRef, RaycastOptions options, out RaycastHit hit, Path hitPath)
        {
            hit = new RaycastHit();

            if (hitPath != null)
                hitPath.Clear();

            //validate input
            if (startPoint.Polygon == NavPolyId.Null || !nav.IsValidPolyRef(startPoint.Polygon))
                return false;

            if (prevRef != NavPolyId.Null && !nav.IsValidPolyRef(prevRef))
                return false;

            Vector3[] verts = new Vector3[PathfindingCommon.VERTS_PER_POLYGON];

            NavTile prevTile, curTile, nextTile;
            NavPoly prevPoly, curPoly, nextPoly;

            NavPolyId curRef = startPoint.Polygon;

            nav.TryGetTileAndPolyByRefUnsafe(curRef, out curTile, out curPoly);
            nextTile = prevTile = curTile;
            nextPoly = prevPoly = curPoly;

            if (prevRef != NavPolyId.Null)
                nav.TryGetTileAndPolyByRefUnsafe(prevRef, out prevTile, out prevPoly);

            while (curRef != NavPolyId.Null)
            {
                //collect vertices
                int nv = 0;
                for (int i = 0; i < curPoly.VertCount; i++)
                {
                    verts[nv] = curTile.Verts[curPoly.Verts[i]];
                    nv++;
                }

                float tmin, tmax;
                int segMin, segMax;
                if (!Intersection.SegmentPoly2D(startPoint.Position, endPos, verts, nv, out tmin, out tmax, out segMin, out segMax))
                {
                    //could not hit the polygon, keep the old t and report hit
                    return true;
                }

                hit.EdgeIndex = segMax;

                //keep track of furthest t so far
                if (tmax > hit.T)
                    hit.T = tmax;

                //store visited polygons
                if (hitPath != null)
                    hitPath.Add(curRef);

                //ray end is completely inside the polygon
                if (segMax == -1)
                {
                    hit.T = float.MaxValue;

                    return true;
                }

                //follow neighbors
                NavPolyId nextRef = NavPolyId.Null;

                foreach (Link link in curPoly.Links)
                {
                    //find link which contains the edge
                    if (link.Edge != segMax)
                        continue;

                    //get pointer to the next polygon
                    nav.TryGetTileAndPolyByRefUnsafe(link.Reference, out nextTile, out nextPoly);

                    //skip off-mesh connection
                    if (nextPoly.PolyType == NavPolyType.OffMeshConnection)
                        continue;

                    //TODO QueryFilter

                    //if the link is internal, just return the ref
                    if (link.Side == BoundarySide.Internal)
                    {
                        nextRef = link.Reference;
                        break;
                    }

                    //if the link is at the tile boundary

                    //check if the link spans the whole edge and accept
                    if (link.BMin == 0 && link.BMax == 255)
                    {
                        nextRef = link.Reference;
                        break;
                    }

                    //check for partial edge links
                    int v0 = curPoly.Verts[link.Edge];
                    int v1 = curPoly.Verts[(link.Edge + 1) % curPoly.VertCount];
                    Vector3 left = curTile.Verts[v0];
                    Vector3 right = curTile.Verts[v1];

                    //check that the intersection lies inside the link portal
                    if (link.Side == BoundarySide.PlusX || link.Side == BoundarySide.MinusX)
                    {
                        //calculate link size
                        float s = 1.0f / 255.0f;
                        float lmin = left.Z + (right.Z - left.Z) * (link.BMin * s);
                        float lmax = left.Z + (right.Z - left.Z) * (link.BMax * s);
                        if (lmin > lmax)
                        {
                            //swap
                            float temp = lmin;
                            lmin = lmax;
                            lmax = temp;
                        }

                        //find z intersection
                        float z = startPoint.Position.Z + (endPos.Z - startPoint.Position.Z) * tmax;
                        if (z >= lmin && z <= lmax)
                        {
                            nextRef = link.Reference;
                            break;
                        }
                    }
                    else if (link.Side == BoundarySide.PlusZ || link.Side == BoundarySide.MinusZ)
                    {
                        //calculate link size
                        float s = 1.0f / 255.0f;
                        float lmin = left.X + (right.X - left.X) * (link.BMin * s);
                        float lmax = left.X + (right.X - left.X) * (link.BMax * s);
                        if (lmin > lmax)
                        {
                            //swap
                            float temp = lmin;
                            lmin = lmax;
                            lmax = temp;
                        }

                        //find x intersection
                        float x = startPoint.Position.X + (endPos.X - startPoint.Position.X) * tmax;
                        if (x >= lmin && x <= lmax)
                        {
                            nextRef = link.Reference;
                            break;
                        }
                    }
                }

                if ((options & RaycastOptions.UseCosts) != 0)
                {
                    //TODO add cost
                }

                if (nextRef == NavPolyId.Null)
                {
                    //no neighbor, we hit a wall

                    //calculate hit normal
                    int a = segMax;
                    int b = (segMax + 1) < nv ? segMax + 1 : 0;
                    Vector3 va = verts[a];
                    Vector3 vb = verts[b];
                    float dx = vb.X - va.X;
                    float dz = vb.Z - va.Z;
                    hit.Normal = new Vector3(dz, 0, dx);
                    hit.Normal.Normalize();
                    return true;
                }

                //no hit, advance to neighbor polygon
                prevRef = curRef;
                curRef = nextRef;
                prevTile = curTile;
                curTile = nextTile;
                prevPoly = curPoly;
                curPoly = nextPoly;
            }

            return true;
        }
Beispiel #3
0
        /// <summary>
        /// Save the sliced path 
        /// </summary>
        /// <param name="path">The path in terms of polygon references</param>
        /// <param name="pathCount">The path length</param>
        /// <param name="maxPath">The maximum path length allowed</param>
        /// <returns>True if the path is saved, false if not</returns>
        public bool FinalizeSlicedFindPath(Path path)
        {
            path.Clear();

            if (query.Status == false)
            {
                query = new QueryData();
                return false;
            }

            int n = 0;

            if (query.Start.Polygon == query.End.Polygon)
            {
                //special case: the search starts and ends at the same poly
                path.Add(query.Start.Polygon);
            }
            else
            {
                //reverse the path
                NavNode prev = null;
                NavNode node = query.LastBestNode;
                NodeFlags prevRay = 0;
                do
                {
                    NavNode next = nodePool.GetNodeAtIdx(node.ParentIndex);
                    node.ParentIndex = nodePool.GetNodeIdx(prev);
                    prev = node;
                    NodeFlags nextRay = node.Flags & NodeFlags.ParentDetached;
                    node.Flags = (node.Flags & ~NodeFlags.ParentDetached) | prevRay;
                    prevRay = nextRay;
                    node = next;
                }
                while (node != null);

                //store path
                node = prev;
                do
                {
                    NavNode next = nodePool.GetNodeAtIdx(node.ParentIndex);
                    if ((node.Flags & NodeFlags.ParentDetached) != 0)
                    {
                        RaycastHit hit;
                        Path m = new Path();
                        NavPoint startPoint = new NavPoint(node.Id, node.Position);
                        bool result = Raycast(ref startPoint, ref next.Position, RaycastOptions.None, out hit, m);
                        path.AppendPath(m);

                        if (path[path.Count - 1] == next.Id)
                            path.RemoveAt(path.Count - 1);
                    }
                    else
                    {
                        path.Add(node.Id);
                    }

                    node = next;
                }
                while (node != null);
            }

            //reset query
            query = new QueryData();

            return true;
        }
Beispiel #4
0
        /// <summary>
        /// Change the move requests for all the agents
        /// </summary>
        public void UpdateMoveRequest()
        {
            const int PATH_MAX_AGENTS = 8;
            Agent[] queue = new Agent[PATH_MAX_AGENTS];
            int numQueue = 0;
            Status status;

            //fire off new requests
            for (int i = 0; i < maxAgents; i++)
            {
                if (!agents[i].IsActive)
                    continue;
                if (agents[i].State == AgentState.Invalid)
                    continue;
                if (agents[i].TargetState == TargetState.None || agents[i].TargetState == TargetState.Velocity)
                    continue;

                if (agents[i].TargetState == TargetState.Requesting)
                {
                    Path path = agents[i].Corridor.NavPath;

                    Vector3 reqPos = new Vector3();
                    Path reqPath = new Path();

                    //quick search towards the goal
                    const int MAX_ITER = 20;
                    NavPoint startPoint = new NavPoint(path[0], agents[i].Position);
                    NavPoint endPoint = new NavPoint(agents[i].TargetRef, agents[i].TargetPosition);
                    navQuery.InitSlicedFindPath(ref startPoint, ref endPoint, navQueryFilter, FindPathOptions.None);
                    int tempInt = 0;
                    navQuery.UpdateSlicedFindPath(MAX_ITER, ref tempInt);
                    status = Status.Failure;
                    if (agents[i].TargetReplan)
                    {
                        //try to use an existing steady path during replan if possible
                        status = navQuery.FinalizedSlicedPathPartial(path, reqPath).ToStatus();
                    }
                    else
                    {
                        //try to move towards the target when the goal changes
                        status = navQuery.FinalizeSlicedFindPath(reqPath).ToStatus();
                    }

                    if (status != Status.Failure && reqPath.Count > 0)
                    {
                        //in progress or succeed
                        if (reqPath[reqPath.Count - 1] != agents[i].TargetRef)
                        {
                            //partial path, constrain target position in last polygon
                            bool tempBool;
                            status = navQuery.ClosestPointOnPoly(reqPath[reqPath.Count - 1], agents[i].TargetPosition, out reqPos, out tempBool).ToStatus();
                            if (status == Status.Failure)
                                reqPath.Clear();
                        }
                        else
                        {
                            reqPos = agents[i].TargetPosition;
                        }
                    }
                    else
                    {
                        reqPath.Clear();
                    }

                    if (reqPath.Count == 0)
                    {
                        //could not find path, start the request from the current location
                        reqPos = agents[i].Position;
                        reqPath.Add(path[0]);
                    }

                    agents[i].Corridor.SetCorridor(reqPos, reqPath);
                    agents[i].Boundary.Reset();
                    agents[i].IsPartial = false;

                    if (reqPath[reqPath.Count - 1] == agents[i].TargetRef)
                    {
                        agents[i].TargetState = TargetState.Valid;
                        agents[i].TargetReplanTime = 0.0f;
                    }
                    else
                    {
                        //the path is longer or potentially unreachable, full plan
                        agents[i].TargetState = TargetState.WaitingForQueue;
                    }
                }

                if (agents[i].TargetState == TargetState.WaitingForQueue)
                {
                    numQueue = AddToPathQueue(agents[i], queue, numQueue, PATH_MAX_AGENTS);
                }
            }

            for (int i = 0; i < numQueue; i++)
            {
                queue[i].TargetPathQueryIndex = pathq.Request(new NavPoint(queue[i].Corridor.GetLastPoly(), queue[i].Corridor.Target), new NavPoint(queue[i].TargetRef, queue[i].TargetPosition));
                if (queue[i].TargetPathQueryIndex != PathQueue.Invalid)
                    queue[i].TargetState = TargetState.WaitingForPath;
            }

            //update requests
            pathq.Update(MaxItersPerUpdate);

            //process path results
            for (int i = 0; i < maxAgents; i++)
            {
                if (!agents[i].IsActive)
                    continue;
                if (agents[i].TargetState == TargetState.None || agents[i].TargetState == TargetState.Velocity)
                    continue;

                if (agents[i].TargetState == TargetState.WaitingForPath)
                {
                    //poll path queue
                    status = pathq.GetRequestStatus(agents[i].TargetPathQueryIndex);
                    if (status == Status.Failure)
                    {
                        //path find failed, retry if the target location is still valid
                        agents[i].TargetPathQueryIndex = PathQueue.Invalid;
                        if (agents[i].TargetRef != NavPolyId.Null)
                            agents[i].TargetState = TargetState.Requesting;
                        else
                            agents[i].TargetState = TargetState.Failed;
                        agents[i].TargetReplanTime = 0.0f;
                    }
                    else if (status == Status.Success)
                    {
                        Path path = agents[i].Corridor.NavPath;

                        //apply results
                        Vector3 targetPos = new Vector3();
                        targetPos = agents[i].TargetPosition;

                        Path res;
                        bool valid = true;
                        status = pathq.GetPathResult(agents[i].TargetPathQueryIndex, out res).ToStatus();
                        if (status == Status.Failure || res.Count == 0)
                            valid = false;

                        //Merge result and existing path
                        if (valid && path[path.Count - 1] != res[0])
                            valid = false;

                        if (valid)
                        {
                            //put the old path infront of the old path
                            if (path.Count > 1)
                            {
                                //make space for the old path
                                //if ((path.Count - 1) + nres > maxPathResult)
                                    //nres = maxPathResult - (npath - 1);

                                for (int j = 0; j < res.Count; j++)
                                    res[path.Count - 1 + j] = res[j];

                                //copy old path in the beginning
                                for (int j = 0; j < path.Count - 1; j++)
                                    res.Add(path[j]);

                                //remove trackbacks
                                res.RemoveTrackbacks();
                            }

                            //check for partial path
                            if (res[res.Count - 1] != agents[i].TargetRef)
                            {
                                //partial path, constrain target position inside the last polygon
                                Vector3 nearest;
                                bool tempBool = false;
                                status = navQuery.ClosestPointOnPoly(res[res.Count - 1], targetPos, out nearest, out tempBool).ToStatus();
                                if (status == Status.Success)
                                    targetPos = nearest;
                                else
                                    valid = false;
                            }
                        }

                        if (valid)
                        {
                            //set current corridor
                            agents[i].Corridor.SetCorridor(targetPos, res);

                            //forced to update boundary
                            agents[i].Boundary.Reset();
                            agents[i].TargetState = TargetState.Valid;
                        }
                        else
                        {
                            //something went wrong
                            agents[i].TargetState = TargetState.Failed;
                        }

                        agents[i].TargetReplanTime = 0.0f;
                    }
                }
            }
        }