protected void DrawErosion(GridGraph graph)
        {
            graph.erodeIterations = EditorGUILayout.IntField(new GUIContent("Erosion iterations", "Sets how many times the graph should be eroded. This adds extra margin to objects."), graph.erodeIterations);
            graph.erodeIterations = graph.erodeIterations < 0 ? 0 : (graph.erodeIterations > 16 ? 16 : graph.erodeIterations);             //Clamp iterations to [0,16]

            if (graph.erodeIterations > 0)
            {
                EditorGUI.indentLevel++;
                graph.erosionUseTags = EditorGUILayout.Toggle(new GUIContent("Erosion Uses Tags", "Instead of making nodes unwalkable, " +
                                                                             "nodes will have their tag set to a value corresponding to their erosion level, " +
                                                                             "which is a quite good measurement of their distance to the closest wall.\nSee online documentation for more info."),
                                                              graph.erosionUseTags);
                if (graph.erosionUseTags)
                {
                    EditorGUI.indentLevel++;
                    graph.erosionFirstTag = EditorGUILayoutx.TagField("First Tag", graph.erosionFirstTag, () => AstarPathEditor.EditTags());
                    EditorGUI.indentLevel--;
                }
                EditorGUI.indentLevel--;
            }
        }
Example #2
0
        /// <summary>Exports the INavmesh graph to a .obj file</summary>
        public static void ExportToFile(RecastGraph target)
        {
            //INavmesh graph = (INavmesh)target;
            if (target == null)
            {
                return;
            }

            NavmeshTile[] tiles = target.GetTiles();

            if (tiles == null)
            {
                if (EditorUtility.DisplayDialog("Scan graph before exporting?", "The graph does not contain any mesh data. Do you want to scan it?", "Ok", "Cancel"))
                {
                    AstarPathEditor.MenuScan();
                    tiles = target.GetTiles();
                    if (tiles == null)
                    {
                        return;
                    }
                }
                else
                {
                    return;
                }
            }

            string path = EditorUtility.SaveFilePanel("Export .obj", "", "navmesh.obj", "obj");

            if (path == "")
            {
                return;
            }

            //Generate .obj
            var sb = new System.Text.StringBuilder();

            string name = System.IO.Path.GetFileNameWithoutExtension(path);

            sb.Append("g ").Append(name).AppendLine();

            //Vertices start from 1
            int vCount = 1;

            //Define single texture coordinate to zero
            sb.Append("vt 0 0\n");

            for (int t = 0; t < tiles.Length; t++)
            {
                NavmeshTile tile = tiles[t];

                if (tile == null)
                {
                    continue;
                }

                Int3[] vertices = tile.verts;

                //Write vertices
                for (int i = 0; i < vertices.Length; i++)
                {
                    var v = (Vector3)vertices[i];
                    sb.Append(string.Format("v {0} {1} {2}\n", -v.x, v.y, v.z));
                }

                //Write triangles
                TriangleMeshNode[] nodes = tile.nodes;
                for (int i = 0; i < nodes.Length; i++)
                {
                    TriangleMeshNode node = nodes[i];
                    if (node == null)
                    {
                        Debug.LogError("Node was null or no TriangleMeshNode. Critical error. Graph type " + target.GetType().Name);
                        return;
                    }
                    if (node.GetVertexArrayIndex(0) < 0 || node.GetVertexArrayIndex(0) >= vertices.Length)
                    {
                        throw new System.Exception("ERR");
                    }

                    sb.Append(string.Format("f {0}/1 {1}/1 {2}/1\n", (node.GetVertexArrayIndex(0) + vCount), (node.GetVertexArrayIndex(1) + vCount), (node.GetVertexArrayIndex(2) + vCount)));
                }

                vCount += vertices.Length;
            }

            string obj = sb.ToString();

            using (var sw = new System.IO.StreamWriter(path))
            {
                sw.Write(obj);
            }
        }
        private static void ShowUpdateWindowIfRelevant()
        {
#if !ASTAR_ATAVISM
            try
            {
                System.DateTime remindDate;
                var             remindVersion = new System.Version(EditorPrefs.GetString("AstarRemindUpdateVersion", "0.0.0.0"));
                if (latestVersion == remindVersion && System.DateTime.TryParse(EditorPrefs.GetString("AstarRemindUpdateDate", "1/1/1971 00:00:01"), out remindDate))
                {
                    if (System.DateTime.UtcNow < remindDate)
                    {
                        // Don't remind yet
                        return;
                    }
                }
                else
                {
                    EditorPrefs.DeleteKey("AstarRemindUpdateDate");
                    EditorPrefs.DeleteKey("AstarRemindUpdateVersion");
                }
            }
            catch
            {
                Debug.LogError("Invalid AstarRemindUpdateVersion or AstarRemindUpdateDate");
            }

            var skipVersion = new System.Version(EditorPrefs.GetString("AstarSkipUpToVersion", AstarPath.Version.ToString()));

            if (AstarPathEditor.FullyDefinedVersion(latestVersion) != AstarPathEditor.FullyDefinedVersion(skipVersion) && AstarPathEditor.FullyDefinedVersion(latestVersion) > AstarPathEditor.FullyDefinedVersion(AstarPath.Version))
            {
                EditorPrefs.DeleteKey("AstarSkipUpToVersion");
                EditorPrefs.DeleteKey("AstarRemindUpdateDate");
                EditorPrefs.DeleteKey("AstarRemindUpdateVersion");

                AstarUpdateWindow.Init(latestVersion, latestVersionDescription);
            }
#endif
        }
Example #4
0
        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--;
            }

            // Make sure the AstarPath object is initialized and the graphs are loaded, this is required to be able to show graph names in the mask popup
            AstarPath.FindAstarPath();

            for (int i = 0; i < graphLabels.Length; i++)
            {
                if (AstarPath.active == null || AstarPath.active.data.graphs == null || i >= AstarPath.active.data.graphs.Length || AstarPath.active.data.graphs[i] == null)
                {
                    graphLabels[i] = "Graph " + i + (i == 31 ? "+" : "");
                }
                else
                {
                    graphLabels[i] = AstarPath.active.data.graphs[i].name + " (graph " + i + ")";
                }
            }

            Mask("graphMask", graphLabels, "Traversable Graphs");

            tagPenaltiesOpen = EditorGUILayout.Foldout(tagPenaltiesOpen, new GUIContent("Tags", "Settings for each tag"));
            if (tagPenaltiesOpen)
            {
                string[] tagNames = AstarPath.FindTagNames();
                EditorGUI.indentLevel++;
                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();
            }
        }
        void DrawTagField()
        {
            if (PropertyField("modifyTag"))
            {
                var tagValue = FindProperty("setTag");
                EditorGUI.indentLevel++;
                EditorGUI.showMixedValue = tagValue.hasMultipleDifferentValues;
                EditorGUI.BeginChangeCheck();
                var newTag = EditorGUILayoutx.TagField("Tag Value", tagValue.intValue, () => AstarPathEditor.EditTags());
                if (EditorGUI.EndChangeCheck())
                {
                    tagValue.intValue = newTag;
                }

                if (GUILayout.Button("Tags can be used to restrict which units can walk on what ground. Click here for more info", "HelpBox"))
                {
                    Application.OpenURL(AstarUpdateChecker.GetURL("tags"));
                }
                EditorGUI.indentLevel--;
            }
        }
Example #6
0
        void OnGUI()
        {
            if (largeStyle == null)
            {
                largeStyle           = EditorStyles.largeLabel;
                largeStyle.fontSize  = 32;
                largeStyle.alignment = TextAnchor.UpperCenter;
                largeStyle.richText  = true;

                normalStyle          = EditorStyles.label;
                normalStyle.wordWrap = true;
                normalStyle.richText = true;
            }

            if (version == null)
            {
                return;
            }

            //GUILayout.BeginHorizontal ();
            //GUILayout.FlexibleSpace();
            GUILayout.Label("New Update Available!", largeStyle);
            GUILayout.Label("There is a new version of the <b>A* Pathfinding Project</b> available for download.\n" +
                            "The new version is <b>" + version.ToString() + "</b> you have <b>" + AstarPath.Version.ToString() + "</b>\n\n" +
                            "<i>Summary:</i>\n" + summary, normalStyle
                            );

            GUILayout.FlexibleSpace();

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();

            GUILayout.BeginVertical();

            Color col = GUI.color;

            GUI.backgroundColor *= new Color(0.5f, 1f, 0.5f);
            if (GUILayout.Button("Take me to the download page!", GUILayout.Height(30), GUILayout.MaxWidth(300)))
            {
                Application.OpenURL(downloadURL);
            }
            GUI.backgroundColor = col;


            if (GUILayout.Button("What's new? (full changelog)"))
            {
                Application.OpenURL(AstarPathEditor.GetURL("changelog"));
            }

            GUILayout.EndVertical();

            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            //GUILayout.Space ( 90 );
            GUILayout.FlexibleSpace();

            GUILayout.BeginHorizontal();
            //GUILayout.FlexibleSpace ();

            if (GUILayout.Button("Skip this version", GUILayout.MaxWidth(100)))
            {
                EditorPrefs.SetString("AstarSkipUpToVersion", version.ToString());
                setReminder = true;
                Close();
            }

            if (GUILayout.Button("Remind me later ( 1 week )", GUILayout.MaxWidth(200)))
            {
                EditorPrefs.SetString("AstarRemindUpdateDate", System.DateTime.UtcNow.AddDays(7).ToString(System.Globalization.CultureInfo.InvariantCulture));
                EditorPrefs.SetString("AstarRemindUpdateVersion", version.ToString());
                setReminder = true;
                Close();
            }

            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();
            //GUILayout.FlexibleSpace();
            //GUILayout.EndHorizontal ();
        }
        public void OnSceneGUI()
        {
            var script = target as GraphUpdateScene;

            // Don't allow editing unless it is the active object
            if (Selection.activeGameObject != script.gameObject || script.legacyMode)
            {
                return;
            }

            // Make sure the points array is not null
            if (script.points == null)
            {
                script.points = new Vector3[0];
                EditorUtility.SetDirty(script);
            }

            List <Vector3> points = Pathfinding.Util.ListPool <Vector3> .Claim();

            points.AddRange(script.points);

            Matrix4x4 invMatrix = script.transform.worldToLocalMatrix;

            Matrix4x4 matrix = script.transform.localToWorldMatrix;

            for (int i = 0; i < points.Count; i++)
            {
                points[i] = matrix.MultiplyPoint3x4(points[i]);
            }


            float minScreenDist = float.PositiveInfinity;

            if (Tools.current != Tool.View && Event.current.type == EventType.Layout)
            {
                for (int i = 0; i < script.points.Length; i++)
                {
                    float dist = HandleUtility.DistanceToLine(points[i], points[i]);
                    HandleUtility.AddControl(-i - 1, dist);
                    minScreenDist = Mathf.Min(dist, minScreenDist);
                }
            }

            // If there is a point sort of close to the cursor, but not close enough
            // to receive a click event, then prevent the user from accidentally clicking
            // which would deselect the current object and be kinda annoying.
            if (Tools.current != Tool.View && minScreenDist < 50)
            {
                HandleUtility.AddDefaultControl(0);
            }

            for (int i = 0; i < points.Count; i++)
            {
                if (i == selectedPoint && Tools.current == Tool.Move)
                {
                    Handles.color = PointSelectedColor;
                    SphereCap(-i - 1, points[i], Quaternion.identity, HandleUtility.GetHandleSize(points[i]) * pointGizmosRadius * 2);

                    Vector3 pre  = points[i];
                    Vector3 post = Handles.PositionHandle(points[i], Quaternion.identity);
                    if (pre != post)
                    {
                        Undo.RecordObject(script, "Moved Point");
                        script.points[i] = invMatrix.MultiplyPoint3x4(post);
                    }
                }
                else
                {
                    Handles.color = PointColor;
                    SphereCap(-i - 1, points[i], Quaternion.identity, HandleUtility.GetHandleSize(points[i]) * pointGizmosRadius);
                }
            }

            if (Event.current.type == EventType.MouseDown)
            {
                int pre = selectedPoint;
                selectedPoint = -(HandleUtility.nearestControl + 1);
                if (pre != selectedPoint)
                {
                    GUI.changed = true;
                }
            }

            if (Tools.current == Tool.Move)
            {
                var darkSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);

                Handles.BeginGUI();
                float width  = 220;
                float height = 76;
                float margin = 10;

                AstarPathEditor.LoadStyles();
                GUILayout.BeginArea(new Rect(Camera.current.pixelWidth - width, Camera.current.pixelHeight - height, width - margin, height - margin), "Shortcuts", AstarPathEditor.astarSkin.FindStyle("SceneBoxDark"));

                GUILayout.Label("Shift+Click: Add new point", darkSkin.label);
                GUILayout.Label("Backspace: Delete selected point", darkSkin.label);

                // Invisible button to capture clicks. This prevents a click inside the box from causing some other GameObject to be selected.
                GUI.Button(new Rect(0, 0, width - margin, height - margin), "", GUIStyle.none);
                GUILayout.EndArea();

                Handles.EndGUI();
            }

            if (Tools.current == Tool.Move && Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Backspace && selectedPoint >= 0 && selectedPoint < points.Count)
            {
                Undo.RecordObject(script, "Removed Point");
                var arr = new List <Vector3>(script.points);
                arr.RemoveAt(selectedPoint);
                points.RemoveAt(selectedPoint);
                script.points = arr.ToArray();
                GUI.changed   = true;
            }

            if (Event.current.shift && Tools.current == Tool.Move)
            {
                HandleUtility.Repaint();

                // Find the closest segment
                int   insertionIndex = points.Count;
                float minDist        = float.PositiveInfinity;
                for (int i = 0; i < points.Count; i++)
                {
                    float dist = HandleUtility.DistanceToLine(points[i], points[(i + 1) % points.Count]);
                    if (dist < minDist)
                    {
                        insertionIndex = i + 1;
                        minDist        = dist;
                    }
                }

                var           ray    = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
                System.Object hit    = HandleUtility.RaySnap(ray);
                Vector3       rayhit = Vector3.zero;
                bool          didHit = false;
                if (hit != null)
                {
                    rayhit = ((RaycastHit)hit).point;
                    didHit = true;
                }
                else
                {
                    var   plane = new Plane(script.transform.up, script.transform.position);
                    float distance;
                    plane.Raycast(ray, out distance);
                    if (distance > 0)
                    {
                        rayhit = ray.GetPoint(distance);
                        didHit = true;
                    }
                }

                if (didHit)
                {
                    if (Event.current.type == EventType.MouseDown)
                    {
                        points.Insert(insertionIndex, rayhit);

                        Undo.RecordObject(script, "Added Point");
                        var arr = new List <Vector3>(script.points);
                        arr.Insert(insertionIndex, invMatrix.MultiplyPoint3x4(rayhit));
                        script.points = arr.ToArray();
                        GUI.changed   = true;
                    }
                    else if (points.Count > 0)
                    {
                        Handles.color = Color.green;
                        Handles.DrawDottedLine(points[(insertionIndex - 1 + points.Count) % points.Count], rayhit, 8);
                        Handles.DrawDottedLine(points[insertionIndex % points.Count], rayhit, 8);
                        SphereCap(0, rayhit, Quaternion.identity, HandleUtility.GetHandleSize(rayhit) * pointGizmosRadius);
                        // Project point down onto a plane
                        var zeroed = invMatrix.MultiplyPoint3x4(rayhit);
                        zeroed.y      = 0;
                        Handles.color = new Color(1, 1, 1, 0.5f);
                        Handles.DrawDottedLine(matrix.MultiplyPoint3x4(zeroed), rayhit, 4);
                    }
                }

                if (Event.current.type == EventType.MouseDown)
                {
                    Event.current.Use();
                }
            }

            // Make sure the convex hull stays up to date
            script.RecalcConvex();
            Pathfinding.Util.ListPool <Vector3> .Release(ref points);

            if (GUI.changed)
            {
                HandleUtility.Repaint();
            }
        }