UnityGame/Library/PackageCache/com.unity.ai.navigation/Editor/NavigationWindow.cs

415 lines
16 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
using UnityEngine;
using UnityEditor.AI;
using UnityEditorInternal;
using Object = UnityEngine.Object;
using UnityEditor;
namespace Unity.AI.Navigation.Editor
{
[InitializeOnLoad]
[EditorWindowTitle(title = "Navigation")]
internal class NavigationWindow : EditorWindow
{
static NavigationWindow s_NavigationWindow;
// Scene based bake configuration
SerializedObject m_SettingsObject;
// Project based configuration
SerializedObject m_NavMeshProjectSettingsObject;
SerializedProperty m_Areas;
SerializedProperty m_AgentTypes;
SerializedProperty m_SettingNames;
Vector2 m_ScrollPos = Vector2.zero;
bool m_Advanced;
ReorderableList m_AreasList;
ReorderableList m_AgentTypeList;
enum Mode
{
AgentTypeSettings = 0,
AreaSettings = 1,
}
Mode m_Mode = Mode.AgentTypeSettings;
static class Styles
{
internal static readonly GUIContent k_AgentTypesHeader = EditorGUIUtility.TrTextContent("Agent Types");
internal static readonly GUIContent k_NameLabel = EditorGUIUtility.TrTextContent("Name");
internal static readonly GUIContent k_CostLabel = EditorGUIUtility.TrTextContent("Cost");
internal static readonly GUIContent[] k_ModeToggles =
{
EditorGUIUtility.TrTextContent("Agents", "Navmesh agent settings."),
EditorGUIUtility.TrTextContent("Areas", "Navmesh area settings."),
};
internal static readonly GUIStyle k_ContentMargins = new GUIStyle
{
padding = new RectOffset(4, 4, 4, 4)
};
};
static NavigationWindow()
{
NavMeshEditorHelpers.areaSettingsClicked += OpenAreaSettings;
NavMeshEditorHelpers.agentTypeSettingsClicked += OpenAgentSettings;
}
[MenuItem("Window/AI/Navigation", false, 1)]
public static void SetupWindow()
{
var window = GetWindow<NavigationWindow>();
window.minSize = new Vector2(300, 360);
}
static void OpenAreaSettings()
{
SetupWindow();
if (s_NavigationWindow == null)
return;
s_NavigationWindow.m_Mode = Mode.AreaSettings;
s_NavigationWindow.InitProjectSettings();
s_NavigationWindow.InitAgentTypes();
}
static void OpenAgentSettings(int agentTypeID)
{
SetupWindow();
if (s_NavigationWindow == null)
return;
s_NavigationWindow.m_Mode = Mode.AgentTypeSettings;
s_NavigationWindow.InitProjectSettings();
s_NavigationWindow.InitAgentTypes();
s_NavigationWindow.m_AgentTypeList.index = -1;
for (int i = 0; i < s_NavigationWindow.m_AgentTypes.arraySize; i++)
{
SerializedProperty agentType = s_NavigationWindow.m_AgentTypes.GetArrayElementAtIndex(i);
SerializedProperty idProp = agentType.FindPropertyRelative("agentTypeID");
if (idProp.intValue == agentTypeID)
{
s_NavigationWindow.m_AgentTypeList.index = i;
break;
}
}
}
public void OnEnable()
{
var iconPath = $"{NavMeshComponentsGUIUtility.k_PackageEditorResourcesFolder}NavigationWindowIcon.asset";
titleContent = EditorGUIUtility.TrTextContentWithIcon("Navigation", iconPath);
s_NavigationWindow = this;
EditorApplication.searchChanged += Repaint;
Repaint();
}
void InitProjectSettings()
{
if (m_NavMeshProjectSettingsObject == null)
{
Object obj = Unsupported.GetSerializedAssetInterfaceSingleton("NavMeshProjectSettings");
m_NavMeshProjectSettingsObject = new SerializedObject(obj);
}
}
void InitAreas()
{
if (m_Areas == null)
{
m_Areas = m_NavMeshProjectSettingsObject.FindProperty("areas");
}
if (m_AreasList == null)
{
m_AreasList = new ReorderableList(m_NavMeshProjectSettingsObject, m_Areas, false, true, false, false);
m_AreasList.drawElementCallback = DrawAreaListElement;
m_AreasList.drawHeaderCallback = DrawAreaListHeader;
m_AreasList.elementHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
}
void InitAgentTypes()
{
if (m_AgentTypes == null)
{
m_AgentTypes = m_NavMeshProjectSettingsObject.FindProperty("m_Settings");
m_SettingNames = m_NavMeshProjectSettingsObject.FindProperty("m_SettingNames");
}
if (m_AgentTypeList == null)
{
m_AgentTypeList = new ReorderableList(m_NavMeshProjectSettingsObject, m_AgentTypes, false, true, true, true);
m_AgentTypeList.drawElementCallback = DrawAgentTypeListElement;
m_AgentTypeList.drawHeaderCallback = DrawAgentTypeListHeader;
m_AgentTypeList.onAddCallback = AddAgentType;
m_AgentTypeList.onRemoveCallback = RemoveAgentType;
m_AgentTypeList.elementHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
}
// This code is replicated from the NavMesh debug draw code.
int Bit(int a, int b)
{
return (a & (1 << b)) >> b;
}
Color GetAreaColor(int i)
{
if (i == 0)
return new Color(0, 0.75f, 1.0f, 0.5f);
int r = (Bit(i, 4) + Bit(i, 1) * 2 + 1) * 63;
int g = (Bit(i, 3) + Bit(i, 2) * 2 + 1) * 63;
int b = (Bit(i, 5) + Bit(i, 0) * 2 + 1) * 63;
return new Color(r / 255.0f, g / 255.0f, b / 255.0f, 0.5f);
}
public void OnDisable()
{
s_NavigationWindow = null;
EditorApplication.searchChanged -= Repaint;
}
void OnSelectionChange()
{
m_ScrollPos = Vector2.zero;
}
void ModeToggle()
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
m_Mode = (Mode)GUILayout.Toolbar((int)m_Mode, Styles.k_ModeToggles, "LargeButton", GUI.ToolbarButtonSize.FitToContents);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
static void GetAreaListRects(Rect rect, out Rect stripeRect, out Rect labelRect, out Rect nameRect, out Rect costRect)
{
float stripeWidth = EditorGUIUtility.singleLineHeight * 0.8f;
float labelWidth = EditorGUIUtility.singleLineHeight * 5;
float costWidth = EditorGUIUtility.singleLineHeight * 4;
float nameWidth = rect.width - stripeWidth - labelWidth - costWidth;
float x = rect.x;
stripeRect = new Rect(x, rect.y, stripeWidth - 4, rect.height);
x += stripeWidth;
labelRect = new Rect(x, rect.y, labelWidth - 4, rect.height);
x += labelWidth;
nameRect = new Rect(x, rect.y, nameWidth - 4, rect.height);
x += nameWidth;
costRect = new Rect(x, rect.y, costWidth, rect.height);
}
void DrawAreaListHeader(Rect rect)
{
GetAreaListRects(rect, out _, out _, out Rect nameRect, out Rect costRect);
GUI.Label(nameRect, Styles.k_NameLabel);
GUI.Label(costRect, Styles.k_CostLabel);
}
void DrawAreaListElement(Rect rect, int index, bool selected, bool focused)
{
SerializedProperty areaProp = m_Areas.GetArrayElementAtIndex(index);
if (areaProp == null)
return;
SerializedProperty nameProp = areaProp.FindPropertyRelative("name");
SerializedProperty costProp = areaProp.FindPropertyRelative("cost");
if (nameProp == null || costProp == null)
return;
rect.height -= 2; // nicer looking with selected list row and a text field in it
bool builtInLayer;
bool allowChangeName;
bool allowChangeCost;
switch (index)
{
case 0: // Default
builtInLayer = true;
allowChangeName = false;
allowChangeCost = true;
break;
case 1: // NonWalkable
builtInLayer = true;
allowChangeName = false;
allowChangeCost = false;
break;
case 2: // Jump
builtInLayer = true;
allowChangeName = false;
allowChangeCost = true;
break;
default:
builtInLayer = false;
allowChangeName = true;
allowChangeCost = true;
break;
}
GetAreaListRects(rect, out Rect stripeRect, out Rect labelRect, out Rect nameRect, out Rect costRect);
bool oldEnabled = GUI.enabled;
Color color = GetAreaColor(index);
Color dimmed = new Color(color.r * 0.1f, color.g * 0.1f, color.b * 0.1f, 0.6f);
EditorGUI.DrawRect(stripeRect, color);
EditorGUI.DrawRect(new Rect(stripeRect.x, stripeRect.y, 1, stripeRect.height), dimmed);
EditorGUI.DrawRect(new Rect(stripeRect.x + stripeRect.width - 1, stripeRect.y, 1, stripeRect.height), dimmed);
EditorGUI.DrawRect(new Rect(stripeRect.x + 1, stripeRect.y, stripeRect.width - 2, 1), dimmed);
EditorGUI.DrawRect(new Rect(stripeRect.x + 1, stripeRect.y + stripeRect.height - 1, stripeRect.width - 2, 1), dimmed);
if (builtInLayer)
GUI.Label(labelRect, EditorGUIUtility.TrTempContent("Built-in " + index));
else
GUI.Label(labelRect, EditorGUIUtility.TrTempContent("User " + index));
int oldIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
GUI.enabled = oldEnabled && allowChangeName;
EditorGUI.PropertyField(nameRect, nameProp, GUIContent.none);
GUI.enabled = oldEnabled && allowChangeCost;
EditorGUI.PropertyField(costRect, costProp, GUIContent.none);
GUI.enabled = oldEnabled;
EditorGUI.indentLevel = oldIndent;
}
static void AddAgentType(ReorderableList list)
{
UnityEngine.AI.NavMesh.CreateSettings();
list.index = UnityEngine.AI.NavMesh.GetSettingsCount() - 1;
}
void RemoveAgentType(ReorderableList list)
{
SerializedProperty agentTypeProp = m_AgentTypes.GetArrayElementAtIndex(list.index);
if (agentTypeProp == null)
return;
SerializedProperty idProp = agentTypeProp.FindPropertyRelative("agentTypeID");
if (idProp == null)
return;
// Cannot delete default.
if (idProp.intValue == 0)
return;
m_SettingNames.DeleteArrayElementAtIndex(list.index);
ReorderableList.defaultBehaviours.DoRemoveButton(list);
}
static void DrawAgentTypeListHeader(Rect rect)
{
GUI.Label(rect, Styles.k_AgentTypesHeader);
}
void DrawAgentTypeListElement(Rect rect, int index, bool selected, bool focused)
{
SerializedProperty agentProp = m_AgentTypes.GetArrayElementAtIndex(index);
if (agentProp == null)
return;
SerializedProperty idProp = agentProp.FindPropertyRelative("agentTypeID");
if (idProp == null)
return;
rect.height -= 2; // nicer looking with selected list row and a text field in it
bool isDefault = idProp.intValue == 0;
using (new EditorGUI.DisabledScope(isDefault))
{
var settingsName = UnityEngine.AI.NavMesh.GetSettingsNameFromID(idProp.intValue);
GUI.Label(rect, EditorGUIUtility.TrTempContent(settingsName));
}
}
public void OnGUI()
{
EditorGUILayout.Space();
ModeToggle();
EditorGUILayout.Space();
InitProjectSettings();
m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos);
switch (m_Mode)
{
case Mode.AreaSettings:
AreaSettings();
break;
case Mode.AgentTypeSettings:
AgentTypeSettings();
break;
}
EditorGUILayout.EndScrollView();
}
void AreaSettings()
{
if (m_Areas == null)
InitAreas();
m_NavMeshProjectSettingsObject.Update();
using (new GUILayout.VerticalScope(Styles.k_ContentMargins))
{
m_AreasList.DoLayoutList();
}
m_NavMeshProjectSettingsObject.ApplyModifiedProperties();
}
void AgentTypeSettings()
{
if (m_AgentTypes == null)
InitAgentTypes();
m_NavMeshProjectSettingsObject.Update();
if (m_AgentTypeList.index < 0)
m_AgentTypeList.index = 0;
using (new GUILayout.VerticalScope(Styles.k_ContentMargins))
{
m_AgentTypeList.DoLayoutList();
}
if (m_AgentTypeList.index >= 0 && m_AgentTypeList.index < m_AgentTypes.arraySize)
{
SerializedProperty nameProp = m_SettingNames.GetArrayElementAtIndex(m_AgentTypeList.index);
SerializedProperty selectedAgentType = m_AgentTypes.GetArrayElementAtIndex(m_AgentTypeList.index);
SerializedProperty radiusProp = selectedAgentType.FindPropertyRelative("agentRadius");
SerializedProperty heightProp = selectedAgentType.FindPropertyRelative("agentHeight");
SerializedProperty stepHeightProp = selectedAgentType.FindPropertyRelative("agentClimb");
SerializedProperty maxSlopeProp = selectedAgentType.FindPropertyRelative("agentSlope");
SerializedProperty ledgeDropHeightProp = selectedAgentType.FindPropertyRelative("ledgeDropHeight");
SerializedProperty jumpDistanceProp = selectedAgentType.FindPropertyRelative("maxJumpAcrossDistance");
const float kDiagramHeight = 120.0f;
Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, kDiagramHeight);
NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, radiusProp.floatValue, heightProp.floatValue, stepHeightProp.floatValue, maxSlopeProp.floatValue);
EditorGUILayout.PropertyField(nameProp, EditorGUIUtility.TrTempContent("Name"));
EditorGUILayout.PropertyField(radiusProp, EditorGUIUtility.TrTempContent("Radius"));
EditorGUILayout.PropertyField(heightProp, EditorGUIUtility.TrTempContent("Height"));
EditorGUILayout.PropertyField(stepHeightProp, EditorGUIUtility.TrTempContent("Step Height"));
const float kMaxSlopeAngle = 60.0f;
EditorGUILayout.Slider(maxSlopeProp, 0.0f, kMaxSlopeAngle, EditorGUIUtility.TrTextContent("Max Slope"));
EditorGUILayout.Space();
EditorGUILayout.LabelField(EditorGUIUtility.TrTempContent("Generated Links"), EditorStyles.boldLabel);
EditorGUILayout.PropertyField(ledgeDropHeightProp, EditorGUIUtility.TrTempContent("Drop Height"));
EditorGUILayout.PropertyField(jumpDistanceProp, EditorGUIUtility.TrTempContent("Jump Distance"));
}
EditorGUILayout.Space();
m_NavMeshProjectSettingsObject.ApplyModifiedProperties();
}
}
}