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--; } }
/// <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 }
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--; } }
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(); } }