341 lines
15 KiB
C#
341 lines
15 KiB
C#
using UnityEditor;
|
|
using UnityEditor.AI;
|
|
using UnityEditor.Overlays;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace Unity.AI.Navigation.Editor
|
|
{
|
|
[Overlay(typeof(SceneView), "AINavigationOverlay", "AI Navigation", defaultDisplay = true)]
|
|
[Icon(NavMeshComponentsGUIUtility.k_PackageEditorResourcesFolder + "Overlay/NavigationOverlay.png")]
|
|
internal class NavigationOverlay : Overlay
|
|
{
|
|
static class Style
|
|
{
|
|
internal static readonly GUIContent SurfacesSectionTexts =
|
|
EditorGUIUtility.TrTextContent("Surfaces");
|
|
|
|
internal static readonly GUIContent SurfacesSelectedOnlyTexts =
|
|
EditorGUIUtility.TrTextContent("Show Only Selected", "Check this to hide surfaces which are not part of the selection hierarchy");
|
|
|
|
internal static readonly GUIContent SurfacesNavMeshTexts =
|
|
EditorGUIUtility.TrTextContent("Show NavMesh", "Display navigation mesh using the associated area's color");
|
|
|
|
internal static readonly GUIContent SurfacesHeightMeshTexts =
|
|
EditorGUIUtility.TrTextContent("Show HeightMesh", "Display height mesh used for accurate vertical placement");
|
|
|
|
internal static readonly GUIContent AgentsSectionTexts =
|
|
EditorGUIUtility.TrTextContent("Agents");
|
|
|
|
internal static readonly GUIContent AgentsPathPolysTexts =
|
|
EditorGUIUtility.TrTextContent("Show Path Polygons", "Shows the polygons leading to goal.");
|
|
|
|
internal static readonly GUIContent AgentsPathNodesTexts =
|
|
EditorGUIUtility.TrTextContent("Show Path Query Nodes", "Shows the nodes expanded during last path query.");
|
|
|
|
internal static readonly GUIContent AgentsNeighboursTexts =
|
|
EditorGUIUtility.TrTextContent("Show Neighbours", "Show the agent neighbours considered during simulation.");
|
|
|
|
internal static readonly GUIContent AgentsWallsTexts =
|
|
EditorGUIUtility.TrTextContent("Show Walls", "Shows the wall segments handled during simulation.");
|
|
|
|
internal static readonly GUIContent AgentsAvoidanceTexts =
|
|
EditorGUIUtility.TrTextContent("Show Avoidance", "Shows the processed avoidance geometry from simulation.");
|
|
|
|
internal static readonly GUIContent AgentsAvoidancePendingDebugRequestTexts =
|
|
EditorGUIUtility.TrTextContent("Avoidance display is not valid until after next game update.", "Avoidance information will be computed on the next game update");
|
|
|
|
internal static readonly GUIContent AgentsAvoidanceDebugRequestsCountExceededTexts =
|
|
EditorGUIUtility.TrTextContent("", "Avoidance information display is limited to a fixed number of agents"); // This text is dynamic
|
|
|
|
internal static readonly GUIContent ObstaclesSectionTexts =
|
|
EditorGUIUtility.TrTextContent("Obstacles");
|
|
|
|
internal static readonly GUIContent ObstaclesCarveHullText =
|
|
EditorGUIUtility.TrTextContent("Show Carve Hull", "Shows the hull used to carve the obstacle from the NavMesh.");
|
|
|
|
internal static readonly GUIContent DeveloperModeSectionTexts =
|
|
EditorGUIUtility.TrTextContent("Developer Mode");
|
|
|
|
internal static readonly GUIContent SurfacesPortalsTexts =
|
|
EditorGUIUtility.TrTextContent("Show NavMesh Portals");
|
|
|
|
internal static readonly GUIContent SurfacesTileLinksTexts =
|
|
EditorGUIUtility.TrTextContent("Show NavMesh Tile Links");
|
|
|
|
internal static readonly GUIContent SurfacesHeightMeshBVTreeTexts =
|
|
EditorGUIUtility.TrTextContent("Show HeightMesh BV-Tree");
|
|
|
|
internal static readonly GUIContent SurfacesHeightMapsTexts =
|
|
EditorGUIUtility.TrTextContent("Show HeightMaps", "Display terrain height maps used for accurate vertical placement");
|
|
|
|
internal static readonly GUIContent SurfacesProximityGridTexts =
|
|
EditorGUIUtility.TrTextContent("Show Proximity Grid");
|
|
|
|
internal static readonly GUIContent NavigationVisualizationDisabledTexts =
|
|
EditorGUIUtility.TrTextContent("Navigation visualization is not available in prefab edition.", "");
|
|
}
|
|
|
|
VisualElement m_RootPanel;
|
|
VisualElement m_OptionsPanel;
|
|
VisualElement m_AgentFoldOut;
|
|
HelpBox m_VisualizationDisabledHelpBox;
|
|
HelpBox m_AgentCountWarning;
|
|
HelpBox m_AgentPendingRequestWarning;
|
|
|
|
public override void OnCreated()
|
|
{
|
|
base.OnCreated();
|
|
|
|
NavMeshEditorHelpers.agentRejectedDebugInfoRequestsCountChanged += OnAgentRejectedDebugInfoRequestsCountChanged;
|
|
NavMeshEditorHelpers.agentDebugRequestsPending += DisplayAgentPendingRequestWarningBox;
|
|
NavMeshEditorHelpers.agentDebugRequestsProcessed += HideAgentPendingRequestWarningBox;
|
|
|
|
PrefabStage.prefabStageOpened += OnPrefabStageOpened;
|
|
PrefabStage.prefabStageClosing += OnPrefabStageClosing;
|
|
}
|
|
|
|
public override void OnWillBeDestroyed()
|
|
{
|
|
NavMeshEditorHelpers.agentRejectedDebugInfoRequestsCountChanged -= OnAgentRejectedDebugInfoRequestsCountChanged;
|
|
NavMeshEditorHelpers.agentDebugRequestsPending -= DisplayAgentPendingRequestWarningBox;
|
|
NavMeshEditorHelpers.agentDebugRequestsProcessed -= HideAgentPendingRequestWarningBox;
|
|
|
|
PrefabStage.prefabStageOpened -= OnPrefabStageOpened;
|
|
PrefabStage.prefabStageClosing -= OnPrefabStageClosing;
|
|
|
|
base.OnWillBeDestroyed();
|
|
}
|
|
|
|
public override VisualElement CreatePanelContent()
|
|
{
|
|
m_RootPanel = new VisualElement();
|
|
|
|
m_OptionsPanel = new VisualElement();
|
|
m_RootPanel.Add(m_OptionsPanel);
|
|
|
|
m_VisualizationDisabledHelpBox = AddHelpBox(HelpBoxMessageType.Info, Style.NavigationVisualizationDisabledTexts, 200, 10, false);
|
|
var visualizationEnabled = PrefabStageUtility.GetCurrentPrefabStage() == null;
|
|
SetVisualizationEnabled(visualizationEnabled);
|
|
|
|
// Surfaces
|
|
var surfacesFoldout = AddFoldout(m_OptionsPanel, Style.SurfacesSectionTexts, 5);
|
|
AddToggle(surfacesFoldout, Style.SurfacesSelectedOnlyTexts, NavMeshVisualizationSettings.showOnlySelectedSurfaces,
|
|
(evt => NavMeshVisualizationSettings.showOnlySelectedSurfaces = evt.newValue));
|
|
AddToggle(surfacesFoldout, Style.SurfacesNavMeshTexts, NavMeshVisualizationSettings.showNavMesh,
|
|
evt => NavMeshVisualizationSettings.showNavMesh = evt.newValue);
|
|
AddToggle(surfacesFoldout, Style.SurfacesHeightMeshTexts, NavMeshVisualizationSettings.showHeightMesh,
|
|
evt => NavMeshVisualizationSettings.showHeightMesh = evt.newValue);
|
|
|
|
// Agents
|
|
m_AgentFoldOut = AddFoldout(m_OptionsPanel, Style.AgentsSectionTexts, 5);
|
|
AddToggle(m_AgentFoldOut, Style.AgentsPathPolysTexts, NavMeshVisualizationSettings.showAgentPath, evt => NavMeshVisualizationSettings.showAgentPath = evt.newValue);
|
|
AddToggle(m_AgentFoldOut, Style.AgentsPathNodesTexts, NavMeshVisualizationSettings.showAgentPathInfo,
|
|
evt => NavMeshVisualizationSettings.showAgentPathInfo = evt.newValue);
|
|
AddToggle(m_AgentFoldOut, Style.AgentsNeighboursTexts, NavMeshVisualizationSettings.showAgentNeighbours,
|
|
evt => NavMeshVisualizationSettings.showAgentNeighbours = evt.newValue);
|
|
AddToggle(m_AgentFoldOut, Style.AgentsWallsTexts, NavMeshVisualizationSettings.showAgentWalls, evt => NavMeshVisualizationSettings.showAgentWalls = evt.newValue);
|
|
AddToggle(m_AgentFoldOut, Style.AgentsAvoidanceTexts, NavMeshVisualizationSettings.showAgentAvoidance,
|
|
evt => NavMeshVisualizationSettings.showAgentAvoidance = evt.newValue);
|
|
|
|
// Create avoidance requests count warning box and display it if needed
|
|
m_AgentCountWarning = AddHelpBox(HelpBoxMessageType.Warning, Style.AgentsAvoidanceDebugRequestsCountExceededTexts, 180, 5, false);
|
|
NavMeshEditorHelpers.GetAgentsDebugInfoRejectedRequestsCount(out var rejected, out var allowed);
|
|
if (rejected > 0)
|
|
{
|
|
DisplayAgentCountWarningBox(rejected, allowed);
|
|
}
|
|
|
|
// Create avoidance pending requests warning box and display it if needed
|
|
m_AgentPendingRequestWarning = AddHelpBox(HelpBoxMessageType.Warning, Style.AgentsAvoidancePendingDebugRequestTexts, 180, 5, false);
|
|
if (NavMeshEditorHelpers.HasPendingAgentDebugInfoRequests())
|
|
{
|
|
DisplayAgentPendingRequestWarningBox();
|
|
}
|
|
|
|
// Obstacles
|
|
var obstaclesFoldout = AddFoldout(m_OptionsPanel, Style.ObstaclesSectionTexts, 5);
|
|
AddToggle(obstaclesFoldout, Style.ObstaclesCarveHullText, NavMeshVisualizationSettings.showObstacleCarveHull,
|
|
evt => NavMeshVisualizationSettings.showObstacleCarveHull = evt.newValue);
|
|
|
|
// Developer Mode only
|
|
if (Unsupported.IsDeveloperMode())
|
|
{
|
|
var developerModeFoldout = AddFoldout(m_OptionsPanel, Style.DeveloperModeSectionTexts, 5);
|
|
AddToggle(developerModeFoldout, Style.SurfacesPortalsTexts, NavMeshVisualizationSettings.showNavMeshPortals,
|
|
evt => NavMeshVisualizationSettings.showNavMeshPortals = evt.newValue);
|
|
AddToggle(developerModeFoldout, Style.SurfacesTileLinksTexts, NavMeshVisualizationSettings.showNavMeshLinks,
|
|
evt => NavMeshVisualizationSettings.showNavMeshLinks = evt.newValue);
|
|
AddToggle(developerModeFoldout, Style.SurfacesHeightMeshBVTreeTexts, NavMeshVisualizationSettings.showHeightMeshBVTree,
|
|
evt => NavMeshVisualizationSettings.showHeightMeshBVTree = evt.newValue);
|
|
AddToggle(developerModeFoldout, Style.SurfacesHeightMapsTexts, NavMeshVisualizationSettings.showHeightMaps,
|
|
evt => NavMeshVisualizationSettings.showHeightMaps = evt.newValue);
|
|
AddToggle(developerModeFoldout, Style.SurfacesProximityGridTexts, NavMeshVisualizationSettings.showProximityGrid,
|
|
evt => NavMeshVisualizationSettings.showProximityGrid = evt.newValue);
|
|
}
|
|
|
|
return m_RootPanel;
|
|
}
|
|
|
|
void OnAgentRejectedDebugInfoRequestsCountChanged(int rejected, int allowed)
|
|
{
|
|
if (rejected == 0)
|
|
{
|
|
HideAgentCountWarningBox();
|
|
}
|
|
else
|
|
{
|
|
DisplayAgentCountWarningBox(rejected, allowed);
|
|
}
|
|
}
|
|
|
|
void OnPrefabStageOpened(PrefabStage obj)
|
|
{
|
|
SetVisualizationEnabled(false);
|
|
}
|
|
|
|
void OnPrefabStageClosing(PrefabStage obj)
|
|
{
|
|
SetVisualizationEnabled(true);
|
|
}
|
|
|
|
static Foldout AddFoldout(VisualElement parent, GUIContent text, int padding)
|
|
{
|
|
var prefName = $"AINavigationOverlay_Foldout_{text.text}";
|
|
|
|
var foldout = new Foldout
|
|
{
|
|
text = text.text,
|
|
tooltip = text.tooltip,
|
|
style =
|
|
{
|
|
paddingBottom = padding
|
|
},
|
|
value = EditorPrefs.GetBool(prefName, true)
|
|
};
|
|
|
|
foldout.RegisterValueChangedCallback(evt =>
|
|
EditorPrefs.SetBool(prefName, evt.newValue));
|
|
|
|
parent.Add(foldout);
|
|
|
|
return foldout;
|
|
}
|
|
|
|
static void AddToggle(VisualElement parent, GUIContent text, bool parameter, EventCallback<ChangeEvent<bool>> callback)
|
|
{
|
|
// Create toggle element with the desired text content
|
|
var toggle = new Toggle
|
|
{
|
|
label = text.text,
|
|
value = parameter,
|
|
tooltip = text.tooltip,
|
|
style =
|
|
{
|
|
marginBottom = 0 // To compact a bit the checkboxes list layout
|
|
}
|
|
};
|
|
|
|
// Add padding to guarantee a minimum separation between labels and checkboxes
|
|
toggle.labelElement.style.paddingRight = 20;
|
|
|
|
// Look for the checkbox container and make it align the checkbox to the right (so that all checkboxes are justified)
|
|
foreach (VisualElement child in toggle.Children())
|
|
{
|
|
if (child != toggle.labelElement)
|
|
{
|
|
child.style.justifyContent = Justify.FlexEnd;
|
|
break;
|
|
}
|
|
}
|
|
|
|
toggle.RegisterCallback(callback);
|
|
|
|
parent.Add(toggle);
|
|
}
|
|
|
|
static HelpBox AddHelpBox(HelpBoxMessageType messageType, GUIContent text, int maxWidth, int verticalMargin, bool visible)
|
|
{
|
|
var helpBox = new HelpBox(text.text, messageType)
|
|
{
|
|
tooltip = text.tooltip,
|
|
style =
|
|
{
|
|
marginBottom = verticalMargin,
|
|
marginTop = verticalMargin,
|
|
maxWidth = maxWidth,
|
|
alignSelf = Align.Center,
|
|
},
|
|
visible = visible
|
|
};
|
|
|
|
return helpBox;
|
|
}
|
|
|
|
void DisplayAgentCountWarningBox(int rejected, int allowed)
|
|
{
|
|
m_AgentCountWarning.text = $"Avoidance visualization can be drawn for {allowed} agents ({allowed + rejected} selected).";
|
|
if (!m_AgentCountWarning.visible)
|
|
{
|
|
m_AgentCountWarning.visible = true;
|
|
m_AgentFoldOut.Add(m_AgentCountWarning);
|
|
}
|
|
}
|
|
|
|
void HideAgentCountWarningBox()
|
|
{
|
|
if (m_AgentCountWarning.visible)
|
|
{
|
|
m_AgentCountWarning.visible = false;
|
|
m_AgentFoldOut.Remove(m_AgentCountWarning);
|
|
}
|
|
}
|
|
|
|
void DisplayAgentPendingRequestWarningBox()
|
|
{
|
|
if (!m_AgentPendingRequestWarning.visible)
|
|
{
|
|
m_AgentPendingRequestWarning.visible = true;
|
|
m_AgentFoldOut.Add(m_AgentPendingRequestWarning);
|
|
}
|
|
}
|
|
|
|
void HideAgentPendingRequestWarningBox()
|
|
{
|
|
if (m_AgentPendingRequestWarning.visible)
|
|
{
|
|
m_AgentPendingRequestWarning.visible = false;
|
|
m_AgentFoldOut.Remove(m_AgentPendingRequestWarning);
|
|
}
|
|
}
|
|
|
|
void SetVisualizationEnabled(bool enabled)
|
|
{
|
|
if (m_VisualizationDisabledHelpBox == null)
|
|
return;
|
|
|
|
if (enabled)
|
|
{
|
|
if (m_VisualizationDisabledHelpBox.visible)
|
|
{
|
|
m_RootPanel.Remove(m_VisualizationDisabledHelpBox);
|
|
m_OptionsPanel.SetEnabled(true);
|
|
m_OptionsPanel.tooltip = "";
|
|
m_VisualizationDisabledHelpBox.visible = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!m_VisualizationDisabledHelpBox.visible)
|
|
{
|
|
m_RootPanel.Insert(0, m_VisualizationDisabledHelpBox);
|
|
m_OptionsPanel.SetEnabled(false);
|
|
m_OptionsPanel.tooltip = Style.NavigationVisualizationDisabledTexts.tooltip;
|
|
m_VisualizationDisabledHelpBox.visible = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|