Example #1
0
        public static void TagMaskField(GUIContent label, int value, System.Action <int> callback)
        {
            GUILayout.BeginHorizontal();

            EditorGUILayout.PrefixLabel(label, EditorStyles.layerMaskField);

            string[] tagNames = AstarPath.FindTagNames();

            string text;

            if (value == 0)
            {
                text = "Nothing";
            }
            else if (value == ~0)
            {
                text = "Everything";
            }
            else
            {
                text = "";
                for (int i = 0; i < 32; i++)
                {
                    if (((value >> i) & 0x1) != 0)
                    {
                        // Add spacing between words
                        if (text.Length > 0)
                        {
                            text += ", ";
                        }

                        text += tagNames[i];

                        // Too long, just give up
                        if (text.Length > 40)
                        {
                            text = "Mixed...";
                            break;
                        }
                    }
                }
            }

            if (GUILayout.Button(text, EditorStyles.layerMaskField, GUILayout.ExpandWidth(true)))
            {
                GenericMenu.MenuFunction2 wrappedCallback = obj => callback((int)obj);

                var menu = new GenericMenu();

                menu.AddItem(new GUIContent("Everything"), value == ~0, wrappedCallback, ~0);
                menu.AddItem(new GUIContent("Nothing"), value == 0, wrappedCallback, 0);

                for (int i = 0; i < tagNames.Length; i++)
                {
                    bool on     = (value >> i & 1) != 0;
                    int  result = on ? value & ~(1 << i) : value | 1 << i;
                    menu.AddItem(new GUIContent(tagNames[i]), on, wrappedCallback, result);
                }

                // Shortcut to open the tag editor
                menu.AddItem(new GUIContent("Edit Tag Names..."), false, AstarPathEditor.EditTags);
                menu.ShowAsContext();

                Event.current.Use();
            }

            GUILayout.EndHorizontal();
        }
Example #2
0
 public PointNode(AstarPath astar) : base(astar)
 {
 }
Example #3
0
        public void RecalculateCosts()
        {
            if (pivots == null)
            {
                RecalculatePivots();
            }
            if (mode == HeuristicOptimizationMode.None)
            {
                return;
            }

            pivotCount = 0;

            for (int i = 0; i < pivots.Length; i++)
            {
                if (pivots[i] != null && (pivots[i].Destroyed || !pivots[i].Walkable))
                {
                    throw new System.Exception("Invalid pivot nodes (destroyed or unwalkable)");
                }
            }

            if (mode != HeuristicOptimizationMode.RandomSpreadOut)
            {
                for (int i = 0; i < pivots.Length; i++)
                {
                    if (pivots[i] == null)
                    {
                        throw new System.Exception("Invalid pivot nodes (null)");
                    }
                }
            }

            UnityEngine.Debug.Log("Recalculating costs...");
            pivotCount = pivots.Length;

            System.Action <int> startCostCalculation = null;

            startCostCalculation = delegate(int k) {
                GraphNode pivot = pivots[k];

                FloodPath fp = null;
                fp = FloodPath.Construct(pivot);
                fp.immediateCallback = delegate(Path _p) {
                    // Handle path pooling
                    _p.Claim(this);

                    // When paths are calculated on navmesh based graphs
                    // the costs are slightly modified to match the actual target and start points
                    // instead of the node centers
                    // so we have to remove the cost for the first and last connection
                    // in each path
                    MeshNode mn         = pivot as MeshNode;
                    uint     costOffset = 0;
                    if (mn != null && mn.connectionCosts != null)
                    {
                        for (int i = 0; i < mn.connectionCosts.Length; i++)
                        {
                            costOffset = System.Math.Max(costOffset, mn.connectionCosts[i]);
                        }
                    }


                    var graphs = AstarPath.active.graphs;
                    // Process graphs in reverse order to raise probability that we encounter large NodeIndex values quicker
                    // to avoid resizing the internal array too often
                    for (int j = graphs.Length - 1; j >= 0; j--)
                    {
                        graphs[j].GetNodes(delegate(GraphNode node) {
                            int idx = node.NodeIndex * pivotCount + k;
                            EnsureCapacity(idx);
                            PathNode pn = fp.pathHandler.GetPathNode(node);
                            if (costOffset > 0)
                            {
                                costs[idx] = pn.pathID == fp.pathID && pn.parent != null ? System.Math.Max(pn.parent.G - costOffset, 0) : 0;
                            }
                            else
                            {
                                costs[idx] = pn.pathID == fp.pathID ? pn.G : 0;
                            }
                            return(true);
                        });
                    }

                    if (mode == HeuristicOptimizationMode.RandomSpreadOut && k < pivots.Length - 1)
                    {
                        int  best      = -1;
                        uint bestScore = 0;

                        // Actual number of nodes
                        int totCount = maxNodeIndex / pivotCount;

                        // Loop through all nodes
                        for (int j = 1; j < totCount; j++)
                        {
                            // Find the minimum distance from the node to all existing pivot points
                            uint mx = 1 << 30;
                            for (int p = 0; p <= k; p++)
                            {
                                mx = System.Math.Min(mx, costs[j * pivotCount + p]);
                            }

                            // Pick the node which has the largest minimum distance to the existing pivot points
                            // (i.e pick the one furthest away from the existing ones)
                            GraphNode node = fp.pathHandler.GetPathNode(j).node;
                            if ((mx > bestScore || best == -1) && node != null && !node.Destroyed && node.Walkable)
                            {
                                best      = j;
                                bestScore = mx;
                            }
                        }

                        if (best == -1)
                        {
                            Debug.LogError("Failed generating random pivot points for heuristic optimizations");
                            return;
                        }

                        pivots[k + 1] = fp.pathHandler.GetPathNode(best).node;

                        Debug.Log("Found node at " + pivots[k + 1].position + " with score " + bestScore);

                        startCostCalculation(k + 1);
                    }

                    // Handle path pooling
                    _p.Release(this);
                };

                AstarPath.StartPath(fp, true);
            };

            if (mode != HeuristicOptimizationMode.RandomSpreadOut)
            {
                // All calculated in paralell
                for (int i = 0; i < pivots.Length; i++)
                {
                    startCostCalculation(i);
                }
            }
            else
            {
                // Recursive and serial
                startCostCalculation(0);
            }


            dirty = false;
        }
Example #4
0
 public NodeLink3Node(AstarPath astar)
 {
     astar.InitializeNode(this);
 }
Example #5
0
 public GridNode(AstarPath astar) : base(astar)
 {
 }
Example #6
0
 /// <summary>
 /// Blocks until this path has been calculated and returned.
 /// Normally it takes a few frames for a path to be calculated and returned.
 /// This function will ensure that the path will be calculated when this function returns
 /// and that the callback for that path has been called.
 ///
 /// Use this function only if you really need to.
 /// There is a point to spreading path calculations out over several frames.
 /// It smoothes out the framerate and makes sure requesting a large
 /// number of paths at the same time does not cause lag.
 ///
 /// Note: Graph updates and other callbacks might get called during the execution of this function.
 ///
 /// <code>
 /// Path p = seeker.StartPath (transform.position, transform.position + Vector3.forward * 10);
 /// p.BlockUntilCalculated();
 /// // The path is calculated now
 /// </code>
 ///
 /// See: This is equivalent to calling AstarPath.BlockUntilCalculated(Path)
 /// See: WaitForPath
 /// </summary>
 public void BlockUntilCalculated()
 {
     AstarPath.BlockUntilCalculated(this);
 }
Example #7
0
        public void RecalculateCosts()
        {
            if (this.pivots == null)
            {
                this.RecalculatePivots();
            }
            if (this.mode == HeuristicOptimizationMode.None)
            {
                return;
            }
            this.pivotCount = 0;
            for (int i = 0; i < this.pivots.Length; i++)
            {
                if (this.pivots[i] != null && (this.pivots[i].Destroyed || !this.pivots[i].Walkable))
                {
                    throw new Exception("Invalid pivot nodes (destroyed or unwalkable)");
                }
            }
            if (this.mode != HeuristicOptimizationMode.RandomSpreadOut)
            {
                for (int j = 0; j < this.pivots.Length; j++)
                {
                    if (this.pivots[j] == null)
                    {
                        throw new Exception("Invalid pivot nodes (null)");
                    }
                }
            }
            Debug.Log("Recalculating costs...");
            this.pivotCount = this.pivots.Length;
            Action <int>   startCostCalculation = null;
            int            numComplete          = 0;
            OnPathDelegate onComplete           = delegate(Path path)
            {
                numComplete++;
                if (numComplete == this.pivotCount)
                {
                    Debug.Log("Grid graph special case!");
                    this.ApplyGridGraphEndpointSpecialCase();
                }
            };

            startCostCalculation = delegate(int k)
            {
                GraphNode pivot = this.pivots[k];
                FloodPath fp    = null;
                fp = FloodPath.Construct(pivot, onComplete);
                fp.immediateCallback = delegate(Path _p)
                {
                    _p.Claim(this);
                    MeshNode meshNode   = pivot as MeshNode;
                    uint     costOffset = 0u;
                    int      k;
                    if (meshNode != null && meshNode.connectionCosts != null)
                    {
                        for (k = 0; k < meshNode.connectionCosts.Length; k++)
                        {
                            costOffset = Math.Max(costOffset, meshNode.connectionCosts[k]);
                        }
                    }
                    NavGraph[] graphs = AstarPath.active.graphs;
                    for (int m = graphs.Length - 1; m >= 0; m--)
                    {
                        graphs[m].GetNodes(delegate(GraphNode node)
                        {
                            int num6 = node.NodeIndex * this.pivotCount + k;
                            this.EnsureCapacity(num6);
                            PathNode pathNode = fp.pathHandler.GetPathNode(node);
                            if (costOffset > 0u)
                            {
                                this.costs[num6] = ((pathNode.pathID != fp.pathID || pathNode.parent == null) ? 0u : Math.Max(pathNode.parent.G - costOffset, 0u));
                            }
                            else
                            {
                                this.costs[num6] = ((pathNode.pathID != fp.pathID) ? 0u : pathNode.G);
                            }
                            return(true);
                        });
                    }
                    if (this.mode == HeuristicOptimizationMode.RandomSpreadOut && k < this.pivots.Length - 1)
                    {
                        if (this.pivots[k + 1] == null)
                        {
                            int  num  = -1;
                            uint num2 = 0u;
                            int  num3 = this.maxNodeIndex / this.pivotCount;
                            for (int n = 1; n < num3; n++)
                            {
                                uint num4 = 1073741824u;
                                for (int num5 = 0; num5 <= k; num5++)
                                {
                                    num4 = Math.Min(num4, this.costs[n * this.pivotCount + num5]);
                                }
                                GraphNode node2 = fp.pathHandler.GetPathNode(n).node;
                                if ((num4 > num2 || num == -1) && node2 != null && !node2.Destroyed && node2.Walkable)
                                {
                                    num  = n;
                                    num2 = num4;
                                }
                            }
                            if (num == -1)
                            {
                                Debug.LogError("Failed generating random pivot points for heuristic optimizations");
                                return;
                            }
                            this.pivots[k + 1] = fp.pathHandler.GetPathNode(num).node;
                        }
                        startCostCalculation(k + 1);
                    }
                    _p.Release(this, false);
                };
                AstarPath.StartPath(fp, true);
            };
            if (this.mode != HeuristicOptimizationMode.RandomSpreadOut)
            {
                for (int l = 0; l < this.pivots.Length; l++)
                {
                    startCostCalculation(l);
                }
            }
            else
            {
                startCostCalculation(0);
            }
            this.dirty = false;
        }
 protected MeshNode(AstarPath astar) : base(astar)
 {
 }
        //public override Int3 Position {get { return position; } }

        public QuadtreeNode(AstarPath astar) : base(astar)
        {
        }
Example #10
0
 public ConvexMeshNode(AstarPath astar) : base(astar)
 {
     this.indices = new int[0];
 }
Example #11
0
 protected GridNodeBase(AstarPath astar) : base(astar)
 {
 }
Example #12
0
 public GridNode(AstarPath astar)
 {
     astar.InitializeNode(this);
 }
 public void SafeOnDestroy()
 {
     AstarPath.RegisterSafeUpdate(new OnVoidDelegate(this.OnDestroy));
 }
Example #14
0
 /// <summary>
 /// SafeOnDestroy should be used when there is a risk that the pathfinding is searching through this graph when called
 /// </summary>
 public void SafeOnDestroy()
 {
     AstarPath.RegisterSafeNodeUpdate(OnDestroy);
 }
Example #15
0
 public MeshNode(AstarPath astar) : base(astar)
 {
 }
Example #16
0
 public ConvexMeshNode(AstarPath astar) : base(astar)
 {
     indices = new int[0];            //\todo Set indices to some reasonable value
 }
Example #17
0
 public PointNode(AstarPath astar)
 {
     astar.InitializeNode(this);
 }
Example #18
0
        public void RecalculateCosts()
        {
            if (pivots == null)
            {
                RecalculatePivots();
            }
            if (mode == HeuristicOptimizationMode.None)
            {
                return;
            }

            pivotCount = 0;

            for (int i = 0; i < pivots.Length; i++)
            {
                if (pivots[i] != null && (pivots[i].Destroyed || !pivots[i].Walkable))
                {
                    throw new System.Exception("Invalid pivot nodes (destroyed or unwalkable)");
                }
            }

            if (mode != HeuristicOptimizationMode.RandomSpreadOut)
            {
                for (int i = 0; i < pivots.Length; i++)
                {
                    if (pivots[i] == null)
                    {
                        throw new System.Exception("Invalid pivot nodes (null)");
                    }
                }
            }

            Debug.Log("Recalculating costs...");
            pivotCount = pivots.Length;

            System.Action <int> startCostCalculation = null;

            int            numComplete = 0;
            OnPathDelegate onComplete  = (Path path) => {
                numComplete++;
                if (numComplete == pivotCount)
                {
                    // Last completed path
                    ApplyGridGraphEndpointSpecialCase();
                }
            };

            startCostCalculation = (int pivotIndex) => {
                GraphNode pivot = pivots[pivotIndex];

                FloodPath floodPath = null;
                floodPath = FloodPath.Construct(pivot, onComplete);
                floodPath.immediateCallback = (Path _p) => {
                    // Handle path pooling
                    _p.Claim(this);

                    // When paths are calculated on navmesh based graphs
                    // the costs are slightly modified to match the actual target and start points
                    // instead of the node centers
                    // so we have to remove the cost for the first and last connection
                    // in each path
                    var  meshNode   = pivot as MeshNode;
                    uint costOffset = 0;
                    if (meshNode != null && meshNode.connections != null)
                    {
                        for (int i = 0; i < meshNode.connections.Length; i++)
                        {
                            costOffset = System.Math.Max(costOffset, meshNode.connections[i].cost);
                        }
                    }


                    var graphs = AstarPath.active.graphs;
                    // Process graphs in reverse order to raise probability that we encounter large NodeIndex values quicker
                    // to avoid resizing the internal array too often
                    for (int j = graphs.Length - 1; j >= 0; j--)
                    {
                        graphs[j].GetNodes(node => {
                            int idx = node.NodeIndex * pivotCount + pivotIndex;
                            EnsureCapacity(idx);
                            PathNode pn = ((IPathInternals)floodPath).PathHandler.GetPathNode(node);
                            if (costOffset > 0)
                            {
                                costs[idx] = pn.pathID == floodPath.pathID && pn.parent != null ? System.Math.Max(pn.parent.G - costOffset, 0) : 0;
                            }
                            else
                            {
                                costs[idx] = pn.pathID == floodPath.pathID ? pn.G : 0;
                            }
                        });
                    }

                    if (mode == HeuristicOptimizationMode.RandomSpreadOut && pivotIndex < pivots.Length - 1)
                    {
                        // If the next pivot is null
                        // then find the node which is furthest away from the earlier
                        // pivot points
                        if (pivots[pivotIndex + 1] == null)
                        {
                            int  best      = -1;
                            uint bestScore = 0;

                            // Actual number of nodes
                            int totCount = maxNodeIndex / pivotCount;

                            // Loop through all nodes
                            for (int j = 1; j < totCount; j++)
                            {
                                // Find the minimum distance from the node to all existing pivot points
                                uint mx = 1 << 30;
                                for (int p = 0; p <= pivotIndex; p++)
                                {
                                    mx = System.Math.Min(mx, costs[j * pivotCount + p]);
                                }

                                // Pick the node which has the largest minimum distance to the existing pivot points
                                // (i.e pick the one furthest away from the existing ones)
                                GraphNode node = ((IPathInternals)floodPath).PathHandler.GetPathNode(j).node;
                                if ((mx > bestScore || best == -1) && node != null && !node.Destroyed && node.Walkable)
                                {
                                    best      = j;
                                    bestScore = mx;
                                }
                            }

                            if (best == -1)
                            {
                                Debug.LogError("Failed generating random pivot points for heuristic optimizations");
                                return;
                            }

                            pivots[pivotIndex + 1] = ((IPathInternals)floodPath).PathHandler.GetPathNode(best).node;
                        }

                        // Start next path
                        startCostCalculation(pivotIndex + 1);
                    }

                    // Handle path pooling
                    _p.Release(this);
                };

                AstarPath.StartPath(floodPath, true);
            };

            if (mode != HeuristicOptimizationMode.RandomSpreadOut)
            {
                // All calculated in parallel
                for (int i = 0; i < pivots.Length; i++)
                {
                    startCostCalculation(i);
                }
            }
            else
            {
                // Recursive and serial
                startCostCalculation(0);
            }

            dirty = false;
        }
Example #19
0
 public WorkItemProcessor(AstarPath astar)
 {
     this.astar = astar;
 }
Example #20
0
 // Token: 0x0600254E RID: 9550 RVA: 0x001A26ED File Offset: 0x001A08ED
 public LevelGridNode(AstarPath astar) : base(astar)
 {
 }
Example #21
0
 public PathThreadInfo(int index, AstarPath astar, PathHandler runData)
 {
     this.threadIndex = index;
     this.astar       = astar;
     this.runData     = runData;
 }
        protected override void Inspector()
        {
            base.Inspector();

            scripts.Clear();
            foreach (var script in targets)
            {
                scripts.Add(script as Seeker);
            }

            Undo.RecordObjects(targets, "Modify settings on Seeker");

            var startEndModifierProp = FindProperty("startEndModifier");

            startEndModifierProp.isExpanded = EditorGUILayout.Foldout(startEndModifierProp.isExpanded, startEndModifierProp.displayName);
            if (startEndModifierProp.isExpanded)
            {
                EditorGUI.indentLevel++;
                Popup("startEndModifier.exactStartPoint", exactnessLabels, "Start Point Snapping");
                Popup("startEndModifier.exactEndPoint", exactnessLabels, "End Point Snapping");
                PropertyField("startEndModifier.addPoints", "Add Points");

                if (FindProperty("startEndModifier.exactStartPoint").enumValueIndex == (int)StartEndModifier.Exactness.Original || FindProperty("startEndModifier.exactEndPoint").enumValueIndex == (int)StartEndModifier.Exactness.Original)
                {
                    if (PropertyField("startEndModifier.useRaycasting", "Physics Raycasting"))
                    {
                        EditorGUI.indentLevel++;
                        PropertyField("startEndModifier.mask", "Layer Mask");
                        EditorGUI.indentLevel--;
                        EditorGUILayout.HelpBox("Using raycasting to snap the start/end points has largely been superseded by the 'ClosestOnNode' snapping option. It is both faster and usually closer to what you want to achieve.", MessageType.Info);
                    }

                    if (PropertyField("startEndModifier.useGraphRaycasting", "Graph Raycasting"))
                    {
                        EditorGUILayout.HelpBox("Using raycasting to snap the start/end points has largely been superseded by the 'ClosestOnNode' snapping option. It is both faster and usually closer to what you want to achieve.", MessageType.Info);
                    }
                }

                EditorGUI.indentLevel--;
            }

            tagPenaltiesOpen = EditorGUILayout.Foldout(tagPenaltiesOpen, new GUIContent("Tags", "Settings for each tag"));
            if (tagPenaltiesOpen)
            {
                EditorGUI.indentLevel++;
                string[] tagNames = AstarPath.FindTagNames();
                if (tagNames.Length != 32)
                {
                    tagNames = new string[32];
                    for (int i = 0; i < tagNames.Length; i++)
                    {
                        tagNames[i] = "" + i;
                    }
                }

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.BeginVertical();
                EditorGUILayout.LabelField("Tag", EditorStyles.boldLabel, GUILayout.MaxWidth(120));
                for (int i = 0; i < tagNames.Length; i++)
                {
                    EditorGUILayout.LabelField(tagNames[i], GUILayout.MaxWidth(120));
                }

                // Make sure the arrays are all of the correct size
                for (int i = 0; i < scripts.Count; i++)
                {
                    if (scripts[i].tagPenalties == null || scripts[i].tagPenalties.Length != tagNames.Length)
                    {
                        scripts[i].tagPenalties = new int[tagNames.Length];
                    }
                }

                if (GUILayout.Button("Edit names", EditorStyles.miniButton))
                {
                    AstarPathEditor.EditTags();
                }
                EditorGUILayout.EndVertical();

                EditorGUILayout.BeginVertical();
                EditorGUILayout.LabelField("Penalty", EditorStyles.boldLabel, GUILayout.MaxWidth(100));
                var prop = FindProperty("tagPenalties").FindPropertyRelative("Array");
                prop.Next(true);
                for (int i = 0; i < tagNames.Length; i++)
                {
                    prop.Next(false);
                    EditorGUILayout.PropertyField(prop, GUIContent.none, false, GUILayout.MinWidth(100));
                    // Penalties should not be negative
                    if (prop.intValue < 0)
                    {
                        prop.intValue = 0;
                    }
                }
                if (GUILayout.Button("Reset all", EditorStyles.miniButton))
                {
                    for (int i = 0; i < tagNames.Length; i++)
                    {
                        for (int j = 0; j < scripts.Count; j++)
                        {
                            scripts[j].tagPenalties[i] = 0;
                        }
                    }
                }
                EditorGUILayout.EndVertical();

                EditorGUILayout.BeginVertical();
                EditorGUILayout.LabelField("Traversable", EditorStyles.boldLabel, GUILayout.MaxWidth(100));
                for (int i = 0; i < tagNames.Length; i++)
                {
                    var anyFalse = false;
                    var anyTrue  = false;
                    for (int j = 0; j < scripts.Count; j++)
                    {
                        var prevTraversable = ((scripts[j].traversableTags >> i) & 0x1) != 0;
                        anyTrue  |= prevTraversable;
                        anyFalse |= !prevTraversable;
                    }
                    EditorGUI.BeginChangeCheck();
                    EditorGUI.showMixedValue = anyTrue & anyFalse;
                    var newTraversable = EditorGUILayout.Toggle(anyTrue);
                    EditorGUI.showMixedValue = false;
                    if (EditorGUI.EndChangeCheck())
                    {
                        for (int j = 0; j < scripts.Count; j++)
                        {
                            scripts[j].traversableTags = (scripts[j].traversableTags & ~(1 << i)) | ((newTraversable ? 1 : 0) << i);
                        }
                    }
                }

                if (GUILayout.Button("Set all/none", EditorStyles.miniButton))
                {
                    for (int j = scripts.Count - 1; j >= 0; j--)
                    {
                        scripts[j].traversableTags = (scripts[0].traversableTags & 0x1) == 0 ? -1 : 0;
                    }
                }
                EditorGUILayout.EndVertical();

                EditorGUILayout.EndHorizontal();
            }
        }
 public GraphUpdateProcessor(AstarPath astar)
 {
     this.astar = astar;
 }
 /**
  * SafeOnDestroy should be used when there is a risk that the pathfinding is searching through this graph when called
  */
 public void SafeOnDestroy()
 {
     AstarPath.RegisterSafeUpdate(OnDestroy, false);
 }
Example #25
0
 public NodeLink3Node(AstarPath active) : base(active)
 {
 }
Example #26
0
        public void OnGUI()
        {
            if (!show || (!Application.isPlaying && !showInEditor))
            {
                return;
            }

            if (style == null)
            {
                style = new GUIStyle();
                style.normal.textColor = Color.white;
                style.padding          = new RectOffset(5, 5, 5, 5);
            }

            if (Time.realtimeSinceStartup - lastUpdate > 0.5f || cachedText == null || !Application.isPlaying)
            {
                lastUpdate = Time.realtimeSinceStartup;

                boxRect = new Rect(5, yOffset, 310, 40);

                text.Length = 0;
                text.AppendLine("A* Pathfinding Project Debugger");
                text.Append("A* Version: ").Append(AstarPath.Version.ToString());

                if (showMemProfile)
                {
                    boxRect.height += 200;

                    text.AppendLine();
                    text.AppendLine();
                    text.Append("Currently allocated".PadRight(25));
                    text.Append((allocMem / 1000000F).ToString("0.0 MB"));
                    text.AppendLine();

                    text.Append("Peak allocated".PadRight(25));
                    text.Append((peakAlloc / 1000000F).ToString("0.0 MB")).AppendLine();

                    text.Append("Last collect peak".PadRight(25));
                    text.Append((collectAlloc / 1000000F).ToString("0.0 MB")).AppendLine();


                    text.Append("Allocation rate".PadRight(25));
                    text.Append((allocRate / 1000000F).ToString("0.0 MB")).AppendLine();

                    text.Append("Collection frequency".PadRight(25));
                    text.Append(delta.ToString("0.00"));
                    text.Append("s\n");

                    text.Append("Last collect fps".PadRight(25));
                    text.Append((1F / lastDeltaTime).ToString("0.0 fps"));
                    text.Append(" (");
                    text.Append(lastDeltaTime.ToString("0.000 s"));
                    text.Append(")");
                }

                if (showFPS)
                {
                    text.AppendLine();
                    text.AppendLine();
                    var delayedFPS = delayedDeltaTime > 0.00001f ? 1F / delayedDeltaTime : 0;
                    text.Append("FPS".PadRight(25)).Append(delayedFPS.ToString("0.0 fps"));


                    float minFps = Mathf.Infinity;

                    for (int i = 0; i < fpsDrops.Length; i++)
                    {
                        if (fpsDrops[i] < minFps)
                        {
                            minFps = fpsDrops[i];
                        }
                    }

                    text.AppendLine();
                    text.Append(("Lowest fps (last " + fpsDrops.Length + ")").PadRight(25)).Append(minFps.ToString("0.0"));
                }

                if (showPathProfile)
                {
                    AstarPath astar = AstarPath.active;

                    text.AppendLine();

                    if (astar == null)
                    {
                        text.Append("\nNo AstarPath Object In The Scene");
                    }
                    else
                    {
#if ProfileAstar
                        double searchSpeed = (double)AstarPath.TotalSearchedNodes * 10000 / (double)AstarPath.TotalSearchTime;
                        text.Append("\nSearch Speed	(nodes/ms)	").Append(searchSpeed.ToString("0")).Append(" (" + AstarPath.TotalSearchedNodes + " / ").Append(((double)AstarPath.TotalSearchTime / 10000F).ToString("0") + ")");
#endif

                        if (Pathfinding.Util.ListPool <Vector3> .GetSize() > maxVecPool)
                        {
                            maxVecPool = Pathfinding.Util.ListPool <Vector3> .GetSize();
                        }
                        if (Pathfinding.Util.ListPool <Pathfinding.GraphNode> .GetSize() > maxNodePool)
                        {
                            maxNodePool = Pathfinding.Util.ListPool <Pathfinding.GraphNode> .GetSize();
                        }

                        text.Append("\nPool Sizes (size/total created)");

                        for (int i = 0; i < debugTypes.Length; i++)
                        {
                            debugTypes[i].Print(text);
                        }
                    }
                }

                cachedText = text.ToString();
            }


            if (font != null)
            {
                style.font     = font;
                style.fontSize = fontSize;
            }

            boxRect.height = style.CalcHeight(new GUIContent(cachedText), boxRect.width);

            GUI.Box(boxRect, "");
            GUI.Label(boxRect, cachedText, style);

            if (showGraph)
            {
                float minMem = float.PositiveInfinity, maxMem = 0, minFPS = float.PositiveInfinity, maxFPS = 0;
                for (int i = 0; i < graph.Length; i++)
                {
                    minMem = Mathf.Min(graph[i].memory, minMem);
                    maxMem = Mathf.Max(graph[i].memory, maxMem);
                    minFPS = Mathf.Min(graph[i].fps, minFPS);
                    maxFPS = Mathf.Max(graph[i].fps, maxFPS);
                }

                float line;
                GUI.color = Color.blue;
                // Round to nearest x.x MB
                line = Mathf.RoundToInt(maxMem / (100.0f * 1000));
                GUI.Label(new Rect(5, Screen.height - AstarMath.MapTo(minMem, maxMem, 0 + graphOffset, graphHeight + graphOffset, line * 1000 * 100) - 10, 100, 20), (line / 10.0f).ToString("0.0 MB"));

                line = Mathf.Round(minMem / (100.0f * 1000));
                GUI.Label(new Rect(5, Screen.height - AstarMath.MapTo(minMem, maxMem, 0 + graphOffset, graphHeight + graphOffset, line * 1000 * 100) - 10, 100, 20), (line / 10.0f).ToString("0.0 MB"));

                GUI.color = Color.green;
                // Round to nearest x.x MB
                line = Mathf.Round(maxFPS);
                GUI.Label(new Rect(55, Screen.height - AstarMath.MapTo(minFPS, maxFPS, 0 + graphOffset, graphHeight + graphOffset, line) - 10, 100, 20), line.ToString("0 FPS"));

                line = Mathf.Round(minFPS);
                GUI.Label(new Rect(55, Screen.height - AstarMath.MapTo(minFPS, maxFPS, 0 + graphOffset, graphHeight + graphOffset, line) - 10, 100, 20), line.ToString("0 FPS"));
            }
        }
Example #27
0
 public TriangleMeshNode(AstarPath astar) : base(astar)
 {
 }
Example #28
0
        protected IEnumerable <Progress> ScanAllTiles()
        {
            transform = CalculateTransform();
            InitializeTileInfo();

            // If this is true, just fill the graph with empty tiles
            if (scanEmptyGraph)
            {
                FillWithEmptyTiles();
                yield break;
            }

            // A walkableClimb higher than walkableHeight can cause issues when generating the navmesh since then it can in some cases
            // Both be valid for a character to walk under an obstacle and climb up on top of it (and that cannot be handled with navmesh without links)
            // The editor scripts also enforce this but we enforce it here too just to be sure
            walkableClimb = Mathf.Min(walkableClimb, walkableHeight);

            yield return(new Progress(0, "Finding Meshes"));

            var bounds  = transform.Transform(new Bounds(forcedBoundsSize * 0.5f, forcedBoundsSize));
            var meshes  = CollectMeshes(bounds);
            var buckets = PutMeshesIntoTileBuckets(meshes);

            Queue <Int2> tileQueue = new Queue <Int2>();

            // Put all tiles in the queue
            for (int z = 0; z < tileZCount; z++)
            {
                for (int x = 0; x < tileXCount; x++)
                {
                    tileQueue.Enqueue(new Int2(x, z));
                }
            }

#if UNITY_WEBGL && !UNITY_EDITOR
            // WebGL does not support multithreading so we will do everything synchronously instead
            BuildTiles(tileQueue, buckets, null, 0);
#else
            // Fire up a bunch of threads to scan the graph in parallel
            int threadCount = Mathf.Min(tileQueue.Count, Mathf.Max(1, AstarPath.CalculateThreadCount(ThreadCount.AutomaticHighLoad)));
            var waitEvents  = new ManualResetEvent[threadCount];

            for (int i = 0; i < waitEvents.Length; i++)
            {
                waitEvents[i] = new ManualResetEvent(false);
#if NETFX_CORE
                // Need to make a copy here, otherwise it may refer to some other index when the task actually runs
                var threadIndex = i;
                System.Threading.Tasks.Task.Run(() => BuildTiles(tileQueue, buckets, waitEvents[threadIndex], threadIndex));
#else
                ThreadPool.QueueUserWorkItem(state => BuildTiles(tileQueue, buckets, waitEvents[(int)state], (int)state), i);
#endif
            }

            // Prioritize responsiveness while playing
            // but when not playing prioritize throughput
            // (the Unity progress bar is also pretty slow to update)
            int timeoutMillis = Application.isPlaying ? 1 : 200;

            while (!WaitHandle.WaitAll(waitEvents, timeoutMillis))
            {
                int count;
                lock (tileQueue) count = tileQueue.Count;

                yield return(new Progress(Mathf.Lerp(0.1f, 0.9f, (tiles.Length - count + 1) / (float)tiles.Length), "Generating Tile " + (tiles.Length - count + 1) + "/" + tiles.Length));
            }
#endif

            yield return(new Progress(0.9f, "Assigning Graph Indices"));

            // Assign graph index to nodes
            uint graphIndex = (uint)AstarPath.active.data.GetGraphIndex(this);

            GetNodes(node => node.GraphIndex = graphIndex);

#if UNITY_WEBGL && !UNITY_EDITOR
            // Put all tiles in the queue to be connected
            for (int i = 0; i < tiles.Length; i++)
            {
                tileQueue.Enqueue(new Int2(tiles[i].x, tiles[i].z));
            }

            // Calculate synchronously
            ConnectTiles(tileQueue, null, true, true);
#else
            // First connect all tiles with an EVEN coordinate sum
            // This would be the white squares on a chess board.
            // Then connect all tiles with an ODD coordinate sum (which would be all black squares).
            // This will prevent the different threads that do all
            // this in parallel from conflicting with each other.
            // The directions are also done separately
            // first they are connected along the X direction and then along the Z direction.
            // Looping over 0 and then 1
            for (int coordinateSum = 0; coordinateSum <= 1; coordinateSum++)
            {
                for (int direction = 0; direction <= 1; direction++)
                {
                    for (int i = 0; i < tiles.Length; i++)
                    {
                        if ((tiles[i].x + tiles[i].z) % 2 == coordinateSum)
                        {
                            tileQueue.Enqueue(new Int2(tiles[i].x, tiles[i].z));
                        }
                    }

                    int numTilesInQueue = tileQueue.Count;
                    for (int i = 0; i < waitEvents.Length; i++)
                    {
                        waitEvents[i].Reset();
#if NETFX_CORE
                        var waitEvent = waitEvents[i];
                        System.Threading.Tasks.Task.Run(() => ConnectTiles(tileQueue, waitEvent, direction == 0, direction == 1));
#else
                        ThreadPool.QueueUserWorkItem(state => ConnectTiles(tileQueue, state as ManualResetEvent, direction == 0, direction == 1), waitEvents[i]);
#endif
                    }

                    while (!WaitHandle.WaitAll(waitEvents, timeoutMillis))
                    {
                        int count;
                        lock (tileQueue) {
                            count = tileQueue.Count;
                        }

                        yield return(new Progress(0.95f, "Connecting Tile " + (numTilesInQueue - count) + "/" + numTilesInQueue + " (Phase " + (direction + 1 + 2 * coordinateSum) + " of 4)"));
                    }
                }
            }
#endif

            for (int i = 0; i < meshes.Count; i++)
            {
                meshes[i].Pool();
            }
            ListPool <RasterizationMesh> .Release(meshes);

            // This may be used by the TileHandlerHelper script to update the tiles
            // while taking NavmeshCuts into account after the graph has been completely recalculated.
            if (OnRecalculatedTiles != null)
            {
                OnRecalculatedTiles(tiles.Clone() as NavmeshTile[]);
            }
        }