NavMeshSurfaceEditor.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. #define NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using UnityEditor.Experimental.SceneManagement;
  6. using UnityEditor.IMGUI.Controls;
  7. using UnityEditor.SceneManagement;
  8. using UnityEditorInternal;
  9. using UnityEngine.AI;
  10. using UnityEngine;
  11. namespace UnityEditor.AI
  12. {
  13. [CanEditMultipleObjects]
  14. [CustomEditor(typeof(NavMeshSurface))]
  15. class NavMeshSurfaceEditor : Editor
  16. {
  17. SerializedProperty m_AgentTypeID;
  18. SerializedProperty m_BuildHeightMesh;
  19. SerializedProperty m_Center;
  20. SerializedProperty m_CollectObjects;
  21. SerializedProperty m_DefaultArea;
  22. SerializedProperty m_LayerMask;
  23. SerializedProperty m_OverrideTileSize;
  24. SerializedProperty m_OverrideVoxelSize;
  25. SerializedProperty m_Size;
  26. SerializedProperty m_TileSize;
  27. SerializedProperty m_UseGeometry;
  28. SerializedProperty m_VoxelSize;
  29. #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  30. SerializedProperty m_NavMeshData;
  31. #endif
  32. class Styles
  33. {
  34. public readonly GUIContent m_LayerMask = new GUIContent("Include Layers");
  35. public readonly GUIContent m_ShowInputGeom = new GUIContent("Show Input Geom");
  36. public readonly GUIContent m_ShowVoxels = new GUIContent("Show Voxels");
  37. public readonly GUIContent m_ShowRegions = new GUIContent("Show Regions");
  38. public readonly GUIContent m_ShowRawContours = new GUIContent("Show Raw Contours");
  39. public readonly GUIContent m_ShowContours = new GUIContent("Show Contours");
  40. public readonly GUIContent m_ShowPolyMesh = new GUIContent("Show Poly Mesh");
  41. public readonly GUIContent m_ShowPolyMeshDetail = new GUIContent("Show Poly Mesh Detail");
  42. }
  43. static Styles s_Styles;
  44. static bool s_ShowDebugOptions;
  45. static Color s_HandleColor = new Color(127f, 214f, 244f, 100f) / 255;
  46. static Color s_HandleColorSelected = new Color(127f, 214f, 244f, 210f) / 255;
  47. static Color s_HandleColorDisabled = new Color(127f * 0.75f, 214f * 0.75f, 244f * 0.75f, 100f) / 255;
  48. BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle();
  49. bool editingCollider
  50. {
  51. get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); }
  52. }
  53. void OnEnable()
  54. {
  55. m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID");
  56. m_BuildHeightMesh = serializedObject.FindProperty("m_BuildHeightMesh");
  57. m_Center = serializedObject.FindProperty("m_Center");
  58. m_CollectObjects = serializedObject.FindProperty("m_CollectObjects");
  59. m_DefaultArea = serializedObject.FindProperty("m_DefaultArea");
  60. m_LayerMask = serializedObject.FindProperty("m_LayerMask");
  61. m_OverrideTileSize = serializedObject.FindProperty("m_OverrideTileSize");
  62. m_OverrideVoxelSize = serializedObject.FindProperty("m_OverrideVoxelSize");
  63. m_Size = serializedObject.FindProperty("m_Size");
  64. m_TileSize = serializedObject.FindProperty("m_TileSize");
  65. m_UseGeometry = serializedObject.FindProperty("m_UseGeometry");
  66. m_VoxelSize = serializedObject.FindProperty("m_VoxelSize");
  67. #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  68. m_NavMeshData = serializedObject.FindProperty("m_NavMeshData");
  69. #endif
  70. NavMeshVisualizationSettings.showNavigation++;
  71. }
  72. void OnDisable()
  73. {
  74. NavMeshVisualizationSettings.showNavigation--;
  75. }
  76. Bounds GetBounds()
  77. {
  78. var navSurface = (NavMeshSurface)target;
  79. return new Bounds(navSurface.transform.position, navSurface.size);
  80. }
  81. public override void OnInspectorGUI()
  82. {
  83. if (s_Styles == null)
  84. s_Styles = new Styles();
  85. serializedObject.Update();
  86. var bs = NavMesh.GetSettingsByID(m_AgentTypeID.intValue);
  87. if (bs.agentTypeID != -1)
  88. {
  89. // Draw image
  90. const float diagramHeight = 80.0f;
  91. Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, diagramHeight);
  92. NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, bs.agentRadius, bs.agentHeight, bs.agentClimb, bs.agentSlope);
  93. }
  94. NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID);
  95. EditorGUILayout.Space();
  96. EditorGUILayout.PropertyField(m_CollectObjects);
  97. if ((CollectObjects)m_CollectObjects.enumValueIndex == CollectObjects.Volume)
  98. {
  99. EditorGUI.indentLevel++;
  100. EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume",
  101. EditorGUIUtility.IconContent("EditCollider"), GetBounds, this);
  102. EditorGUILayout.PropertyField(m_Size);
  103. EditorGUILayout.PropertyField(m_Center);
  104. EditorGUI.indentLevel--;
  105. }
  106. else
  107. {
  108. if (editingCollider)
  109. EditMode.QuitEditMode();
  110. }
  111. EditorGUILayout.PropertyField(m_LayerMask, s_Styles.m_LayerMask);
  112. EditorGUILayout.PropertyField(m_UseGeometry);
  113. EditorGUILayout.Space();
  114. m_OverrideVoxelSize.isExpanded = EditorGUILayout.Foldout(m_OverrideVoxelSize.isExpanded, "Advanced");
  115. if (m_OverrideVoxelSize.isExpanded)
  116. {
  117. EditorGUI.indentLevel++;
  118. NavMeshComponentsGUIUtility.AreaPopup("Default Area", m_DefaultArea);
  119. // Override voxel size.
  120. EditorGUILayout.PropertyField(m_OverrideVoxelSize);
  121. using (new EditorGUI.DisabledScope(!m_OverrideVoxelSize.boolValue || m_OverrideVoxelSize.hasMultipleDifferentValues))
  122. {
  123. EditorGUI.indentLevel++;
  124. EditorGUILayout.PropertyField(m_VoxelSize);
  125. if (!m_OverrideVoxelSize.hasMultipleDifferentValues)
  126. {
  127. if (!m_AgentTypeID.hasMultipleDifferentValues)
  128. {
  129. float voxelsPerRadius = m_VoxelSize.floatValue > 0.0f ? (bs.agentRadius / m_VoxelSize.floatValue) : 0.0f;
  130. EditorGUILayout.LabelField(" ", voxelsPerRadius.ToString("0.00") + " voxels per agent radius", EditorStyles.miniLabel);
  131. }
  132. if (m_OverrideVoxelSize.boolValue)
  133. EditorGUILayout.HelpBox("Voxel size controls how accurately the navigation mesh is generated from the level geometry. A good voxel size is 2-4 voxels per agent radius. Making voxel size smaller will increase build time.", MessageType.None);
  134. }
  135. EditorGUI.indentLevel--;
  136. }
  137. // Override tile size
  138. EditorGUILayout.PropertyField(m_OverrideTileSize);
  139. using (new EditorGUI.DisabledScope(!m_OverrideTileSize.boolValue || m_OverrideTileSize.hasMultipleDifferentValues))
  140. {
  141. EditorGUI.indentLevel++;
  142. EditorGUILayout.PropertyField(m_TileSize);
  143. if (!m_TileSize.hasMultipleDifferentValues && !m_VoxelSize.hasMultipleDifferentValues)
  144. {
  145. float tileWorldSize = m_TileSize.intValue * m_VoxelSize.floatValue;
  146. EditorGUILayout.LabelField(" ", tileWorldSize.ToString("0.00") + " world units", EditorStyles.miniLabel);
  147. }
  148. if (!m_OverrideTileSize.hasMultipleDifferentValues)
  149. {
  150. if (m_OverrideTileSize.boolValue)
  151. EditorGUILayout.HelpBox("Tile size controls the how local the changes to the world are (rebuild or carve). Small tile size allows more local changes, while potentially generating more data overall.", MessageType.None);
  152. }
  153. EditorGUI.indentLevel--;
  154. }
  155. // Height mesh
  156. using (new EditorGUI.DisabledScope(true))
  157. {
  158. EditorGUILayout.PropertyField(m_BuildHeightMesh);
  159. }
  160. EditorGUILayout.Space();
  161. EditorGUI.indentLevel--;
  162. }
  163. EditorGUILayout.Space();
  164. serializedObject.ApplyModifiedProperties();
  165. var hadError = false;
  166. var multipleTargets = targets.Length > 1;
  167. foreach (NavMeshSurface navSurface in targets)
  168. {
  169. var settings = navSurface.GetBuildSettings();
  170. // Calculating bounds is potentially expensive when unbounded - so here we just use the center/size.
  171. // It means the validation is not checking vertical voxel limit correctly when the surface is set to something else than "in volume".
  172. var bounds = new Bounds(Vector3.zero, Vector3.zero);
  173. if (navSurface.collectObjects == CollectObjects.Volume)
  174. {
  175. bounds = new Bounds(navSurface.center, navSurface.size);
  176. }
  177. var errors = settings.ValidationReport(bounds);
  178. if (errors.Length > 0)
  179. {
  180. if (multipleTargets)
  181. EditorGUILayout.LabelField(navSurface.name);
  182. foreach (var err in errors)
  183. {
  184. EditorGUILayout.HelpBox(err, MessageType.Warning);
  185. }
  186. GUILayout.BeginHorizontal();
  187. GUILayout.Space(EditorGUIUtility.labelWidth);
  188. if (GUILayout.Button("Open Agent Settings...", EditorStyles.miniButton))
  189. NavMeshEditorHelpers.OpenAgentSettings(navSurface.agentTypeID);
  190. GUILayout.EndHorizontal();
  191. hadError = true;
  192. }
  193. }
  194. if (hadError)
  195. EditorGUILayout.Space();
  196. #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  197. var nmdRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
  198. EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData);
  199. var rectLabel = EditorGUI.PrefixLabel(nmdRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(m_NavMeshData.displayName));
  200. EditorGUI.EndProperty();
  201. using (new EditorGUI.DisabledScope(true))
  202. {
  203. EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData);
  204. EditorGUI.ObjectField(rectLabel, m_NavMeshData, GUIContent.none);
  205. EditorGUI.EndProperty();
  206. }
  207. #endif
  208. using (new EditorGUI.DisabledScope(Application.isPlaying || m_AgentTypeID.intValue == -1))
  209. {
  210. GUILayout.BeginHorizontal();
  211. GUILayout.Space(EditorGUIUtility.labelWidth);
  212. if (GUILayout.Button("Clear"))
  213. {
  214. NavMeshAssetManager.instance.ClearSurfaces(targets);
  215. SceneView.RepaintAll();
  216. }
  217. if (GUILayout.Button("Bake"))
  218. {
  219. NavMeshAssetManager.instance.StartBakingSurfaces(targets);
  220. }
  221. GUILayout.EndHorizontal();
  222. }
  223. // Show progress for the selected targets
  224. var bakeOperations = NavMeshAssetManager.instance.GetBakeOperations();
  225. for (int i = bakeOperations.Count - 1; i >= 0; --i)
  226. {
  227. if (!targets.Contains(bakeOperations[i].surface))
  228. continue;
  229. var oper = bakeOperations[i].bakeOperation;
  230. if (oper == null)
  231. continue;
  232. var p = oper.progress;
  233. if (oper.isDone)
  234. {
  235. SceneView.RepaintAll();
  236. continue;
  237. }
  238. GUILayout.BeginHorizontal();
  239. if (GUILayout.Button("Cancel", EditorStyles.miniButton))
  240. {
  241. var bakeData = bakeOperations[i].bakeData;
  242. UnityEngine.AI.NavMeshBuilder.Cancel(bakeData);
  243. bakeOperations.RemoveAt(i);
  244. }
  245. EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(), p, "Baking: " + (int)(100 * p) + "%");
  246. if (p <= 1)
  247. Repaint();
  248. GUILayout.EndHorizontal();
  249. }
  250. }
  251. [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)]
  252. static void RenderBoxGizmoSelected(NavMeshSurface navSurface, GizmoType gizmoType)
  253. {
  254. RenderBoxGizmo(navSurface, gizmoType, true);
  255. }
  256. [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)]
  257. static void RenderBoxGizmoNotSelected(NavMeshSurface navSurface, GizmoType gizmoType)
  258. {
  259. if (NavMeshVisualizationSettings.showNavigation > 0)
  260. RenderBoxGizmo(navSurface, gizmoType, false);
  261. else
  262. Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true);
  263. }
  264. static void RenderBoxGizmo(NavMeshSurface navSurface, GizmoType gizmoType, bool selected)
  265. {
  266. var color = selected ? s_HandleColorSelected : s_HandleColor;
  267. if (!navSurface.enabled)
  268. color = s_HandleColorDisabled;
  269. var oldColor = Gizmos.color;
  270. var oldMatrix = Gizmos.matrix;
  271. // Use the unscaled matrix for the NavMeshSurface
  272. var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one);
  273. Gizmos.matrix = localToWorld;
  274. if (navSurface.collectObjects == CollectObjects.Volume)
  275. {
  276. Gizmos.color = color;
  277. Gizmos.DrawWireCube(navSurface.center, navSurface.size);
  278. if (selected && navSurface.enabled)
  279. {
  280. var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f);
  281. Gizmos.color = colorTrans;
  282. Gizmos.DrawCube(navSurface.center, navSurface.size);
  283. }
  284. }
  285. else
  286. {
  287. if (navSurface.navMeshData != null)
  288. {
  289. var bounds = navSurface.navMeshData.sourceBounds;
  290. Gizmos.color = Color.grey;
  291. Gizmos.DrawWireCube(bounds.center, bounds.size);
  292. }
  293. }
  294. Gizmos.matrix = oldMatrix;
  295. Gizmos.color = oldColor;
  296. Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true);
  297. }
  298. void OnSceneGUI()
  299. {
  300. if (!editingCollider)
  301. return;
  302. var navSurface = (NavMeshSurface)target;
  303. var color = navSurface.enabled ? s_HandleColor : s_HandleColorDisabled;
  304. var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one);
  305. using (new Handles.DrawingScope(color, localToWorld))
  306. {
  307. m_BoundsHandle.center = navSurface.center;
  308. m_BoundsHandle.size = navSurface.size;
  309. EditorGUI.BeginChangeCheck();
  310. m_BoundsHandle.DrawHandle();
  311. if (EditorGUI.EndChangeCheck())
  312. {
  313. Undo.RecordObject(navSurface, "Modified NavMesh Surface");
  314. Vector3 center = m_BoundsHandle.center;
  315. Vector3 size = m_BoundsHandle.size;
  316. navSurface.center = center;
  317. navSurface.size = size;
  318. EditorUtility.SetDirty(target);
  319. }
  320. }
  321. }
  322. [MenuItem("GameObject/AI/NavMesh Surface", false, 2000)]
  323. public static void CreateNavMeshSurface(MenuCommand menuCommand)
  324. {
  325. var parent = menuCommand.context as GameObject;
  326. var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Surface", parent);
  327. go.AddComponent<NavMeshSurface>();
  328. var view = SceneView.lastActiveSceneView;
  329. if (view != null)
  330. view.MoveToView(go.transform);
  331. }
  332. }
  333. }