Nessuna descrizione
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NavMeshSurfaceEditor.cs 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #define NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  2. using System;
  3. using UnityEditor;
  4. using UnityEditor.AI;
  5. #if !UNITY_2021_2_OR_NEWER
  6. using UnityEditor.Experimental.SceneManagement;
  7. #endif
  8. using UnityEditor.SceneManagement;
  9. using UnityEditor.IMGUI.Controls;
  10. using UnityEditorInternal;
  11. using UnityEngine.AI;
  12. using UnityEngine;
  13. using System.Linq;
  14. namespace Unity.AI.Navigation.Editor
  15. {
  16. [CanEditMultipleObjects]
  17. [CustomEditor(typeof(NavMeshSurface))]
  18. class NavMeshSurfaceEditor : UnityEditor.Editor
  19. {
  20. SerializedProperty m_AgentTypeID;
  21. SerializedProperty m_BuildHeightMesh;
  22. SerializedProperty m_Center;
  23. SerializedProperty m_CollectObjects;
  24. SerializedProperty m_DefaultArea;
  25. #if UNITY_2022_2_OR_NEWER
  26. SerializedProperty m_GenerateLinks;
  27. #endif
  28. SerializedProperty m_LayerMask;
  29. SerializedProperty m_OverrideTileSize;
  30. SerializedProperty m_OverrideVoxelSize;
  31. SerializedProperty m_Size;
  32. SerializedProperty m_TileSize;
  33. SerializedProperty m_UseGeometry;
  34. SerializedProperty m_VoxelSize;
  35. SerializedProperty m_MinRegionArea;
  36. SerializedProperty m_LedgeDropHeight;
  37. SerializedProperty m_MaxJumpAcrossDistance;
  38. #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  39. SerializedProperty m_NavMeshData;
  40. #endif
  41. class Styles
  42. {
  43. internal readonly GUIContent m_LayerMask = new GUIContent("Include Layers");
  44. internal readonly GUIContent m_MinRegionArea = new GUIContent("Minimum Region Area");
  45. internal readonly GUIContent m_GenerateLinks = new GUIContent(
  46. "Generate Links",
  47. "If enabled, collected objects will generate links depending on the agent settings values for drop height and jump distance.\nThis behaviour can be overriden using NavMeshModifier components.");
  48. }
  49. static Styles s_Styles;
  50. static bool s_ShowDebugOptions;
  51. static Color s_HandleColor = new Color(127f, 214f, 244f, 100f) / 255;
  52. static Color s_HandleColorSelected = new Color(127f, 214f, 244f, 210f) / 255;
  53. static Color s_HandleColorDisabled = new Color(127f * 0.75f, 214f * 0.75f, 244f * 0.75f, 100f) / 255;
  54. BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle();
  55. bool editingCollider
  56. {
  57. get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); }
  58. }
  59. void OnEnable()
  60. {
  61. m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID");
  62. m_BuildHeightMesh = serializedObject.FindProperty("m_BuildHeightMesh");
  63. m_Center = serializedObject.FindProperty("m_Center");
  64. m_CollectObjects = serializedObject.FindProperty("m_CollectObjects");
  65. m_DefaultArea = serializedObject.FindProperty("m_DefaultArea");
  66. #if UNITY_2022_2_OR_NEWER
  67. m_GenerateLinks = serializedObject.FindProperty("m_GenerateLinks");
  68. #endif
  69. m_LayerMask = serializedObject.FindProperty("m_LayerMask");
  70. m_OverrideTileSize = serializedObject.FindProperty("m_OverrideTileSize");
  71. m_OverrideVoxelSize = serializedObject.FindProperty("m_OverrideVoxelSize");
  72. m_Size = serializedObject.FindProperty("m_Size");
  73. m_TileSize = serializedObject.FindProperty("m_TileSize");
  74. m_UseGeometry = serializedObject.FindProperty("m_UseGeometry");
  75. m_VoxelSize = serializedObject.FindProperty("m_VoxelSize");
  76. m_MinRegionArea = serializedObject.FindProperty("m_MinRegionArea");
  77. #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  78. m_NavMeshData = serializedObject.FindProperty("m_NavMeshData");
  79. #endif
  80. #if !UNITY_2022_2_OR_NEWER
  81. NavMeshVisualizationSettings.showNavigation++;
  82. #endif
  83. }
  84. #if !UNITY_2022_2_OR_NEWER
  85. void OnDisable()
  86. {
  87. NavMeshVisualizationSettings.showNavigation--;
  88. }
  89. #endif
  90. Bounds GetBounds()
  91. {
  92. var navSurface = (NavMeshSurface)target;
  93. return new Bounds(navSurface.transform.position, navSurface.size);
  94. }
  95. public override void OnInspectorGUI()
  96. {
  97. if (s_Styles == null)
  98. s_Styles = new Styles();
  99. serializedObject.Update();
  100. var bs = NavMesh.GetSettingsByID(m_AgentTypeID.intValue);
  101. if (bs.agentTypeID != -1)
  102. {
  103. // Draw image
  104. const float diagramHeight = 80.0f;
  105. Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, diagramHeight);
  106. NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, bs.agentRadius, bs.agentHeight, bs.agentClimb, bs.agentSlope);
  107. }
  108. NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID);
  109. NavMeshComponentsGUIUtility.AreaPopup("Default Area", m_DefaultArea);
  110. #if UNITY_2022_2_OR_NEWER
  111. EditorGUILayout.PropertyField(m_GenerateLinks, s_Styles.m_GenerateLinks);
  112. #endif
  113. EditorGUILayout.PropertyField(m_UseGeometry);
  114. m_CollectObjects.isExpanded = EditorGUILayout.Foldout(m_CollectObjects.isExpanded, "Object Collection");
  115. if (m_CollectObjects.isExpanded)
  116. {
  117. EditorGUI.indentLevel++;
  118. EditorGUILayout.PropertyField(m_CollectObjects);
  119. if ((CollectObjects)m_CollectObjects.enumValueIndex == CollectObjects.Volume)
  120. {
  121. EditorGUI.indentLevel++;
  122. EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume",
  123. EditorGUIUtility.IconContent("EditCollider"), GetBounds, this);
  124. EditorGUILayout.PropertyField(m_Size);
  125. EditorGUILayout.PropertyField(m_Center);
  126. EditorGUI.indentLevel--;
  127. }
  128. else
  129. {
  130. if (editingCollider)
  131. EditMode.QuitEditMode();
  132. }
  133. EditorGUILayout.PropertyField(m_LayerMask, s_Styles.m_LayerMask);
  134. EditorGUI.indentLevel--;
  135. }
  136. EditorGUILayout.Space();
  137. m_OverrideVoxelSize.isExpanded = EditorGUILayout.Foldout(m_OverrideVoxelSize.isExpanded, "Advanced");
  138. if (m_OverrideVoxelSize.isExpanded)
  139. {
  140. EditorGUI.indentLevel++;
  141. // Override voxel size.
  142. EditorGUILayout.PropertyField(m_OverrideVoxelSize);
  143. using (new EditorGUI.DisabledScope(!m_OverrideVoxelSize.boolValue || m_OverrideVoxelSize.hasMultipleDifferentValues))
  144. {
  145. EditorGUI.indentLevel++;
  146. EditorGUILayout.PropertyField(m_VoxelSize);
  147. if (!m_OverrideVoxelSize.hasMultipleDifferentValues)
  148. {
  149. if (!m_AgentTypeID.hasMultipleDifferentValues)
  150. {
  151. float voxelsPerRadius = m_VoxelSize.floatValue > 0.0f ? (bs.agentRadius / m_VoxelSize.floatValue) : 0.0f;
  152. EditorGUILayout.LabelField(" ", voxelsPerRadius.ToString("0.00") + " voxels per agent radius", EditorStyles.miniLabel);
  153. }
  154. if (m_OverrideVoxelSize.boolValue)
  155. 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);
  156. }
  157. EditorGUI.indentLevel--;
  158. }
  159. // Override tile size
  160. EditorGUILayout.PropertyField(m_OverrideTileSize);
  161. using (new EditorGUI.DisabledScope(!m_OverrideTileSize.boolValue || m_OverrideTileSize.hasMultipleDifferentValues))
  162. {
  163. EditorGUI.indentLevel++;
  164. EditorGUILayout.PropertyField(m_TileSize);
  165. if (!m_TileSize.hasMultipleDifferentValues && !m_VoxelSize.hasMultipleDifferentValues)
  166. {
  167. float tileWorldSize = m_TileSize.intValue * m_VoxelSize.floatValue;
  168. EditorGUILayout.LabelField(" ", tileWorldSize.ToString("0.00") + " world units", EditorStyles.miniLabel);
  169. }
  170. if (!m_OverrideTileSize.hasMultipleDifferentValues)
  171. {
  172. if (m_OverrideTileSize.boolValue)
  173. 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);
  174. }
  175. EditorGUI.indentLevel--;
  176. }
  177. EditorGUILayout.PropertyField(m_MinRegionArea, s_Styles.m_MinRegionArea);
  178. // Height mesh
  179. #if UNITY_2022_2_OR_NEWER
  180. var heightMeshGUIContent = new GUIContent(m_BuildHeightMesh.displayName, "Generate an accurate height mesh for precise agent placement (slower).");
  181. EditorGUILayout.PropertyField(m_BuildHeightMesh, heightMeshGUIContent);
  182. #else
  183. using (new EditorGUI.DisabledScope(true))
  184. {
  185. var heightMeshGUIContent = new GUIContent(m_BuildHeightMesh.displayName, "HeightMesh functionality is only available with Unity 2022.2 or newer.");
  186. EditorGUILayout.PropertyField(m_BuildHeightMesh, heightMeshGUIContent);
  187. }
  188. #endif
  189. EditorGUILayout.Space();
  190. EditorGUI.indentLevel--;
  191. }
  192. EditorGUILayout.Space();
  193. serializedObject.ApplyModifiedProperties();
  194. var hadError = false;
  195. var multipleTargets = targets.Length > 1;
  196. foreach (NavMeshSurface navSurface in targets)
  197. {
  198. var settings = navSurface.GetBuildSettings();
  199. // Calculating bounds is potentially expensive when unbounded - so here we just use the center/size.
  200. // It means the validation is not checking vertical voxel limit correctly when the surface is set to something else than "in volume".
  201. var bounds = new Bounds(Vector3.zero, Vector3.zero);
  202. if (navSurface.collectObjects == CollectObjects.Volume)
  203. {
  204. bounds = new Bounds(navSurface.center, navSurface.size);
  205. }
  206. var errors = settings.ValidationReport(bounds);
  207. if (errors.Length > 0)
  208. {
  209. if (multipleTargets)
  210. EditorGUILayout.LabelField(navSurface.name);
  211. foreach (var err in errors)
  212. {
  213. EditorGUILayout.HelpBox(err, MessageType.Warning);
  214. }
  215. GUILayout.BeginHorizontal();
  216. GUILayout.Space(EditorGUIUtility.labelWidth);
  217. if (GUILayout.Button("Open Agent Settings...", EditorStyles.miniButton))
  218. NavMeshEditorHelpers.OpenAgentSettings(navSurface.agentTypeID);
  219. GUILayout.EndHorizontal();
  220. hadError = true;
  221. }
  222. }
  223. if (hadError)
  224. EditorGUILayout.Space();
  225. #if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF
  226. var nmdRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
  227. EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData);
  228. var rectLabel = EditorGUI.PrefixLabel(nmdRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(m_NavMeshData.displayName));
  229. EditorGUI.EndProperty();
  230. using (new EditorGUI.DisabledScope(true))
  231. {
  232. EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData);
  233. EditorGUI.ObjectField(rectLabel, m_NavMeshData, GUIContent.none);
  234. EditorGUI.EndProperty();
  235. }
  236. #endif
  237. using (new EditorGUI.DisabledScope(Application.isPlaying || m_AgentTypeID.intValue == -1))
  238. {
  239. GUILayout.BeginHorizontal();
  240. GUILayout.Space(EditorGUIUtility.labelWidth);
  241. using (new EditorGUI.DisabledScope(targets.All(s => (s as NavMeshSurface)?.navMeshData == null)))
  242. {
  243. if (GUILayout.Button("Clear"))
  244. {
  245. NavMeshAssetManager.instance.ClearSurfaces(targets);
  246. SceneView.RepaintAll();
  247. }
  248. }
  249. if (GUILayout.Button("Bake"))
  250. {
  251. NavMeshAssetManager.instance.StartBakingSurfaces(targets);
  252. }
  253. GUILayout.EndHorizontal();
  254. // Inform when selected target is being baked
  255. var bakeOperations = NavMeshAssetManager.instance.GetBakeOperations();
  256. bool bakeInProgress = bakeOperations.Any(b =>
  257. {
  258. if (!targets.Contains(b.surface))
  259. return false;
  260. return b.bakeOperation != null;
  261. });
  262. if (bakeInProgress)
  263. {
  264. GUILayout.BeginVertical(EditorStyles.helpBox);
  265. if (GUILayout.Button("NavMesh baking is in progress.", EditorStyles.linkLabel))
  266. Progress.ShowDetails(false);
  267. GUILayout.EndHorizontal();
  268. }
  269. }
  270. }
  271. #if UNITY_2022_2_OR_NEWER
  272. [DrawGizmo(GizmoType.InSelectionHierarchy | GizmoType.Active | GizmoType.Pickable)]
  273. static void RenderGizmoSelected(NavMeshSurface navSurface, GizmoType gizmoType)
  274. {
  275. navSurface.navMeshDataInstance.FlagAsInSelectionHierarchy();
  276. DrawBoundingBoxGizmoAndIcon(navSurface, true);
  277. }
  278. [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)]
  279. static void RenderGizmoNotSelected(NavMeshSurface navSurface, GizmoType gizmoType)
  280. {
  281. DrawBoundingBoxGizmoAndIcon(navSurface, false);
  282. }
  283. #else
  284. [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)]
  285. static void RenderBoxGizmoSelected(NavMeshSurface navSurface, GizmoType gizmoType)
  286. {
  287. DrawBoundingBoxGizmoAndIcon(navSurface, true);
  288. }
  289. [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)]
  290. static void RenderBoxGizmoNotSelected(NavMeshSurface navSurface, GizmoType gizmoType)
  291. {
  292. if (NavMeshVisualizationSettings.showNavigation > 0)
  293. DrawBoundingBoxGizmoAndIcon(navSurface, false);
  294. else
  295. Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true);
  296. }
  297. #endif
  298. static void DrawBoundingBoxGizmoAndIcon(NavMeshSurface navSurface, bool selected)
  299. {
  300. var color = selected ? s_HandleColorSelected : s_HandleColor;
  301. if (!navSurface.enabled)
  302. color = s_HandleColorDisabled;
  303. var oldColor = Gizmos.color;
  304. var oldMatrix = Gizmos.matrix;
  305. // Use the unscaled matrix for the NavMeshSurface
  306. var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one);
  307. Gizmos.matrix = localToWorld;
  308. if (navSurface.collectObjects == CollectObjects.Volume)
  309. {
  310. Gizmos.color = color;
  311. Gizmos.DrawWireCube(navSurface.center, navSurface.size);
  312. if (selected && navSurface.enabled)
  313. {
  314. var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f);
  315. Gizmos.color = colorTrans;
  316. Gizmos.DrawCube(navSurface.center, navSurface.size);
  317. }
  318. }
  319. else
  320. {
  321. if (navSurface.navMeshData != null)
  322. {
  323. var bounds = navSurface.navMeshData.sourceBounds;
  324. Gizmos.color = Color.grey;
  325. Gizmos.DrawWireCube(bounds.center, bounds.size);
  326. }
  327. }
  328. Gizmos.matrix = oldMatrix;
  329. Gizmos.color = oldColor;
  330. Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true);
  331. }
  332. void OnSceneGUI()
  333. {
  334. if (!editingCollider)
  335. return;
  336. var navSurface = (NavMeshSurface)target;
  337. var color = navSurface.enabled ? s_HandleColor : s_HandleColorDisabled;
  338. var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one);
  339. using (new Handles.DrawingScope(color, localToWorld))
  340. {
  341. m_BoundsHandle.center = navSurface.center;
  342. m_BoundsHandle.size = navSurface.size;
  343. EditorGUI.BeginChangeCheck();
  344. m_BoundsHandle.DrawHandle();
  345. if (EditorGUI.EndChangeCheck())
  346. {
  347. Undo.RecordObject(navSurface, "Modified NavMesh Surface");
  348. Vector3 center = m_BoundsHandle.center;
  349. Vector3 size = m_BoundsHandle.size;
  350. navSurface.center = center;
  351. navSurface.size = size;
  352. EditorUtility.SetDirty(target);
  353. }
  354. }
  355. }
  356. [MenuItem("GameObject/AI/NavMesh Surface", false, 2000)]
  357. public static void CreateNavMeshSurface(MenuCommand menuCommand)
  358. {
  359. var parent = menuCommand.context as GameObject;
  360. var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Surface", parent);
  361. go.AddComponent<NavMeshSurface>();
  362. var view = SceneView.lastActiveSceneView;
  363. if (view != null)
  364. view.MoveToView(go.transform);
  365. }
  366. }
  367. }