Esempio n. 1
0
        public void OnDrawGizmos(Pathfinding.Util.RetainedGizmos gizmos)
        {
            var hasher = new Pathfinding.Util.RetainedGizmos.Hasher(AstarPath.active);

            hasher.AddHash(gizmoVersion);

            if (!gizmos.Draw(hasher))
            {
                var builder = ObjectPool <RetainedGizmos.Builder> .Claim();

                var centers = ArrayPool <UnityEngine.Vector3> .Claim(areas.Length);

                for (int i = 0; i < areas.Length; i++)
                {
                    Int3 center = Int3.zero;
                    var  childs = children[i];
                    if (childs.Count > 0)
                    {
                        for (int j = 0; j < childs.Count; j++)
                        {
                            center += childs[j].position;
                        }
                        center    /= childs.Count;
                        centers[i] = (UnityEngine.Vector3)center;
                    }
                }

                for (int i = 0; i < areas.Length; i++)
                {
                    if (children[i].Count > 0)
                    {
                        for (int j = 0; j < connections[i].Count; j++)
                        {
                            if (connections[i][j] > i)
                            {
                                builder.DrawLine(centers[i], centers[connections[i][j]], UnityEngine.Color.black);
                            }
                        }
                    }
                }

                builder.Submit(gizmos, hasher);
            }
        }
Esempio n. 2
0
        /** Use this for initialization.
         *
         * \param s Optionally provide in order to take tag penalties into account. May be null if you do not use a Seeker\
         * \param p Path to follow
         * \param mergePartEndpoints If true, then adjacent parts that the path is split up in will
         * try to use the same start/end points. For example when using a link on a navmesh graph
         * Instead of first following the path to the center of the node where the link is and then
         * follow the link, the path will be adjusted to go to the exact point where the link starts
         * which usually makes more sense.
         * \param simplificationMode The path can optionally be simplified. This can be a bit expensive for long paths.
         */
        public void Initialize(Seeker s, Path p, bool mergePartEndpoints, RichFunnel.FunnelSimplification simplificationMode)
        {
            if (p.error)
            {
                throw new System.ArgumentException("Path has an error");
            }

            List <GraphNode> nodes = p.path;

            if (nodes.Count == 0)
            {
                throw new System.ArgumentException("Path traverses no nodes");
            }

            seeker = s;
            // Release objects back to object pool
            // Yeah, I know, it's casting... but this won't be called much
            for (int i = 0; i < parts.Count; i++)
            {
                if (parts[i] is RichFunnel)
                {
                    ObjectPool <RichFunnel> .Release(parts[i] as RichFunnel);
                }
                else if (parts[i] is RichSpecial)
                {
                    ObjectPool <RichSpecial> .Release(parts[i] as RichSpecial);
                }
            }

            parts.Clear();
            currentPart = 0;

            // Initialize new

            //Break path into parts
            for (int i = 0; i < nodes.Count; i++)
            {
                if (nodes[i] is TriangleMeshNode)
                {
                    var        graph = AstarData.GetGraph(nodes[i]);
                    RichFunnel f     = ObjectPool <RichFunnel> .Claim().Initialize(this, graph);

                    f.funnelSimplificationMode = simplificationMode;

                    int  sIndex            = i;
                    uint currentGraphIndex = nodes[sIndex].GraphIndex;


                    for (; i < nodes.Count; i++)
                    {
                        if (nodes[i].GraphIndex != currentGraphIndex && !(nodes[i] is NodeLink3Node))
                        {
                            break;
                        }
                    }
                    i--;

                    if (sIndex == 0)
                    {
                        f.exactStart = p.vectorPath[0];
                    }
                    else
                    {
                        f.exactStart = (Vector3)nodes[mergePartEndpoints ? sIndex - 1 : sIndex].position;
                    }

                    if (i == nodes.Count - 1)
                    {
                        f.exactEnd = p.vectorPath[p.vectorPath.Count - 1];
                    }
                    else
                    {
                        f.exactEnd = (Vector3)nodes[mergePartEndpoints ? i + 1 : i].position;
                    }

                    f.BuildFunnelCorridor(nodes, sIndex, i);

                    parts.Add(f);
                }
                else if (NodeLink2.GetNodeLink(nodes[i]) != null)
                {
                    NodeLink2 nl = NodeLink2.GetNodeLink(nodes[i]);

                    int  sIndex            = i;
                    uint currentGraphIndex = nodes[sIndex].GraphIndex;

                    for (i++; i < nodes.Count; i++)
                    {
                        if (nodes[i].GraphIndex != currentGraphIndex)
                        {
                            break;
                        }
                    }
                    i--;

                    if (i - sIndex > 1)
                    {
                        throw new System.Exception("NodeLink2 path length greater than two (2) nodes. " + (i - sIndex));
                    }
                    else if (i - sIndex == 0)
                    {
                        //Just continue, it might be the case that a NodeLink was the closest node
                        continue;
                    }

                    RichSpecial rps = ObjectPool <RichSpecial> .Claim().Initialize(nl, nodes[sIndex]);

                    parts.Add(rps);
                }
            }
        }
Esempio n. 3
0
        // Token: 0x06002140 RID: 8512 RVA: 0x001890A0 File Offset: 0x001872A0
        public void Initialize(Seeker seeker, Path path, bool mergePartEndpoints, bool simplificationMode)
        {
            if (path.error)
            {
                throw new ArgumentException("Path has an error");
            }
            List <GraphNode> path2 = path.path;

            if (path2.Count == 0)
            {
                throw new ArgumentException("Path traverses no nodes");
            }
            this.seeker = seeker;
            for (int i = 0; i < this.parts.Count; i++)
            {
                RichFunnel  richFunnel  = this.parts[i] as RichFunnel;
                RichSpecial richSpecial = this.parts[i] as RichSpecial;
                if (richFunnel != null)
                {
                    ObjectPool <RichFunnel> .Release(ref richFunnel);
                }
                else if (richSpecial != null)
                {
                    ObjectPool <RichSpecial> .Release(ref richSpecial);
                }
            }
            this.Clear();
            this.Endpoint = path.vectorPath[path.vectorPath.Count - 1];
            for (int j = 0; j < path2.Count; j++)
            {
                if (path2[j] is TriangleMeshNode)
                {
                    NavmeshBase navmeshBase = AstarData.GetGraph(path2[j]) as NavmeshBase;
                    if (navmeshBase == null)
                    {
                        throw new Exception("Found a TriangleMeshNode that was not in a NavmeshBase graph");
                    }
                    RichFunnel richFunnel2 = ObjectPool <RichFunnel> .Claim().Initialize(this, navmeshBase);

                    richFunnel2.funnelSimplification = simplificationMode;
                    int  num        = j;
                    uint graphIndex = path2[num].GraphIndex;
                    while (j < path2.Count && (path2[j].GraphIndex == graphIndex || path2[j] is NodeLink3Node))
                    {
                        j++;
                    }
                    j--;
                    if (num == 0)
                    {
                        richFunnel2.exactStart = path.vectorPath[0];
                    }
                    else
                    {
                        richFunnel2.exactStart = (Vector3)path2[mergePartEndpoints ? (num - 1) : num].position;
                    }
                    if (j == path2.Count - 1)
                    {
                        richFunnel2.exactEnd = path.vectorPath[path.vectorPath.Count - 1];
                    }
                    else
                    {
                        richFunnel2.exactEnd = (Vector3)path2[mergePartEndpoints ? (j + 1) : j].position;
                    }
                    richFunnel2.BuildFunnelCorridor(path2, num, j);
                    this.parts.Add(richFunnel2);
                }
                else if (NodeLink2.GetNodeLink(path2[j]) != null)
                {
                    NodeLink2 nodeLink    = NodeLink2.GetNodeLink(path2[j]);
                    int       num2        = j;
                    uint      graphIndex2 = path2[num2].GraphIndex;
                    j++;
                    while (j < path2.Count && path2[j].GraphIndex == graphIndex2)
                    {
                        j++;
                    }
                    j--;
                    if (j - num2 > 1)
                    {
                        throw new Exception("NodeLink2 path length greater than two (2) nodes. " + (j - num2));
                    }
                    if (j - num2 != 0)
                    {
                        RichSpecial item = ObjectPool <RichSpecial> .Claim().Initialize(nodeLink, path2[num2]);

                        this.parts.Add(item);
                    }
                }
            }
        }
Esempio n. 4
0
        // Token: 0x06000033 RID: 51 RVA: 0x0000417C File Offset: 0x0000257C
        public void Initialize(Seeker s, Path p, bool mergePartEndpoints, RichFunnel.FunnelSimplification simplificationMode)
        {
            if (p.error)
            {
                throw new ArgumentException("Path has an error");
            }
            List <GraphNode> path = p.path;

            if (path.Count == 0)
            {
                throw new ArgumentException("Path traverses no nodes");
            }
            this.seeker = s;
            for (int i = 0; i < this.parts.Count; i++)
            {
                if (this.parts[i] is RichFunnel)
                {
                    ObjectPool <RichFunnel> .Release(this.parts[i] as RichFunnel);
                }
                else if (this.parts[i] is RichSpecial)
                {
                    ObjectPool <RichSpecial> .Release(this.parts[i] as RichSpecial);
                }
            }
            this.parts.Clear();
            this.currentPart = 0;
            for (int j = 0; j < path.Count; j++)
            {
                if (path[j] is TriangleMeshNode)
                {
                    IFunnelGraph graph      = AstarData.GetGraph(path[j]) as IFunnelGraph;
                    RichFunnel   richFunnel = ObjectPool <RichFunnel> .Claim().Initialize(this, graph);

                    richFunnel.funnelSimplificationMode = simplificationMode;
                    int  num        = j;
                    uint graphIndex = path[num].GraphIndex;
                    while (j < path.Count)
                    {
                        if (path[j].GraphIndex != graphIndex && !(path[j] is NodeLink3Node))
                        {
                            break;
                        }
                        j++;
                    }
                    j--;
                    if (num == 0)
                    {
                        richFunnel.exactStart = p.vectorPath[0];
                    }
                    else if (mergePartEndpoints)
                    {
                        richFunnel.exactStart = (Vector3)path[num - 1].position;
                    }
                    else
                    {
                        richFunnel.exactStart = (Vector3)path[num].position;
                    }
                    if (j == path.Count - 1)
                    {
                        richFunnel.exactEnd = p.vectorPath[p.vectorPath.Count - 1];
                    }
                    else if (mergePartEndpoints)
                    {
                        richFunnel.exactEnd = (Vector3)path[j + 1].position;
                    }
                    else
                    {
                        richFunnel.exactEnd = (Vector3)path[j].position;
                    }
                    richFunnel.BuildFunnelCorridor(path, num, j);
                    this.parts.Add(richFunnel);
                }
                else if (path[j] != null && NodeLink2.GetNodeLink(path[j]) != null)
                {
                    NodeLink2 nodeLink    = NodeLink2.GetNodeLink(path[j]);
                    int       num2        = j;
                    uint      graphIndex2 = path[num2].GraphIndex;
                    for (j++; j < path.Count; j++)
                    {
                        if (path[j].GraphIndex != graphIndex2)
                        {
                            break;
                        }
                    }
                    j--;
                    if (j - num2 > 1)
                    {
                        throw new Exception("NodeLink2 path length greater than two (2) nodes. " + (j - num2));
                    }
                    if (j - num2 != 0)
                    {
                        RichSpecial item = ObjectPool <RichSpecial> .Claim().Initialize(nodeLink, path[num2]);

                        this.parts.Add(item);
                    }
                }
            }
        }
        /// <summary>Async method for moving the graph</summary>
        IEnumerator UpdateGraphCoroutine()
        {
            // Find the direction that we want to move the graph in.
            // Calcuculate this in graph space (where a distance of one is the size of one node)
            Vector3 dir = PointToGraphSpace(target.position) - PointToGraphSpace(graph.center);

            // Snap to a whole number of nodes
            dir.x = Mathf.Round(dir.x);
            dir.z = Mathf.Round(dir.z);
            dir.y = 0;

            // Nothing do to
            if (dir == Vector3.zero)
            {
                yield break;
            }

            // Number of nodes to offset in each direction
            Int2 offset = new Int2(-Mathf.RoundToInt(dir.x), -Mathf.RoundToInt(dir.z));

            // Move the center (this is in world units, so we need to convert it back from graph space)
            graph.center += graph.transform.TransformVector(dir);
            graph.UpdateTransform();

            // Cache some variables for easier access
            int width = graph.width;
            int depth = graph.depth;

            GridNodeBase[] nodes = graph.nodes;
            // Layers are required when handling LayeredGridGraphs
            int layers = graph.LayerCount;

            // Create a temporary buffer required for the calculations
            Memory.Realloc(ref buffer, width * depth);

            // Check if we have moved less than a whole graph width all directions
            // If we have moved more than this we can just as well recalculate the whole graph
            if (Mathf.Abs(offset.x) <= width && Mathf.Abs(offset.y) <= depth)
            {
                // Note: the upper limits are treated as exclusive limits (xmax, ymax)
                IntRect recalculateRect = new IntRect(0, 0, offset.x, offset.y);

                // If offset.x < 0, adjust the rect
                if (recalculateRect.xmin > recalculateRect.xmax)
                {
                    int tmp2 = recalculateRect.xmax;
                    recalculateRect.xmax = width + recalculateRect.xmin;
                    recalculateRect.xmin = width + tmp2;
                }

                // If offset.y < 0, adjust the rect
                if (recalculateRect.ymin > recalculateRect.ymax)
                {
                    int tmp2 = recalculateRect.ymax;
                    recalculateRect.ymax = depth + recalculateRect.ymin;
                    recalculateRect.ymin = depth + tmp2;
                }

                // Offset each node by the #offset variable
                // nodes which would end up outside the graph
                // will wrap around to the other side of it
                for (int l = 0; l < layers; l++)
                {
                    int layerOffset = l * width * depth;
                    for (int z = 0; z < depth; z++)
                    {
                        int pz = z * width;
                        int tz = ((z + offset.y + depth) % depth) * width;
                        for (int x = 0; x < width; x++)
                        {
                            buffer[tz + ((x + offset.x + width) % width)] = nodes[layerOffset + pz + x];
                        }
                    }

                    yield return(null);

                    // Copy the nodes back to the graph
                    // and set the correct indices
                    for (int z = 0; z < depth; z++)
                    {
                        int pz = z * width;
                        for (int x = 0; x < width; x++)
                        {
                            int newIndex = pz + x;
                            var node     = buffer[newIndex];
                            if (node != null)
                            {
                                node.NodeInGridIndex = newIndex;
                            }
                            nodes[layerOffset + newIndex] = node;
                        }

                        // Calculate the limits for the region that has been wrapped
                        // to the other side of the graph.
                        // This part is made up of a couple of rows and a couple of columns
                        // So depending on the row we are in (z) we should either iterate over the whole row
                        // or only the relevant columns.
                        int xmin, xmax;
                        if (z >= recalculateRect.ymin && z < recalculateRect.ymax)
                        {
                            xmin = 0;
                            xmax = width;
                        }
                        else
                        {
                            xmin = recalculateRect.xmin;
                            xmax = recalculateRect.xmax;
                        }

                        for (int x = xmin; x < xmax; x++)
                        {
                            var node = buffer[pz + x];
                            if (node != null)
                            {
                                // Clear connections on all nodes that are wrapped and placed on the other side of the graph.
                                // This is both to clear any custom connections (which do not really make sense after moving the node)
                                // and to prevent possible exceptions when the node will later (possibly) be destroyed because it was
                                // not needed anymore (only for layered grid graphs).
                                node.ClearConnections(false);
                            }
                        }
                    }

                    yield return(null);
                }

                // Recalculate the nodes
                // Take a look at the image in the docs for the UpdateGraph method
                // to see which nodes are being recalculated.
                var dependencyTracker = ObjectPool <Jobs.JobDependencyTracker> .Claim();

                var updateNodesJob1 = graph.UpdateRegion(new IntRect(0, recalculateRect.ymin, width, recalculateRect.ymax), dependencyTracker, Unity.Collections.Allocator.Persistent);

                // Note: It is important the the main thread work is completed before the second update begins.
                // Otherwise if we give updateNodesJob1 as a dependency the code might call .Complete on a the handle and block indefinitely
                // since it requires work to complete in the main thread.
                foreach (var _ in updateNodesJob1.CompleteTimeSliced(MaxMillisPerFrame))
                {
                    yield return(null);
                }

                IntRect job2rect;
                if (recalculateRect.ymin == 0)
                {
                    job2rect = new IntRect(recalculateRect.xmin, recalculateRect.ymax, recalculateRect.xmax, depth);
                }
                else if (recalculateRect.ymax == depth)
                {
                    job2rect = new IntRect(recalculateRect.xmin, 0, recalculateRect.xmax, recalculateRect.ymin - 1);
                }
                else
                {
                    throw new System.Exception("Should not happen");
                }

                var updateNodesJob2 = graph.UpdateRegion(job2rect, dependencyTracker, Unity.Collections.Allocator.Persistent);
                foreach (var _ in updateNodesJob2.CompleteTimeSliced(MaxMillisPerFrame))
                {
                    yield return(null);
                }
                ObjectPool <Jobs.JobDependencyTracker> .Release(ref dependencyTracker);

                // Calculate all connections for the nodes along the boundary
                // of the graph, these always need to be updated
                /// <summary>TODO: Optimize to not traverse all nodes in the graph, only those at the edges</summary>
                for (int z = 0; z < depth; z++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (x == 0 || z == 0 || x == width - 1 || z == depth - 1)
                        {
                            graph.CalculateConnections(x, z);
                        }
                    }
                }

                yield return(null);
            }
            else
            {
                var dependencyTracker = ObjectPool <Jobs.JobDependencyTracker> .Claim();

                var updateNodesJob = graph.UpdateRegion(new IntRect(0, 0, width, depth), dependencyTracker, Unity.Collections.Allocator.Persistent);
                foreach (var _ in updateNodesJob.CompleteTimeSliced(MaxMillisPerFrame))
                {
                    yield return(null);
                }
                ObjectPool <Jobs.JobDependencyTracker> .Release(ref dependencyTracker);
            }
        }
Esempio n. 6
0
        /// <summary>Use this for initialization.</summary>
        /// <param name="seeker">Optionally provide in order to take tag penalties into account. May be null if you do not use a Seeker\</param>
        /// <param name="path">Path to follow</param>
        /// <param name="mergePartEndpoints">If true, then adjacent parts that the path is split up in will
        /// try to use the same start/end points. For example when using a link on a navmesh graph
        /// Instead of first following the path to the center of the node where the link is and then
        /// follow the link, the path will be adjusted to go to the exact point where the link starts
        /// which usually makes more sense.</param>
        /// <param name="simplificationMode">The path can optionally be simplified. This can be a bit expensive for long paths.</param>
        public void Initialize(Seeker seeker, Path path, bool mergePartEndpoints, bool simplificationMode)
        {
            if (path.error)
            {
                throw new System.ArgumentException("Path has an error");
            }

            List <GraphNode> nodes = path.path;

            if (nodes.Count == 0)
            {
                throw new System.ArgumentException("Path traverses no nodes");
            }

            this.seeker = seeker;
            // Release objects back to object pool
            // Yeah, I know, it's casting... but this won't be called much
            for (int i = 0; i < parts.Count; i++)
            {
                var funnelPart  = parts[i] as RichFunnel;
                var specialPart = parts[i] as RichSpecial;
                if (funnelPart != null)
                {
                    ObjectPool <RichFunnel> .Release(ref funnelPart);
                }
                else if (specialPart != null)
                {
                    ObjectPool <RichSpecial> .Release(ref specialPart);
                }
            }

            Clear();

            // Initialize new
            Endpoint = path.vectorPath[path.vectorPath.Count - 1];

            //Break path into parts
            for (int i = 0; i < nodes.Count; i++)
            {
                if (nodes[i] is TriangleMeshNode)
                {
                    var graph = AstarData.GetGraph(nodes[i]) as NavmeshBase;
                    if (graph == null)
                    {
                        throw new System.Exception("Found a TriangleMeshNode that was not in a NavmeshBase graph");
                    }

                    RichFunnel f = ObjectPool <RichFunnel> .Claim().Initialize(this, graph);

                    f.funnelSimplification = simplificationMode;

                    int  sIndex            = i;
                    uint currentGraphIndex = nodes[sIndex].GraphIndex;


                    for (; i < nodes.Count; i++)
                    {
                        if (nodes[i].GraphIndex != currentGraphIndex && !(nodes[i] is NodeLink3Node))
                        {
                            break;
                        }
                    }
                    i--;

                    if (sIndex == 0)
                    {
                        f.exactStart = path.vectorPath[0];
                    }
                    else
                    {
                        f.exactStart = (Vector3)nodes[mergePartEndpoints ? sIndex - 1 : sIndex].position;
                    }

                    if (i == nodes.Count - 1)
                    {
                        f.exactEnd = path.vectorPath[path.vectorPath.Count - 1];
                    }
                    else
                    {
                        f.exactEnd = (Vector3)nodes[mergePartEndpoints ? i + 1 : i].position;
                    }

                    f.BuildFunnelCorridor(nodes, sIndex, i);

                    parts.Add(f);
                }
                else if (NodeLink2.GetNodeLink(nodes[i]) != null)
                {
                    NodeLink2 nl = NodeLink2.GetNodeLink(nodes[i]);

                    int  sIndex            = i;
                    uint currentGraphIndex = nodes[sIndex].GraphIndex;

                    for (i++; i < nodes.Count; i++)
                    {
                        if (nodes[i].GraphIndex != currentGraphIndex)
                        {
                            break;
                        }
                    }
                    i--;

                    if (i - sIndex > 1)
                    {
                        throw new System.Exception("NodeLink2 path length greater than two (2) nodes. " + (i - sIndex));
                    }
                    else if (i - sIndex == 0)
                    {
                        //Just continue, it might be the case that a NodeLink was the closest node
                        continue;
                    }

                    RichSpecial rps = ObjectPool <RichSpecial> .Claim().Initialize(nl, nodes[sIndex]);

                    parts.Add(rps);
                }
                else if (!(nodes[i] is PointNode))
                {
                    // Some other graph type which we do not have support for
                    throw new System.InvalidOperationException("The RichAI movment script can only be used on recast/navmesh graphs. A node of type " + nodes[i].GetType().Name + " was in the path.");
                }
            }
        }