No Description
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.

NavigationWindow.cs 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #if UNITY_2022_2_OR_NEWER
  2. using UnityEngine;
  3. using UnityEditor.AI;
  4. using UnityEditorInternal;
  5. using EditorNavMeshBuilder = UnityEditor.AI.NavMeshBuilder;
  6. using Object = UnityEngine.Object;
  7. using UnityEditor;
  8. namespace Unity.AI.Navigation.Editor
  9. {
  10. [InitializeOnLoad]
  11. [EditorWindowTitle(title = "Navigation")]
  12. internal class NavigationWindow : EditorWindow
  13. {
  14. static NavigationWindow s_NavigationWindow;
  15. // Scene based bake configuration
  16. SerializedObject m_SettingsObject;
  17. // Project based configuration
  18. SerializedObject m_NavMeshProjectSettingsObject;
  19. SerializedProperty m_Areas;
  20. SerializedProperty m_AgentTypes;
  21. SerializedProperty m_SettingNames;
  22. Vector2 m_ScrollPos = Vector2.zero;
  23. bool m_Advanced;
  24. ReorderableList m_AreasList;
  25. ReorderableList m_AgentTypeList;
  26. enum Mode
  27. {
  28. AgentTypeSettings = 0,
  29. AreaSettings = 1,
  30. }
  31. Mode m_Mode = Mode.AgentTypeSettings;
  32. static class Styles
  33. {
  34. internal static readonly GUIContent k_AgentTypesHeader = EditorGUIUtility.TrTextContent("Agent Types");
  35. internal static readonly GUIContent k_NameLabel = EditorGUIUtility.TrTextContent("Name");
  36. internal static readonly GUIContent k_CostLabel = EditorGUIUtility.TrTextContent("Cost");
  37. internal static readonly GUIContent[] k_ModeToggles =
  38. {
  39. EditorGUIUtility.TrTextContent("Agents", "Navmesh agent settings."),
  40. EditorGUIUtility.TrTextContent("Areas", "Navmesh area settings."),
  41. };
  42. internal static readonly GUIStyle k_ContentMargins = new GUIStyle
  43. {
  44. padding = new RectOffset(4, 4, 4, 4)
  45. };
  46. };
  47. static NavigationWindow()
  48. {
  49. NavMeshEditorHelpers.areaSettingsClicked += OpenAreaSettings;
  50. NavMeshEditorHelpers.agentTypeSettingsClicked += OpenAgentSettings;
  51. }
  52. [MenuItem("Window/AI/Navigation", false, 1)]
  53. public static void SetupWindow()
  54. {
  55. var window = GetWindow<NavigationWindow>();
  56. window.minSize = new Vector2(300, 360);
  57. }
  58. static void OpenAreaSettings()
  59. {
  60. SetupWindow();
  61. if (s_NavigationWindow == null)
  62. return;
  63. s_NavigationWindow.m_Mode = Mode.AreaSettings;
  64. s_NavigationWindow.InitProjectSettings();
  65. s_NavigationWindow.InitAgentTypes();
  66. }
  67. static void OpenAgentSettings(int agentTypeID)
  68. {
  69. SetupWindow();
  70. if (s_NavigationWindow == null)
  71. return;
  72. s_NavigationWindow.m_Mode = Mode.AgentTypeSettings;
  73. s_NavigationWindow.InitProjectSettings();
  74. s_NavigationWindow.InitAgentTypes();
  75. s_NavigationWindow.m_AgentTypeList.index = -1;
  76. for (int i = 0; i < s_NavigationWindow.m_AgentTypes.arraySize; i++)
  77. {
  78. SerializedProperty agentType = s_NavigationWindow.m_AgentTypes.GetArrayElementAtIndex(i);
  79. SerializedProperty idProp = agentType.FindPropertyRelative("agentTypeID");
  80. if (idProp.intValue == agentTypeID)
  81. {
  82. s_NavigationWindow.m_AgentTypeList.index = i;
  83. break;
  84. }
  85. }
  86. }
  87. public void OnEnable()
  88. {
  89. var iconPath = $"{NavMeshComponentsGUIUtility.k_PackageEditorResourcesFolder}NavigationWindowIcon.png";
  90. titleContent = EditorGUIUtility.TrTextContentWithIcon("Navigation", iconPath);
  91. s_NavigationWindow = this;
  92. EditorApplication.searchChanged += Repaint;
  93. Repaint();
  94. }
  95. void InitProjectSettings()
  96. {
  97. if (m_NavMeshProjectSettingsObject == null)
  98. {
  99. Object obj = Unsupported.GetSerializedAssetInterfaceSingleton("NavMeshProjectSettings");
  100. m_NavMeshProjectSettingsObject = new SerializedObject(obj);
  101. }
  102. }
  103. void InitAreas()
  104. {
  105. if (m_Areas == null)
  106. {
  107. m_Areas = m_NavMeshProjectSettingsObject.FindProperty("areas");
  108. }
  109. if (m_AreasList == null)
  110. {
  111. m_AreasList = new ReorderableList(m_NavMeshProjectSettingsObject, m_Areas, false, true, false, false);
  112. m_AreasList.drawElementCallback = DrawAreaListElement;
  113. m_AreasList.drawHeaderCallback = DrawAreaListHeader;
  114. m_AreasList.elementHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
  115. }
  116. }
  117. void InitAgentTypes()
  118. {
  119. if (m_AgentTypes == null)
  120. {
  121. m_AgentTypes = m_NavMeshProjectSettingsObject.FindProperty("m_Settings");
  122. m_SettingNames = m_NavMeshProjectSettingsObject.FindProperty("m_SettingNames");
  123. }
  124. if (m_AgentTypeList == null)
  125. {
  126. m_AgentTypeList = new ReorderableList(m_NavMeshProjectSettingsObject, m_AgentTypes, false, true, true, true);
  127. m_AgentTypeList.drawElementCallback = DrawAgentTypeListElement;
  128. m_AgentTypeList.drawHeaderCallback = DrawAgentTypeListHeader;
  129. m_AgentTypeList.onAddCallback = AddAgentType;
  130. m_AgentTypeList.onRemoveCallback = RemoveAgentType;
  131. m_AgentTypeList.elementHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
  132. }
  133. }
  134. // This code is replicated from the navmesh debug draw code.
  135. int Bit(int a, int b)
  136. {
  137. return (a & (1 << b)) >> b;
  138. }
  139. Color GetAreaColor(int i)
  140. {
  141. if (i == 0)
  142. return new Color(0, 0.75f, 1.0f, 0.5f);
  143. int r = (Bit(i, 4) + Bit(i, 1) * 2 + 1) * 63;
  144. int g = (Bit(i, 3) + Bit(i, 2) * 2 + 1) * 63;
  145. int b = (Bit(i, 5) + Bit(i, 0) * 2 + 1) * 63;
  146. return new Color(r / 255.0f, g / 255.0f, b / 255.0f, 0.5f);
  147. }
  148. public void OnDisable()
  149. {
  150. s_NavigationWindow = null;
  151. EditorApplication.searchChanged -= Repaint;
  152. }
  153. void OnSelectionChange()
  154. {
  155. m_ScrollPos = Vector2.zero;
  156. }
  157. void ModeToggle()
  158. {
  159. EditorGUILayout.BeginHorizontal();
  160. GUILayout.FlexibleSpace();
  161. m_Mode = (Mode)GUILayout.Toolbar((int)m_Mode, Styles.k_ModeToggles, "LargeButton", GUI.ToolbarButtonSize.FitToContents);
  162. GUILayout.FlexibleSpace();
  163. EditorGUILayout.EndHorizontal();
  164. }
  165. static void GetAreaListRects(Rect rect, out Rect stripeRect, out Rect labelRect, out Rect nameRect, out Rect costRect)
  166. {
  167. float stripeWidth = EditorGUIUtility.singleLineHeight * 0.8f;
  168. float labelWidth = EditorGUIUtility.singleLineHeight * 5;
  169. float costWidth = EditorGUIUtility.singleLineHeight * 4;
  170. float nameWidth = rect.width - stripeWidth - labelWidth - costWidth;
  171. float x = rect.x;
  172. stripeRect = new Rect(x, rect.y, stripeWidth - 4, rect.height);
  173. x += stripeWidth;
  174. labelRect = new Rect(x, rect.y, labelWidth - 4, rect.height);
  175. x += labelWidth;
  176. nameRect = new Rect(x, rect.y, nameWidth - 4, rect.height);
  177. x += nameWidth;
  178. costRect = new Rect(x, rect.y, costWidth, rect.height);
  179. }
  180. void DrawAreaListHeader(Rect rect)
  181. {
  182. GetAreaListRects(rect, out _, out _, out Rect nameRect, out Rect costRect);
  183. GUI.Label(nameRect, Styles.k_NameLabel);
  184. GUI.Label(costRect, Styles.k_CostLabel);
  185. }
  186. void DrawAreaListElement(Rect rect, int index, bool selected, bool focused)
  187. {
  188. SerializedProperty areaProp = m_Areas.GetArrayElementAtIndex(index);
  189. if (areaProp == null)
  190. return;
  191. SerializedProperty nameProp = areaProp.FindPropertyRelative("name");
  192. SerializedProperty costProp = areaProp.FindPropertyRelative("cost");
  193. if (nameProp == null || costProp == null)
  194. return;
  195. rect.height -= 2; // nicer looking with selected list row and a text field in it
  196. bool builtInLayer;
  197. bool allowChangeName;
  198. bool allowChangeCost;
  199. switch (index)
  200. {
  201. case 0: // Default
  202. builtInLayer = true;
  203. allowChangeName = false;
  204. allowChangeCost = true;
  205. break;
  206. case 1: // NonWalkable
  207. builtInLayer = true;
  208. allowChangeName = false;
  209. allowChangeCost = false;
  210. break;
  211. case 2: // Jump
  212. builtInLayer = true;
  213. allowChangeName = false;
  214. allowChangeCost = true;
  215. break;
  216. default:
  217. builtInLayer = false;
  218. allowChangeName = true;
  219. allowChangeCost = true;
  220. break;
  221. }
  222. GetAreaListRects(rect, out Rect stripeRect, out Rect labelRect, out Rect nameRect, out Rect costRect);
  223. bool oldEnabled = GUI.enabled;
  224. Color color = GetAreaColor(index);
  225. Color dimmed = new Color(color.r * 0.1f, color.g * 0.1f, color.b * 0.1f, 0.6f);
  226. EditorGUI.DrawRect(stripeRect, color);
  227. EditorGUI.DrawRect(new Rect(stripeRect.x, stripeRect.y, 1, stripeRect.height), dimmed);
  228. EditorGUI.DrawRect(new Rect(stripeRect.x + stripeRect.width - 1, stripeRect.y, 1, stripeRect.height), dimmed);
  229. EditorGUI.DrawRect(new Rect(stripeRect.x + 1, stripeRect.y, stripeRect.width - 2, 1), dimmed);
  230. EditorGUI.DrawRect(new Rect(stripeRect.x + 1, stripeRect.y + stripeRect.height - 1, stripeRect.width - 2, 1), dimmed);
  231. if (builtInLayer)
  232. GUI.Label(labelRect, EditorGUIUtility.TrTempContent("Built-in " + index));
  233. else
  234. GUI.Label(labelRect, EditorGUIUtility.TrTempContent("User " + index));
  235. int oldIndent = EditorGUI.indentLevel;
  236. EditorGUI.indentLevel = 0;
  237. GUI.enabled = oldEnabled && allowChangeName;
  238. EditorGUI.PropertyField(nameRect, nameProp, GUIContent.none);
  239. GUI.enabled = oldEnabled && allowChangeCost;
  240. EditorGUI.PropertyField(costRect, costProp, GUIContent.none);
  241. GUI.enabled = oldEnabled;
  242. EditorGUI.indentLevel = oldIndent;
  243. }
  244. static void AddAgentType(ReorderableList list)
  245. {
  246. UnityEngine.AI.NavMesh.CreateSettings();
  247. list.index = UnityEngine.AI.NavMesh.GetSettingsCount() - 1;
  248. }
  249. void RemoveAgentType(ReorderableList list)
  250. {
  251. SerializedProperty agentTypeProp = m_AgentTypes.GetArrayElementAtIndex(list.index);
  252. if (agentTypeProp == null)
  253. return;
  254. SerializedProperty idProp = agentTypeProp.FindPropertyRelative("agentTypeID");
  255. if (idProp == null)
  256. return;
  257. // Cannot delete default.
  258. if (idProp.intValue == 0)
  259. return;
  260. m_SettingNames.DeleteArrayElementAtIndex(list.index);
  261. ReorderableList.defaultBehaviours.DoRemoveButton(list);
  262. }
  263. static void DrawAgentTypeListHeader(Rect rect)
  264. {
  265. GUI.Label(rect, Styles.k_AgentTypesHeader);
  266. }
  267. void DrawAgentTypeListElement(Rect rect, int index, bool selected, bool focused)
  268. {
  269. SerializedProperty agentProp = m_AgentTypes.GetArrayElementAtIndex(index);
  270. if (agentProp == null)
  271. return;
  272. SerializedProperty idProp = agentProp.FindPropertyRelative("agentTypeID");
  273. if (idProp == null)
  274. return;
  275. rect.height -= 2; // nicer looking with selected list row and a text field in it
  276. bool isDefault = idProp.intValue == 0;
  277. using (new EditorGUI.DisabledScope(isDefault))
  278. {
  279. var settingsName = UnityEngine.AI.NavMesh.GetSettingsNameFromID(idProp.intValue);
  280. GUI.Label(rect, EditorGUIUtility.TrTempContent(settingsName));
  281. }
  282. }
  283. public void OnGUI()
  284. {
  285. EditorGUILayout.Space();
  286. ModeToggle();
  287. EditorGUILayout.Space();
  288. InitProjectSettings();
  289. m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos);
  290. switch (m_Mode)
  291. {
  292. case Mode.AreaSettings:
  293. AreaSettings();
  294. break;
  295. case Mode.AgentTypeSettings:
  296. AgentTypeSettings();
  297. break;
  298. }
  299. EditorGUILayout.EndScrollView();
  300. }
  301. void AreaSettings()
  302. {
  303. if (m_Areas == null)
  304. InitAreas();
  305. m_NavMeshProjectSettingsObject.Update();
  306. using (new GUILayout.VerticalScope(Styles.k_ContentMargins))
  307. {
  308. m_AreasList.DoLayoutList();
  309. }
  310. m_NavMeshProjectSettingsObject.ApplyModifiedProperties();
  311. }
  312. void AgentTypeSettings()
  313. {
  314. if (m_AgentTypes == null)
  315. InitAgentTypes();
  316. m_NavMeshProjectSettingsObject.Update();
  317. if (m_AgentTypeList.index < 0)
  318. m_AgentTypeList.index = 0;
  319. using (new GUILayout.VerticalScope(Styles.k_ContentMargins))
  320. {
  321. m_AgentTypeList.DoLayoutList();
  322. }
  323. if (m_AgentTypeList.index >= 0 && m_AgentTypeList.index < m_AgentTypes.arraySize)
  324. {
  325. SerializedProperty nameProp = m_SettingNames.GetArrayElementAtIndex(m_AgentTypeList.index);
  326. SerializedProperty selectedAgentType = m_AgentTypes.GetArrayElementAtIndex(m_AgentTypeList.index);
  327. SerializedProperty radiusProp = selectedAgentType.FindPropertyRelative("agentRadius");
  328. SerializedProperty heightProp = selectedAgentType.FindPropertyRelative("agentHeight");
  329. SerializedProperty stepHeightProp = selectedAgentType.FindPropertyRelative("agentClimb");
  330. SerializedProperty maxSlopeProp = selectedAgentType.FindPropertyRelative("agentSlope");
  331. SerializedProperty ledgeDropHeightProp = selectedAgentType.FindPropertyRelative("ledgeDropHeight");
  332. SerializedProperty jumpDistanceProp = selectedAgentType.FindPropertyRelative("maxJumpAcrossDistance");
  333. const float kDiagramHeight = 120.0f;
  334. Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, kDiagramHeight);
  335. NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, radiusProp.floatValue, heightProp.floatValue, stepHeightProp.floatValue, maxSlopeProp.floatValue);
  336. EditorGUILayout.PropertyField(nameProp, EditorGUIUtility.TrTempContent("Name"));
  337. EditorGUILayout.PropertyField(radiusProp, EditorGUIUtility.TrTempContent("Radius"));
  338. EditorGUILayout.PropertyField(heightProp, EditorGUIUtility.TrTempContent("Height"));
  339. EditorGUILayout.PropertyField(stepHeightProp, EditorGUIUtility.TrTempContent("Step Height"));
  340. const float kMaxSlopeAngle = 60.0f;
  341. EditorGUILayout.Slider(maxSlopeProp, 0.0f, kMaxSlopeAngle, EditorGUIUtility.TrTextContent("Max Slope"));
  342. EditorGUILayout.Space();
  343. EditorGUILayout.LabelField(EditorGUIUtility.TrTempContent("Generated Links"), EditorStyles.boldLabel);
  344. EditorGUILayout.PropertyField(ledgeDropHeightProp, EditorGUIUtility.TrTempContent("Drop Height"));
  345. EditorGUILayout.PropertyField(jumpDistanceProp, EditorGUIUtility.TrTempContent("Jump Distance"));
  346. }
  347. EditorGUILayout.Space();
  348. m_NavMeshProjectSettingsObject.ApplyModifiedProperties();
  349. }
  350. }
  351. }
  352. #endif