Exemple #1
0
 /// <summary>
 /// Finds a random point in a NavMesh within a specified circle.
 /// </summary>
 /// <param name="center">The center point.</param>
 /// <param name="radius">The maximum distance away from the center that the random point can be. If 0, any point on the mesh can be returned.</param>
 /// <returns>A random point within the specified circle.</returns>
 public NavPoint FindRandomPointAroundCircle(NavPoint center, float radius)
 {
     NavPoint result;
     this.FindRandomPointAroundCircle(center, radius, out result);
     return result;
 }
Exemple #2
0
        /// <summary>
        /// Finds a random point in a NavMesh within a specified circle.
        /// </summary>
        /// <param name="center">The center point.</param>
        /// <param name="radius">The maximum distance away from the center that the random point can be. If 0, any point on the mesh can be returned.</param>
        /// <param name="randomPoint">A random point within the specified circle.</param>
        public void FindRandomPointAroundCircle(NavPoint center, float radius, out NavPoint randomPoint)
        {
            //TODO fix state
            if (nav == null || nodePool == null || openList == null)
                throw new InvalidOperationException("Something null");

            //validate input
            if (center.Polygon == PolyId.Null)
                throw new ArgumentOutOfRangeException("startRef", "Null poly reference");

            if (!nav.IsValidPolyRef(center.Polygon))
                throw new ArgumentException("startRef", "Poly reference is not valid for this navmesh");

            MeshTile startTile;
            Poly startPoly;
            nav.TryGetTileAndPolyByRefUnsafe(center.Polygon, out startTile, out startPoly);

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

            Node startNode = nodePool.GetNode(center.Polygon);
            startNode.Pos = center.Position;
            startNode.ParentIdx = 0;
            startNode.cost = 0;
            startNode.total = 0;
            startNode.Id = center.Polygon;
            startNode.Flags = NodeFlags.Open;
            openList.Push(startNode);

            bool doRadiusCheck = radius != 0;
            float radiusSqr = radius * radius;
            float areaSum = 0.0f;

            MeshTile randomTile = null;
            Poly randomPoly = null;
            PolyId randomPolyRef = PolyId.Null;

            while (openList.Count > 0)
            {
                Node bestNode = openList.Pop();
                SetNodeFlagClosed(ref bestNode);

                //get poly and tile
                PolyId bestRef = bestNode.Id;
                MeshTile bestTile;
                Poly bestPoly;
                nav.TryGetTileAndPolyByRefUnsafe(bestRef, out bestTile, out bestPoly);

                //place random locations on ground
                if (bestPoly.PolyType == PolygonType.Ground)
                {
                    //calculate area of polygon
                    float polyArea = 0.0f;
                    float area;
                    for (int j = 2; j < bestPoly.VertCount; j++)
                    {
                        Triangle3.Area2D(ref bestTile.Verts[bestPoly.Verts[0]], ref bestTile.Verts[bestPoly.Verts[j - 1]], ref bestTile.Verts[bestPoly.Verts[j]], out area);
                        polyArea += area;
                    }

                    //choose random polygon weighted by area using resevoir sampling
                    areaSum += polyArea;
                    float u = (float)rand.NextDouble();
                    if (u * areaSum <= polyArea)
                    {
                        randomTile = bestTile;
                        randomPoly = bestPoly;
                        randomPolyRef = bestRef;
                    }
                }

                //get parent poly and tile
                PolyId parentRef = PolyId.Null;
                MeshTile parentTile;
                Poly parentPoly;
                if (bestNode.ParentIdx != 0)
                    parentRef = nodePool.GetNodeAtIdx(bestNode.ParentIdx).Id;
                if (parentRef != PolyId.Null)
                    nav.TryGetTileAndPolyByRefUnsafe(parentRef, out parentTile, out parentPoly);

                for (int i = bestPoly.FirstLink; i != Link.Null; i = bestTile.Links[i].Next)
                {
                    Link link = bestTile.Links[i];
                    PolyId neighbourRef = link.Reference;

                    //skip invalid neighbours and do not follor back to parent
                    if (neighbourRef == PolyId.Null || neighbourRef == parentRef)
                        continue;

                    //expand to neighbour
                    MeshTile neighbourTile;
                    Poly neighbourPoly;
                    nav.TryGetTileAndPolyByRefUnsafe(neighbourRef, out neighbourTile, out neighbourPoly);

                    //find edge and calculate distance to edge
                    Vector3 va = new Vector3();
                    Vector3 vb = new Vector3();
                    if (!GetPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, ref va, ref vb))
                        continue;

                    //if circle isn't touching next polygon, skip it
                    if (doRadiusCheck)
                    {
                        float tseg;
                        float distSqr = Distance.PointToSegment2DSquared(ref center.Position, ref va, ref vb, out tseg);
                        if (distSqr > radiusSqr)
                            continue;
                    }

                    Node neighbourNode = nodePool.GetNode(neighbourRef);
                    if (neighbourNode == null)
                        continue;

                    if (IsInClosedList(neighbourNode))
                        continue;

                    //cost
                    if (neighbourNode.Flags == 0)
                        neighbourNode.Pos = Vector3.Lerp(va, vb, 0.5f);

                    float total = bestNode.total + (bestNode.Pos - neighbourNode.Pos).Length();

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

                    neighbourNode.Id = neighbourRef;
                    neighbourNode.Flags = RemoveNodeFlagClosed(neighbourNode);
                    neighbourNode.ParentIdx = nodePool.GetNodeIdx(bestNode);
                    neighbourNode.total = total;

                    if (IsInOpenList(neighbourNode))
                    {
                        openList.Modify(neighbourNode);
                    }
                    else
                    {
                        neighbourNode.Flags = NodeFlags.Open;
                        openList.Push(neighbourNode);
                    }
                }
            }

            //TODO invalid state.
            if (randomPoly == null)
                throw new InvalidOperationException("Poly null?");

            Vector3 randomPt;
            FindRandomPointOnPoly(randomTile, randomPoly, randomPolyRef, out randomPt);

            randomPoint = new NavPoint(randomPolyRef, randomPt);
        }
Exemple #3
0
        /// <summary>
        /// Finds a random point somewhere in the navigation mesh.
        /// </summary>
        /// <param name="randomPoint">Resulting random point.</param>
        public void FindRandomPoint(out NavPoint randomPoint)
        {
            //TODO we're object-oriented, can prevent this state from ever happening.
            if (nav == null)
                throw new InvalidOperationException("TODO prevent this state from ever occuring");

            //randomly pick one tile
            //assume all tiles cover roughly the same area
            MeshTile tile = null;
            float tsum = 0.0f;

            for (int i = 0; i < nav.TileCount; i++)
            {
                MeshTile t = nav[i];

                if (t == null || t.Header == null)
                    continue;

                //choose random tile using reservoir sampling
                float area = 1.0f;
                tsum += area;
                float u = (float)rand.NextDouble();
                if (u * tsum <= area)
                    tile = t;
            }

            //TODO why?
            if (tile == null)
                throw new InvalidOperationException("No tiles?");

            //randomly pick one polygon weighted by polygon area
            Poly poly = null;
            PolyId polyRef = PolyId.Null;
            PolyId polyBase = nav.GetPolyRefBase(tile);

            float areaSum = 0.0f;
            for (int i = 0; i < tile.Header.PolyCount; i++)
            {
                Poly p = tile.Polys[i];

                //don't return off-mesh connection polygons
                if (p.PolyType != PolygonType.Ground)
                    continue;

                PolyId reference;
                PolyId.SetPolyIndex(ref polyBase, i, out reference);

                //calculate area of polygon
                float polyArea = 0.0f;
                float area;
                for (int j = 2; j < p.VertCount; j++)
                {
                    Triangle3.Area2D(ref tile.Verts[p.Verts[0]], ref tile.Verts[p.Verts[j - 1]], ref tile.Verts[p.Verts[j]], out area);
                    polyArea += area;
                }

                //choose random polygon weighted by area, usig resevoir sampling
                areaSum += polyArea;
                float u = (float)rand.NextDouble();
                if (u * areaSum <= polyArea)
                {
                    poly = p;
                    polyRef = reference;
                }
            }

            //TODO why?
            if (poly == null)
                throw new InvalidOperationException("No polys?");

            //randomRef = polyRef;
            Vector3 randomPt;
            FindRandomPointOnPoly(tile, poly, polyRef, out randomPt);

            randomPoint = new NavPoint(polyRef, randomPt);
        }
Exemple #4
0
        /// <summary>
        /// Move along the NavMeshQuery and update the position
        /// </summary>
        /// <param name="npos">Current position</param>
        /// <param name="navquery">The NavMeshQuery</param>
        /// <returns>True if position changed, false if not</returns>
        public bool MovePosition(Vector3 npos, NavMeshQuery navquery)
        {
            const int MaxVisited = 16;

            Vector3 result = new Vector3();
            List<NavPolyId> visited = new List<NavPolyId>(MaxVisited);
            NavPoint startPoint = new NavPoint(path[0], pos);

            //move along navmesh and update new position
            bool status = navquery.MoveAlongSurface(ref startPoint, ref npos, out result, visited);

            if (status == true)
            {
                MergeCorridorStartMoved(path, visited);

                //adjust the position to stay on top of the navmesh
                float h = pos.Y;
                navquery.GetPolyHeight(path[0], result, ref h);
                result.Y = h;
                pos = result;
                return true;
            }

            return false;
        }
Exemple #5
0
        /// <summary>
        /// Find the nearest poly within a certain range.
        /// </summary>
        /// <param name="center">Center.</param>
        /// <param name="extents">Extents.</param>
        /// <param name="nearestPt">The neareast point.</param>
        public void FindNearestPoly(ref Vector3 center, ref Vector3 extents, out NavPoint nearestPt)
        {
            nearestPt = NavPoint.Null;

            //TODO error state?

            // Get nearby polygons from proximity grid.
            List<PolyId> polys = new List<PolyId>(128);
            if (!QueryPolygons(ref center, ref extents, polys))
                throw new InvalidOperationException("no nearby polys?");

            float nearestDistanceSqr = float.MaxValue;
            for (int i = 0; i < polys.Count; i++)
            {
                PolyId reference = polys[i];
                Vector3 closestPtPoly;
                bool posOverPoly;
                ClosestPointOnPoly(reference, center, out closestPtPoly, out posOverPoly);

                // If a point is directly over a polygon and closer than
                // climb height, favor that instead of straight line nearest point.
                Vector3 diff = center - closestPtPoly;
                float d = 0;
                if (posOverPoly)
                {
                    MeshTile tile;
                    Poly poly;
                    nav.TryGetTileAndPolyByRefUnsafe(polys[i], out tile, out poly);
                    d = Math.Abs(diff.Y) - tile.Header.WalkableClimb;
                    d = d > 0 ? d * d : 0;
                }
                else
                {
                    d = diff.LengthSquared();
                }

                if (d < nearestDistanceSqr)
                {
                    nearestDistanceSqr = d;
                    nearestPt = new NavPoint(reference, closestPtPoly);
                }
            }
        }
Exemple #6
0
 /// <summary>
 /// Finds a random point in a NavMesh connected to a specified point on the same mesh.
 /// </summary>
 /// <param name="connectedTo">The point that the random point will be connected to.</param>
 /// <param name="randomPoint">A random point connected to <c>connectedTo</c>.</param>
 public void FindRandomConnectedPoint(NavPoint connectedTo, out NavPoint randomPoint)
 {
     FindRandomPointAroundCircle(connectedTo, 0, out randomPoint);
 }
Exemple #7
0
        /// <summary>
        /// Request an empty slot in the path queue
        /// </summary>
        /// <param name="start">Start position</param>
        /// <param name="end">End position</param>
        /// <returns>Index of empty slot</returns>
        public int Request(NavPoint start, NavPoint end)
        {
            //find empty slot
            int slot = -1;
            for (int i = 0; i < MaxQueue; i++)
            {
                if (queue[i].Index == 0)
                {
                    slot = i;
                    break;
                }
            }

            //could not find slot
            if (slot == -1)
                return PathQueue.Invalid;

            int index = nextHandle++;
            if (nextHandle == 0) nextHandle++;

            PathQuery q = queue[slot];
            q.Index = index;
            q.Start = start;
            q.End = end;

            q.Status = 0;
            q.PathCount = 0;
            q.KeepAlive = 0;

            queue[slot] = q;

            return index;
        }
Exemple #8
0
        public bool Raycast(NavPoint startPoint, Vector3 endPos, ref float t, ref Vector3 hitNormal, PolyId[] path, ref int pathCount, int maxPath)
        {
            t = 0;
            pathCount = 0;

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

            PolyId curRef = startPoint.Polygon;
            Vector3[] verts = new Vector3[PathfindingCommon.VERTS_PER_POLYGON];
            int n = 0;

            hitNormal = new Vector3(0, 0, 0);

            while (curRef != PolyId.Null)
            {
                //cast ray against current polygon
                MeshTile tile;
                Poly poly;
                nav.TryGetTileAndPolyByRefUnsafe(curRef, out tile, out poly);

                //collect vertices
                int nv = 0;
                for (int i = 0; i < poly.VertCount; i++)
                {
                    verts[nv] = tile.Verts[poly.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
                    pathCount = n;
                    return true;
                }

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

                //store visited polygons
                if (n < maxPath)
                    path[n++] = curRef;

                //ray end is completely inside the polygon
                if (segMax == -1)
                {
                    t = float.MaxValue;
                    pathCount = n;
                    return true;
                }

                //follow neighbours
                PolyId nextRef = PolyId.Null;

                for (int i = poly.FirstLink; i != Link.Null; i = tile.Links[i].Next)
                {
                    Link link = tile.Links[i];

                    //find link which contains the edge
                    if (link.Edge != segMax)
                        continue;

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

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

                    //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 = poly.Verts[link.Edge];
                    int v1 = poly.Verts[(link.Edge + 1) % poly.VertCount];
                    Vector3 left = tile.Verts[v0];
                    Vector3 right = tile.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 (nextRef == PolyId.Null)
                {
                    //no neighbour, 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;
                    hitNormal.X = dz;
                    hitNormal.Y = 0;
                    hitNormal.Z = -dx;
                    hitNormal.Normalize();

                    pathCount = n;
                    return true;
                }

                //no hit, advance to neighbour polygon
                curRef = nextRef;
            }

            pathCount = n;

            return true;
        }
Exemple #9
0
        /// <summary>
        /// Examine polygons in the NavMeshQuery and add polygon edges
        /// </summary>
        /// <param name="reference">The starting polygon reference</param>
        /// <param name="pos">Current position</param>
        /// <param name="collisionQueryRange">Range to query</param>
        /// <param name="navquery">The NavMeshQuery</param>
        public void Update(NavPolyId reference, Vector3 pos, float collisionQueryRange, NavMeshQuery navquery)
        {
            const int MAX_SEGS_PER_POLY = PathfindingCommon.VERTS_PER_POLYGON;

            if (reference == NavPolyId.Null)
            {
                this.center = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
                this.segCount = 0;
                this.numPolys = 0;
                return;
            }

            this.center = pos;

            //first query non-overlapping polygons
            NavPolyId[] tempArray = new NavPolyId[polys.Length];
            NavPoint centerPoint = new NavPoint(reference, pos);
            navquery.FindLocalNeighborhood(ref centerPoint, collisionQueryRange, polys, tempArray, ref numPolys, MaxLocalPolys);

            //secondly, store all polygon edges
            this.segCount = 0;
            Segment[] segs = new Segment[MAX_SEGS_PER_POLY];
            int numSegs = 0;
            for (int j = 0; j < numPolys; j++)
            {
                tempArray = new NavPolyId[segs.Length];
                navquery.GetPolyWallSegments(polys[j], segs, tempArray, ref numSegs, MAX_SEGS_PER_POLY);
                for (int k = 0; k < numSegs; k++)
                {
                    //skip too distant segments
                    float tseg;
                    float distSqr = Distance.PointToSegment2DSquared(ref pos, ref segs[k].Start, ref segs[k].End, out tseg);
                    if (distSqr > collisionQueryRange * collisionQueryRange)
                        continue;
                    AddSegment(distSqr, segs[k]);
                }
            }
        }
Exemple #10
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;
                    }
                }
            }
        }
 public StraightPathVertex(NavPoint point, StraightPathFlags flags)
 {
     Point = point;
     Flags = flags;
 }
Exemple #12
0
        /// <summary>
        /// Use an efficient local visibility search to try to optimize the corridor between the current position and the next.
        /// </summary>
        /// <param name="next">The next postion</param>
        /// <param name="pathOptimizationRange">The range</param>
        /// <param name="navquery">The NavMeshQuery</param>
        public void OptimizePathVisibility(Vector3 next, float pathOptimizationRange, NavMeshQuery navquery)
        {
            //clamp the ray to max distance
            Vector3 goal = next;
            float dist = Vector3Extensions.Distance2D(pos, goal);

            //if too close to the goal, do not try to optimize
            if (dist < 0.01f)
                return;

            dist = Math.Min(dist + 0.01f, pathOptimizationRange);

            //adjust ray length
            Vector3 delta = goal - pos;
            goal = pos + delta * (pathOptimizationRange / dist);

            NavPoint startPoint = new NavPoint(path[0], pos);
            Path raycastPath = new Path();
            RaycastHit hit;

            navquery.Raycast(ref startPoint, ref goal, RaycastOptions.None, out hit, raycastPath);
            if (raycastPath.Count > 1 && hit.T > 0.99f)
            {
                MergeCorridorStartShortcut(raycastPath, raycastPath);
            }
        }
Exemple #13
0
        /// <summary>
        /// Use a local area path search to try to reoptimize this corridor
        /// </summary>
        /// <param name="navquery">The NavMeshQuery</param>
        /// <returns>True if optimized, false if not</returns>
        public bool OptimizePathTopology(NavMeshQuery navquery, NavQueryFilter filter)
        {
            if (path.Count < 3)
                return false;

            const int MaxIter = 32;
            const int MaxRes = 32;

            Path res = new Path();
            int numRes = 0;
            int tempInt = 0;
            NavPoint startPoint = new NavPoint(path[0], pos);
            NavPoint endPoint = new NavPoint(path[path.Count - 1], target);
            navquery.InitSlicedFindPath(ref startPoint, ref endPoint, filter, FindPathOptions.None);
            navquery.UpdateSlicedFindPath(MaxIter, ref tempInt);
            bool status = navquery.FinalizedSlicedPathPartial(path, res);

            if (status == true && numRes > 0)
            {
                MergeCorridorStartShortcut(path, res);
                return true;
            }

            return false;
        }
Exemple #14
0
        /// <summary>
        /// Initialize a sliced path, which is used mostly for crowd pathfinding.
        /// </summary>
        /// <param name="startPoint">The start point.</param>
        /// <param name="endPoint">The end point.</param>
        /// <returns>True if path initialized, false otherwise</returns>
        public bool InitSlicedFindPath(NavPoint startPoint, NavPoint endPoint)
        {
            //validate input
            if (startPoint.Polygon == PolyId.Null || endPoint.Polygon == PolyId.Null)
                return false;

            if (!nav.IsValidPolyRef(startPoint.Polygon) || !nav.IsValidPolyRef(endPoint.Polygon))
                return false;

            if (startPoint.Polygon == endPoint.Polygon)
            {
                query.Status = true;
                return true;
            }

            //init path state
            query = new QueryData();
            query.Status = false;
            query.Start = startPoint;
            query.End = endPoint;

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

            Node startNode = nodePool.GetNode(startPoint.Polygon);
            startNode.Pos = startPoint.Position;
            startNode.ParentIdx = 0;
            startNode.cost = 0;
            startNode.total = (endPoint.Position - startPoint.Position).Length() * H_SCALE;
            startNode.Id = startPoint.Polygon;
            startNode.Flags = NodeFlags.Open;
            openList.Push(startNode);

            query.Status = true;
            query.LastBestNode = startNode;
            query.LastBestNodeCost = startNode.total;

            return query.Status;
        }
Exemple #15
0
        /// <summary>
        /// Initialize a sliced path, which is used mostly for crowd pathfinding.
        /// </summary>
        /// <param name="startPoint">The start point.</param>
        /// <param name="endPoint">The end point.</param>
        /// <param name="filter">A filter for the navigation mesh.</param>
        /// <param name="options">Options for how the path should be found.</param>
        /// <returns>True if path initialized, false otherwise</returns>
        public bool InitSlicedFindPath(ref NavPoint startPoint, ref NavPoint endPoint, NavQueryFilter filter, FindPathOptions options)
        {
            //validate input
            if (startPoint.Polygon == NavPolyId.Null || endPoint.Polygon == NavPolyId.Null)
                return false;

            if (!nav.IsValidPolyRef(startPoint.Polygon) || !nav.IsValidPolyRef(endPoint.Polygon))
                return false;

            if (startPoint.Polygon == endPoint.Polygon)
            {
                query.Status = true;
                return true;
            }

            //init path state
            query = new QueryData();
            query.Status = false;
            query.Start = startPoint;
            query.End = endPoint;

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

            NavNode startNode = nodePool.GetNode(startPoint.Polygon);
            startNode.Position = startPoint.Position;
            startNode.ParentIndex = 0;
            startNode.PolyCost = 0;
            startNode.TotalCost = (endPoint.Position - startPoint.Position).Length() * HeuristicScale;
            startNode.Id = startPoint.Polygon;
            startNode.Flags = NodeFlags.Open;
            openList.Push(startNode);

            query.Status = true;
            query.LastBestNode = startNode;
            query.LastBestNodeCost = startNode.TotalCost;

            return query.Status;
        }
Exemple #16
0
        /// <summary>
        /// This method is optimized for small delta movement and a small number of polygons.
        /// If movement distance is too large, the result will form an incomplete path.
        /// </summary>
        /// <param name="startPoint">The start point.</param>
        /// <param name="endPos">End position</param>
        /// <param name="resultPos">Intermediate point</param>
        /// <param name="visited">Visited polygon references</param>
        /// <returns>True, if point found. False, if otherwise.</returns>
        public bool MoveAlongSurface(NavPoint startPoint, Vector3 endPos, ref Vector3 resultPos, List<PolyId> visited)
        {
            if (nav == null)
                return false;
            if (tinyNodePool == null)
                return false;

            visited.Clear();

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

            int MAX_STACK = 48;
            Queue<Node> nodeQueue = new Queue<Node>(MAX_STACK);

            tinyNodePool.Clear();

            Node startNode = tinyNodePool.GetNode(startPoint.Polygon);
            startNode.ParentIdx = 0;
            startNode.cost = 0;
            startNode.total = 0;
            startNode.Id = startPoint.Polygon;
            startNode.Flags = NodeFlags.Closed;
            nodeQueue.Enqueue(startNode);

            Vector3 bestPos = startPoint.Position;
            float bestDist = float.MaxValue;
            Node bestNode = null;

            //search constraints
            Vector3 searchPos = Vector3.Lerp(startPoint.Position, endPos, 0.5f);
            float searchRad = (startPoint.Position - endPos).Length() / 2.0f + 0.001f;
            float searchRadSqr = searchRad * searchRad;

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

            while (nodeQueue.Count > 0)
            {
                //pop front
                Node curNode = nodeQueue.Dequeue();

                //get poly and tile
                PolyId curRef = curNode.Id;
                MeshTile curTile;
                Poly curPoly;
                nav.TryGetTileAndPolyByRefUnsafe(curRef, out curTile, out curPoly);

                //collect vertices
                int nverts = curPoly.VertCount;
                for (int i = 0; i < nverts; i++)
                    verts[i] = curTile.Verts[curPoly.Verts[i]];

                //if target is inside poly, stop search
                if (Containment.PointInPoly(endPos, verts, nverts))
                {
                    bestNode = curNode;
                    bestPos = endPos;
                    break;
                }

                //find wall edges and find nearest point inside walls
                for (int i = 0, j = curPoly.VertCount - 1; i < curPoly.VertCount; j = i++)
                {
                    //find links to neighbors
                    List<PolyId> neis = new List<PolyId>(8);

                    if ((curPoly.Neis[j] & Link.External) != 0)
                    {
                        //tile border
                        for (int k = curPoly.FirstLink; k != Link.Null; k = curTile.Links[k].Next)
                        {
                            Link link = curTile.Links[k];
                            if (link.Edge == j)
                            {
                                if (link.Reference != PolyId.Null)
                                {
                                    MeshTile neiTile;
                                    Poly neiPoly;
                                    nav.TryGetTileAndPolyByRefUnsafe(link.Reference, out neiTile, out neiPoly);

                                    if (neis.Count < neis.Capacity)
                                        neis.Add(link.Reference);
                                }
                            }
                        }
                    }
                    else if (curPoly.Neis[j] != 0)
                    {
                        int idx = curPoly.Neis[j] - 1;
                        PolyId reference = nav.GetPolyRefBase(curTile);
                        PolyId.SetPolyIndex(ref reference, idx, out reference);
                        neis.Add(reference); //internal edge, encode id
                    }

                    if (neis.Count == 0)
                    {
                        //wall edge, calculate distance
                        float tseg = 0;
                        float distSqr = Distance.PointToSegment2DSquared(ref endPos, ref verts[j], ref verts[i], out tseg);
                        if (distSqr < bestDist)
                        {
                            //update nearest distance
                            bestPos = Vector3.Lerp(verts[j], verts[i], tseg);
                            bestDist = distSqr;
                            bestNode = curNode;
                        }
                    }
                    else
                    {
                        for (int k = 0; k < neis.Count; k++)
                        {
                            //skip if no node can be allocated
                            Node neighbourNode = tinyNodePool.GetNode(neis[k]);
                            if (neighbourNode == null)
                                continue;

                            //skip if already visited
                            if ((neighbourNode.Flags & NodeFlags.Closed) != 0)
                                continue;

                            //skip the link if too far from search constraint
                            float distSqr = Distance.PointToSegment2DSquared(ref searchPos, ref verts[j], ref verts[i]);
                            if (distSqr > searchRadSqr)
                                continue;

                            //mark the node as visited and push to queue
                            if (nodeQueue.Count < MAX_STACK)
                            {
                                neighbourNode.ParentIdx = tinyNodePool.GetNodeIdx(curNode);
                                neighbourNode.Flags |= NodeFlags.Closed;
                                nodeQueue.Enqueue(neighbourNode);
                            }
                        }
                    }
                }
            }

            if ((endPos - bestPos).Length() > 1f)
                return false;

            if (bestNode != null)
            {
                //save the path
                Node node = bestNode;
                do
                {
                    visited.Add(node.Id);
                    if (visited.Count >= visited.Capacity)
                        break;

                    node = tinyNodePool.GetNodeAtIdx(node.ParentIdx);
                }
                while (node != null);

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

            resultPos = bestPos;

            return true;
        }
Exemple #17
0
 public bool Raycast(ref NavPoint startPoint, ref Vector3 endPos, RaycastOptions options, out RaycastHit hit, Path hitPath)
 {
     return Raycast(ref startPoint, ref endPos, NavPolyId.Null, options, out hit, hitPath);
 }
Exemple #18
0
        /// <summary>
        /// Store polygons that are within a certain range from the current polygon
        /// </summary>
        /// <param name="centerPoint">Starting position</param>
        /// <param name="radius">Range to search within</param>
        /// <param name="resultRef">All the polygons within range</param>
        /// <param name="resultParent">Polygon's parents</param>
        /// <param name="resultCount">Number of polygons stored</param>
        /// <param name="maxResult">Maximum number of polygons allowed</param>
        /// <returns>True, unless input is invalid</returns>
        public bool FindLocalNeighbourhood(NavPoint centerPoint, float radius, PolyId[] resultRef, PolyId[] resultParent, ref int resultCount, int maxResult)
        {
            resultCount = 0;

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

            int MAX_STACK = 48;
            Node[] stack = new Node[MAX_STACK];
            int nstack = 0;

            tinyNodePool.Clear();

            Node startNode = tinyNodePool.GetNode(centerPoint.Polygon);
            startNode.ParentIdx = 0;
            startNode.Id = centerPoint.Polygon;
            startNode.Flags = NodeFlags.Closed;
            stack[nstack++] = startNode;

            float radiusSqr = radius * radius;

            Vector3[] pa = new Vector3[PathfindingCommon.VERTS_PER_POLYGON];
            Vector3[] pb = new Vector3[PathfindingCommon.VERTS_PER_POLYGON];

            int n = 0;
            if (n < maxResult)
            {
                resultRef[n] = startNode.Id;
                resultParent[n] = PolyId.Null;
                ++n;
            }

            while (nstack > 0)
            {
                //pop front
                Node curNode = stack[0];
                for (int i = 0; i < nstack - 1; i++)
                    stack[i] = stack[i + 1];
                nstack--;

                //get poly and tile
                PolyId curRef = curNode.Id;
                MeshTile curTile;
                Poly curPoly;
                nav.TryGetTileAndPolyByRefUnsafe(curRef, out curTile, out curPoly);

                for (int i = curPoly.FirstLink; i != Link.Null; i = curTile.Links[i].Next)
                {
                    Link link = curTile.Links[i];
                    PolyId neighbourRef = link.Reference;

                    //skip invalid neighbours
                    if (neighbourRef == PolyId.Null)
                        continue;

                    //skip if cannot allocate more nodes
                    Node neighbourNode = tinyNodePool.GetNode(neighbourRef);
                    if (neighbourNode == null)
                        continue;

                    //skip visited
                    if ((neighbourNode.Flags & NodeFlags.Closed) != 0)
                        continue;

                    //expand to neighbour
                    MeshTile neighbourTile;
                    Poly neighbourPoly;
                    nav.TryGetTileAndPolyByRefUnsafe(neighbourRef, out neighbourTile, out neighbourPoly);

                    //skip off-mesh connections
                    if (neighbourPoly.PolyType == PolygonType.OffMeshConnection)
                        continue;

                    //find edge and calculate distance to edge
                    Vector3 va = new Vector3();
                    Vector3 vb = new Vector3();
                    if (!GetPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, ref va, ref vb))
                        continue;

                    //if the circle is not touching the next polygon, skip it
                    float tseg;
                    float distSqr = Distance.PointToSegment2DSquared(ref centerPoint.Position, ref va, ref vb, out tseg);
                    if (distSqr > radiusSqr)
                        continue;

                    //mark node visited
                    neighbourNode.Flags |= NodeFlags.Closed;
                    neighbourNode.ParentIdx = tinyNodePool.GetNodeIdx(curNode);

                    //check that the polygon doesn't collide with existing polygons

                    //collect vertices of the neighbour poly
                    int npa = neighbourPoly.VertCount;
                    for (int k = 0; k < npa; k++)
                        pa[k] = neighbourTile.Verts[neighbourPoly.Verts[k]];

                    bool overlap = false;
                    for (int j = 0; j < n; j++)
                    {
                        PolyId pastRef = resultRef[j];

                        //connected polys do not overlap
                        bool connected = false;
                        for (int k = curPoly.FirstLink; k != Link.Null; k = curTile.Links[k].Next)
                        {
                            if (curTile.Links[k].Reference == pastRef)
                            {
                                connected = true;
                                break;
                            }
                        }

                        if (connected)
                            continue;

                        //potentially overlapping
                        MeshTile pastTile;
                        Poly pastPoly;
                        nav.TryGetTileAndPolyByRefUnsafe(pastRef, out pastTile, out pastPoly);

                        //get vertices and test overlap
                        int npb = pastPoly.VertCount;
                        for (int k = 0; k < npb; k++)
                            pb[k] = pastTile.Verts[pastPoly.Verts[k]];

                        if (Intersection.PolyPoly2D(pa, npa, pb, npb))
                        {
                            overlap = true;
                            break;
                        }
                    }

                    if (overlap)
                        continue;

                    //store poly
                    if (n < maxResult)
                    {
                        resultRef[n] = neighbourRef;
                        resultParent[n] = curRef;
                        ++n;
                    }

                    if (nstack < MAX_STACK)
                    {
                        stack[nstack++] = neighbourNode;
                    }
                }
            }

            resultCount = n;

            return true;
        }
Exemple #19
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;
        }
Exemple #20
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="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, List<PolyId> path)
        {
            //reset path of polygons
            path.Clear();

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

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

            //path can't store any elements
            if (path.Capacity == 0)
                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
            Node startNode = nodePool.GetNode(startRef);
            startNode.Pos = startPos;
            startNode.ParentIdx = 0;
            startNode.cost = 0;
            startNode.total = (startPos - endPos).Length() * H_SCALE;
            startNode.Id = startRef;
            startNode.Flags = NodeFlags.Open;
            openList.Push(startNode);

            Node lastBestNode = startNode;
            float lastBestTotalCost = startNode.total;

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

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

                //get current poly and tile
                PolyId bestRef = bestNode.Id;
                MeshTile bestTile;
                Poly bestPoly;
                nav.TryGetTileAndPolyByRefUnsafe(bestRef, out bestTile, out bestPoly);

                //get parent poly and tile
                PolyId parentRef = PolyId.Null;
                MeshTile parentTile;
                Poly parentPoly;
                if (bestNode.ParentIdx != 0)
                    parentRef = nodePool.GetNodeAtIdx(bestNode.ParentIdx).Id;
                if (parentRef != PolyId.Null)
                    nav.TryGetTileAndPolyByRefUnsafe(parentRef, out parentTile, out parentPoly);

                //examine neighbors
                for (int i = bestPoly.FirstLink; i != Link.Null; i = bestTile.Links[i].Next)
                {
                    PolyId neighbourRef = bestTile.Links[i].Reference;

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

                    //get neighbour poly and tile
                    MeshTile neighbourTile;
                    Poly neighbourPoly;
                    nav.TryGetTileAndPolyByRefUnsafe(neighbourRef, out neighbourTile, out neighbourPoly);

                    Node neighbourNode = nodePool.GetNode(neighbourRef);
                    if (neighbourNode == null)
                        continue;

                    //if node is visited the first time, calculate node position
                    if (neighbourNode.Flags == 0)
                    {
                        GetEdgeMidPoint(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, ref neighbourNode.Pos);
                    }

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

                    //special case for last node
                    if (neighbourRef == endRef)
                    {
                        //cost
                        float curCost = GetCost(bestNode.Pos, neighbourNode.Pos, bestPoly);
                        float endCost = GetCost(neighbourNode.Pos, endPos, neighbourPoly);

                        cost = bestNode.cost + curCost + endCost;
                        heuristic = 0;
                    }
                    else
                    {
                        //cost
                        float curCost = GetCost(bestNode.Pos, neighbourNode.Pos, bestPoly);

                        cost = bestNode.cost + curCost;
                        heuristic = (neighbourNode.Pos - endPos).Length() * H_SCALE;
                    }

                    float total = cost + heuristic;

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

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

                    //add or update the node
                    neighbourNode.ParentIdx = nodePool.GetNodeIdx(bestNode);
                    neighbourNode.Id = neighbourRef;
                    neighbourNode.Flags = RemoveNodeFlagClosed(neighbourNode);
                    neighbourNode.cost = cost;
                    neighbourNode.total = total;

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

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

            //save path
            Node node = lastBestNode;
            do
            {
                path.Add(node.Id);
                if (path.Count >= path.Capacity)
                    break;

                node = nodePool.GetNodeAtIdx(node.ParentIdx);
            }
            while (node != null);

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

            return true;
        }
Exemple #21
0
        /// <summary>
        /// Update the sliced path as agents move across the path.
        /// </summary>
        /// <param name="maxIter">Maximum iterations</param>
        /// <param name="doneIters">Number of times iterated through</param>
        /// <returns>True if updated, false if not</returns>
        public bool UpdateSlicedFindPath(int maxIter, ref int doneIters)
        {
            if (query.Status != true)
                return query.Status;

            //make sure the request is still valid
            if (!nav.IsValidPolyRef(query.Start.Polygon) || !nav.IsValidPolyRef(query.End.Polygon))
            {
                query.Status = false;
                return false;
            }

            int iter = 0;
            while (iter < maxIter && !openList.Empty())
            {
                iter++;

                //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 == query.End.Polygon)
                {
                    query.LastBestNode = bestNode;
                    query.Status = true;
                    doneIters = iter;
                    return query.Status;
                }

                //get current poly and tile
                NavPolyId bestRef = bestNode.Id;
                NavTile bestTile;
                NavPoly bestPoly;
                if (!nav.TryGetTileAndPolyByRef(bestRef, out bestTile, out bestPoly))
                {
                    //the polygon has disappeared during the sliced query, fail
                    query.Status = false;
                    doneIters = iter;
                    return query.Status;
                }

                //get parent poly and tile
                NavPolyId parentRef = NavPolyId.Null;
                NavPolyId grandpaRef = NavPolyId.Null;
                NavTile parentTile = null;
                NavPoly parentPoly = null;
                NavNode parentNode = null;
                if (bestNode.ParentIndex != 0)
                {
                    parentNode = nodePool.GetNodeAtIdx(bestNode.ParentIndex);
                    parentRef = parentNode.Id;
                    if (parentNode.ParentIndex != 0)
                        grandpaRef = nodePool.GetNodeAtIdx(parentNode.ParentIndex).Id;
                }
                if (parentRef != NavPolyId.Null)
                {
                    bool invalidParent = !nav.TryGetTileAndPolyByRef(parentRef, out parentTile, out parentPoly);
                    if (invalidParent || (grandpaRef != NavPolyId.Null && !nav.IsValidPolyRef(grandpaRef)))
                    {
                        //the polygon has disappeared during the sliced query, fail
                        query.Status = false;
                        doneIters = iter;
                        return query.Status;
                    }
                }

                //decide whether to test raycast to previous nodes
                bool tryLOS = false;
                if ((query.Options & FindPathOptions.AnyAngle) != 0)
                {
                    if ((parentRef != NavPolyId.Null) && (parentNode.Position - bestNode.Position).LengthSquared() < query.RaycastLimitSquared)
                        tryLOS = true;
                }

                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);

                    if (!query.Filter.PassFilter(neighborRef, neighborTile, neighborPoly))
                        continue;

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

                    if (neighborNode.ParentIndex != 0 && neighborNode.ParentIndex == bestNode.ParentIndex)
                        continue;

                    if (neighborNode.Flags == 0)
                    {
                        GetEdgeMidPoint(bestRef, bestPoly, bestTile, neighborRef, neighborPoly, neighborTile, ref neighborNode.Position);
                    }

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

                    bool foundShortCut = false;
                    RaycastHit hit;
                    Path hitPath = new Path();
                    if (tryLOS)
                    {
                        NavPoint startPoint = new NavPoint(parentRef, parentNode.Position);
                        Raycast(ref startPoint, ref neighborNode.Position, grandpaRef, RaycastOptions.UseCosts, out hit, hitPath);
                        foundShortCut = hit.T >= 1.0f;
                    }

                    if (foundShortCut)
                    {
                        cost = parentNode.PolyCost + hitPath.Cost;
                    }
                    else
                    {
                        float curCost = query.Filter.GetCost(bestNode.Position, neighborNode.Position,
                            parentRef, parentTile, parentPoly,
                            bestRef, bestTile, bestPoly,
                            neighborRef, neighborTile, neighborPoly);

                        cost = bestNode.PolyCost + curCost;
                    }

                    //special case for last node
                    if (neighborRef == query.End.Polygon)
                    {
                        //cost
                        float endCost = query.Filter.GetCost(bestNode.Position, neighborNode.Position,
                            bestRef, bestTile, bestPoly,
                            neighborRef, neighborTile, neighborPoly,
                            NavPolyId.Null, null, null);

                        cost = cost + endCost;
                        heuristic = 0;
                    }
                    else
                    {
                        heuristic = (neighborNode.Position - query.End.Position).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 (foundShortCut)
                        neighborNode.Flags |= NodeFlags.ParentDetached;

                    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 < query.LastBestNodeCost)
                    {
                        query.LastBestNodeCost = heuristic;
                        query.LastBestNode = neighborNode;
                    }
                }
            }

            //exhausted all nodes, but could not find path
            if (openList.Empty())
            {
                query.Status = true;
            }

            doneIters = iter;

            return query.Status;
        }
Exemple #22
0
 /// <summary>
 /// Finds a random point in a NavMesh connected to a specified point on the same mesh.
 /// </summary>
 /// <param name="connectedTo">The point that the random point will be connected to.</param>
 /// <returns>A random point connected to <c>connectedTo</c>.</returns>
 public NavPoint FindRandomConnectedPoint(NavPoint connectedTo)
 {
     NavPoint result;
     FindRandomConnectedPoint(connectedTo, out result);
     return result;
 }
Exemple #23
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;
        }
Exemple #24
0
        private void GeneratePathfinding()
        {
            if (!hasGenerated)
                return;

            Random rand = new Random();
            NavQueryFilter filter = new NavQueryFilter();

            buildData = new NavMeshBuilder(polyMesh, polyMeshDetail, new SharpNav.Pathfinding.OffMeshConnection[0], settings);

            tiledNavMesh = new TiledNavMesh(buildData);
            navMeshQuery = new NavMeshQuery(tiledNavMesh, 2048);

            //Find random start and end points on the poly mesh
            /*int startRef;
            navMeshQuery.FindRandomPoint(out startRef, out startPos);*/

            SVector3 c = new SVector3(10, 0, 0);
            SVector3 e = new SVector3(5, 5, 5);
            navMeshQuery.FindNearestPoly(ref c, ref e, out startPt);

            navMeshQuery.FindRandomPointAroundCircle(ref startPt, 1000, out endPt);

            //calculate the overall path, which contains an array of polygon references
            int MAX_POLYS = 256;
            path = new Path();
            navMeshQuery.FindPath(ref startPt, ref endPt, filter, path);

            //find a smooth path over the mesh surface
            int npolys = path.Count;
            SVector3 iterPos = new SVector3();
            SVector3 targetPos = new SVector3();
            navMeshQuery.ClosestPointOnPoly(startPt.Polygon, startPt.Position, ref iterPos);
            navMeshQuery.ClosestPointOnPoly(path[npolys - 1], endPt.Position, ref targetPos);

            smoothPath = new List<SVector3>(2048);
            smoothPath.Add(iterPos);

            float STEP_SIZE = 0.5f;
            float SLOP = 0.01f;
            while (npolys > 0 && smoothPath.Count < smoothPath.Capacity)
            {
                //find location to steer towards
                SVector3 steerPos = new SVector3();
                StraightPathFlags steerPosFlag = 0;
                NavPolyId steerPosRef = NavPolyId.Null;

                if (!GetSteerTarget(navMeshQuery, iterPos, targetPos, SLOP, path, ref steerPos, ref steerPosFlag, ref steerPosRef))
                    break;

                bool endOfPath = (steerPosFlag & StraightPathFlags.End) != 0 ? true : false;
                bool offMeshConnection = (steerPosFlag & StraightPathFlags.OffMeshConnection) != 0 ? true : false;

                //find movement delta
                SVector3 delta = steerPos - iterPos;
                float len = (float)Math.Sqrt(SVector3.Dot(delta, delta));

                //if steer target is at end of path or off-mesh link
                //don't move past location
                if ((endOfPath || offMeshConnection) && len < STEP_SIZE)
                    len = 1;
                else
                    len = STEP_SIZE / len;

                SVector3 moveTgt = new SVector3();
                VMad(ref moveTgt, iterPos, delta, len);

                //move
                SVector3 result = new SVector3();
                List<NavPolyId> visited = new List<NavPolyId>(16);
                NavPoint startPoint = new NavPoint(path[0], iterPos);
                navMeshQuery.MoveAlongSurface(ref startPoint, ref moveTgt, out result, visited);
                path.FixupCorridor(visited);
                npolys = path.Count;
                float h = 0;
                navMeshQuery.GetPolyHeight(path[0], result, ref h);
                result.Y = h;
                iterPos = result;

                //handle end of path when close enough
                if (endOfPath && InRange(iterPos, steerPos, SLOP, 1.0f))
                {
                    //reached end of path
                    iterPos = targetPos;
                    if (smoothPath.Count < smoothPath.Capacity)
                    {
                        smoothPath.Add(iterPos);
                    }
                    break;
                }

                //store results
                if (smoothPath.Count < smoothPath.Capacity)
                {
                    smoothPath.Add(iterPos);
                }
            }
        }
 public StraightPathVertex(NavPoint point, StraightPathFlags flags)
 {
     Point = point;
     Flags = flags;
 }