From 54f78ac10b4ba09c65840524be51e5c62165042b Mon Sep 17 00:00:00 2001 From: Sinai Date: Sat, 15 May 2021 06:20:56 +1000 Subject: [PATCH] Implement InspectUnderMouse --- src/UI/Inspectors/InspectUnderMouse.cs | 380 ++++++++++++++++++++ src/UI/Inspectors/TODO_InspectUnderMouse.cs | 327 ----------------- 2 files changed, 380 insertions(+), 327 deletions(-) create mode 100644 src/UI/Inspectors/InspectUnderMouse.cs delete mode 100644 src/UI/Inspectors/TODO_InspectUnderMouse.cs diff --git a/src/UI/Inspectors/InspectUnderMouse.cs b/src/UI/Inspectors/InspectUnderMouse.cs new file mode 100644 index 0000000..4df6d7a --- /dev/null +++ b/src/UI/Inspectors/InspectUnderMouse.cs @@ -0,0 +1,380 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using UnityExplorer.Core; +using UnityExplorer.Core.Input; +using UnityExplorer.Core.Runtime; +using UnityExplorer.UI; +using UnityExplorer.UI.Panels; + +namespace UnityExplorer.UI.Inspectors +{ + public enum MouseInspectMode + { + World, + UI + } + + public class InspectUnderMouse : UIPanel + { + public static InspectUnderMouse Instance { get; private set; } + + public InspectUnderMouse() { Instance = this; } + + public static void OnDropdownSelect(int index) + { + switch (index) + { + case 0: return; + case 1: Instance.StartInspect(MouseInspectMode.World); break; + case 2: Instance.StartInspect(MouseInspectMode.UI); break; + } + UIManager.MouseInspectDropdown.value = 0; + } + + // UIPanel + public override string Name => "Inspect Under Mouse"; + public override UIManager.Panels PanelType => UIManager.Panels.MouseInspector; + public override int MinWidth => -1; + public override int MinHeight => -1; + public override bool CanDragAndResize => false; + public override bool NavButtonWanted => false; + public override bool ShouldSaveActiveState => false; + public override bool ShowByDefault => false; + + internal static Text objNameLabel; + internal static Text objPathLabel; + internal static Text mousePosLabel; + + // Mouse Inspector + public static bool Inspecting { get; set; } + public static MouseInspectMode Mode { get; set; } + + private static GameObject lastHitObject; + private static Vector3 lastMousePos; + + private static readonly List wasDisabledGraphics = new List(); + private static readonly List wasDisabledCanvasGroups = new List(); + private static readonly List objectsAddedCastersTo = new List(); + + internal static Camera MainCamera; + internal static GraphicRaycaster[] graphicRaycasters; + + + public void StartInspect(MouseInspectMode mode) + { + MainCamera = Camera.main; + if (!MainCamera) + return; + + Mode = mode; + Inspecting = true; + UIManager.NavBarRect.gameObject.SetActive(false); + UIManager.PanelHolder.SetActive(false); + + UIRoot.SetActive(true); + + if (mode == MouseInspectMode.UI) + SetupUIRaycast(); + } + + internal void ClearHitData() + { + lastHitObject = null; + objNameLabel.text = "No hits..."; + objPathLabel.text = ""; + } + + public void StopInspect() + { + Inspecting = false; + UIManager.NavBarRect.gameObject.SetActive(true); + UIManager.PanelHolder.SetActive(true); + UIRoot.SetActive(false); + + if (Mode == MouseInspectMode.UI) + StopUIInspect(); + + ClearHitData(); + } + + private static float timeOfLastRaycast; + + public void UpdateInspect() + { + if (InputManager.GetKeyDown(KeyCode.Escape)) + { + StopInspect(); + return; + } + + if (lastHitObject && InputManager.GetMouseButtonDown(0)) + { + var target = lastHitObject; + StopInspect(); + InspectorManager.Inspect(target); + return; + } + + var mousePos = InputManager.MousePosition; + + if (mousePos != lastMousePos) + UpdatePosition(mousePos); + + if (!timeOfLastRaycast.OccuredEarlierThan(0.1f)) + return; + + timeOfLastRaycast = Time.realtimeSinceStartup; + + // actual inspect raycast + + switch (Mode) + { + case MouseInspectMode.UI: + RaycastUI(mousePos); break; + case MouseInspectMode.World: + RaycastWorld(mousePos); break; + } + } + + internal void UpdatePosition(Vector2 mousePos) + { + lastMousePos = mousePos; + + // use the raw mouse pos for the label + mousePosLabel.text = $"Mouse Position: {mousePos.ToString()}"; + + // constrain the mouse pos we use within certain bounds + if (mousePos.x < 350) + mousePos.x = 350; + if (mousePos.x > Screen.width - 350) + mousePos.x = Screen.width - 350; + if (mousePos.y < mainPanelRect.rect.height) + mousePos.y += mainPanelRect.rect.height + 10; + else + mousePos.y -= 10; + + // calculate and set our UI position + var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos); + + UIRoot.transform.localPosition = new Vector3(inversePos.x, inversePos.y, 0); + } + + internal void OnHitGameObject(GameObject obj) + { + if (obj != lastHitObject) + { + lastHitObject = obj; + objNameLabel.text = $"Click to Inspect: {obj.name}"; + objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}"; + } + } + + // Collider raycasting + + internal void RaycastWorld(Vector2 mousePos) + { + var ray = MainCamera.ScreenPointToRay(mousePos); + Physics.Raycast(ray, out RaycastHit hit, 1000f); + + if (hit.transform) + { + var obj = hit.transform.gameObject; + OnHitGameObject(obj); + } + else + { + if (lastHitObject) + ClearHitData(); + } + } + + // UI Graphic raycasting + + private static void SetupUIRaycast() + { + foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Canvas))) + { + var canvas = obj.TryCast(); + if (!canvas || !canvas.enabled || !canvas.gameObject.activeInHierarchy) + continue; + if (!canvas.GetComponent()) + { + canvas.gameObject.AddComponent(); + //ExplorerCore.Log("Added raycaster to " + canvas.name); + objectsAddedCastersTo.Add(canvas.gameObject); + } + } + + // recache Graphic Raycasters each time we start + var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster)); + graphicRaycasters = new GraphicRaycaster[casters.Length]; + for (int i = 0; i < casters.Length; i++) + { + graphicRaycasters[i] = casters[i].TryCast(); + } + + // enable raycastTarget on Graphics + foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Graphic))) + { + var graphic = obj.TryCast(); + if (!graphic || !graphic.enabled || graphic.raycastTarget || !graphic.gameObject.activeInHierarchy) + continue; + graphic.raycastTarget = true; + //ExplorerCore.Log("Enabled raycastTarget on " + graphic.name); + wasDisabledGraphics.Add(graphic); + } + + // enable blocksRaycasts on CanvasGroups + foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(CanvasGroup))) + { + var canvas = obj.TryCast(); + if (!canvas || !canvas.gameObject.activeInHierarchy || canvas.blocksRaycasts) + continue; + canvas.blocksRaycasts = true; + //ExplorerCore.Log("Enabled raycasts on " + canvas.name); + wasDisabledCanvasGroups.Add(canvas); + } + } + + internal void RaycastUI(Vector2 mousePos) + { + var ped = new PointerEventData(null) + { + position = mousePos + }; + + //ExplorerCore.Log("~~~~~~~~~ begin raycast ~~~~~~~~"); + GameObject hitObject = null; + int highestLayer = int.MinValue; + int highestOrder = int.MinValue; + int highestDepth = int.MinValue; + foreach (var gr in graphicRaycasters) + { + if (!gr || !gr.canvas) + continue; + + var list = new List(); + RuntimeProvider.Instance.GraphicRaycast(gr, ped, list); + + if (list.Count > 0) + { + foreach (var hit in list) + { + // Manualy trying to determine which object is "on top". + // Could be improved, but seems to work pretty well and isn't as laggy as you would expect. + + if (!hit.gameObject) + continue; + + if (hit.gameObject.GetComponent() is CanvasGroup group && group.alpha == 0) + continue; + + if (hit.gameObject.GetComponent() is Graphic graphic && graphic.color.a == 0f) + continue; + + if (hit.sortingLayer < highestLayer) + continue; + + if (hit.sortingLayer > highestLayer) + { + highestLayer = hit.sortingLayer; + highestDepth = int.MinValue; + } + + if (hit.depth < highestDepth) + continue; + + if (hit.depth > highestDepth) + { + highestDepth = hit.depth; + highestOrder = int.MinValue; + } + + if (hit.sortingOrder <= highestOrder) + continue; + + highestOrder = hit.sortingOrder; + hitObject = hit.gameObject; + } + } + else + { + if (lastHitObject) + ClearHitData(); + } + } + + if (hitObject) + OnHitGameObject(hitObject); + + //ExplorerCore.Log("~~~~~~~~~ end raycast ~~~~~~~~"); + } + + private static void StopUIInspect() + { + foreach (var obj in objectsAddedCastersTo) + { + if (obj.GetComponent() is GraphicRaycaster raycaster) + GameObject.Destroy(raycaster); + } + + foreach (var graphic in wasDisabledGraphics) + graphic.raycastTarget = false; + + foreach (var canvas in wasDisabledCanvasGroups) + canvas.blocksRaycasts = false; + + objectsAddedCastersTo.Clear(); + wasDisabledCanvasGroups.Clear(); + wasDisabledGraphics.Clear(); + } + + + // UI Construction + + protected internal override void DoSetDefaultPosAndAnchors() + { + mainPanelRect.anchorMin = Vector2.zero; + mainPanelRect.anchorMax = Vector2.zero; + mainPanelRect.pivot = new Vector2(0.5f, 1); + mainPanelRect.sizeDelta = new Vector2(700, 150); + } + + public override void ConstructPanelContent() + { + // hide title bar + this.titleBar.SetActive(false); + this.UIRoot.transform.SetParent(UIManager.CanvasRoot.transform, false); + + var inspectContent = UIFactory.CreateVerticalGroup(this.content, "InspectContent", true, true, true, true, 3, new Vector4(2, 2, 2, 2)); + UIFactory.SetLayoutElement(inspectContent, flexibleWidth: 9999, flexibleHeight: 9999); + + // Title text + + var title = UIFactory.CreateLabel(inspectContent, "InspectLabel", "Mouse Inspector (press ESC to cancel)", TextAnchor.MiddleCenter); + UIFactory.SetLayoutElement(title.gameObject, flexibleWidth: 9999); + + mousePosLabel = UIFactory.CreateLabel(inspectContent, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter); + + objNameLabel = UIFactory.CreateLabel(inspectContent, "HitLabelObj", "No hits...", TextAnchor.MiddleLeft); + objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow; + + objPathLabel = UIFactory.CreateLabel(inspectContent, "PathLabel", "", TextAnchor.MiddleLeft); + objPathLabel.fontStyle = FontStyle.Italic; + objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap; + + UIFactory.SetLayoutElement(objPathLabel.gameObject, minHeight: 75); + + UIRoot.SetActive(false); + } + + public override void DoSaveToConfigElement() { } + + public override string GetSaveDataFromConfigManager() => null; + } +} diff --git a/src/UI/Inspectors/TODO_InspectUnderMouse.cs b/src/UI/Inspectors/TODO_InspectUnderMouse.cs deleted file mode 100644 index 188618c..0000000 --- a/src/UI/Inspectors/TODO_InspectUnderMouse.cs +++ /dev/null @@ -1,327 +0,0 @@ -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using UnityEngine; -//using UnityEngine.EventSystems; -//using UnityEngine.UI; -//using UnityExplorer.Core; -//using UnityExplorer.Core.Input; -//using UnityExplorer.Core.Runtime; -//using UnityExplorer.UI; -//using UnityExplorer.UI.Main; -//using UnityExplorer.Inspectors; - -//namespace UnityExplorer.UI.Main.Home -//{ -// public class InspectUnderMouse -// { -// public enum MouseInspectMode -// { -// World, -// UI -// } - -// public static bool Inspecting { get; set; } - -// public static MouseInspectMode Mode { get; set; } - -// private static GameObject s_lastHit; -// private static Vector3 s_lastMousePos; - -// private static readonly List _wasDisabledGraphics = new List(); -// private static readonly List _wasDisabledCanvasGroups = new List(); -// private static readonly List _objectsAddedCastersTo = new List(); - -// internal static Camera MainCamera; -// internal static GraphicRaycaster[] graphicRaycasters; - -// public static void Init() -// { -// ConstructUI(); -// } - -// public static void StartInspect(MouseInspectMode mode) -// { -// MainCamera = Camera.main; -// if (!MainCamera) -// return; - -// Mode = mode; -// Inspecting = true; -// MainMenu.Instance.MainPanel.SetActive(false); - -// s_UIContent.SetActive(true); - -// if (mode == MouseInspectMode.UI) -// SetupUIRaycast(); -// } - -// internal static void ClearHitData() -// { -// s_lastHit = null; -// s_objNameLabel.text = "No hits..."; -// s_objPathLabel.text = ""; -// } - -// public static void StopInspect() -// { -// Inspecting = false; -// MainMenu.Instance.MainPanel.SetActive(true); -// s_UIContent.SetActive(false); - -// if (Mode == MouseInspectMode.UI) -// StopUIInspect(); - -// ClearHitData(); -// } - -// public static void UpdateInspect() -// { -// if (InputManager.GetKeyDown(KeyCode.Escape)) -// { -// StopInspect(); -// return; -// } - -// var mousePos = InputManager.MousePosition; - -// if (mousePos != s_lastMousePos) -// UpdatePosition(mousePos); - -// // actual inspect raycast - -// switch (Mode) -// { -// case MouseInspectMode.UI: -// RaycastUI(mousePos); break; -// case MouseInspectMode.World: -// RaycastWorld(mousePos); break; -// } -// } - -// internal static void UpdatePosition(Vector2 mousePos) -// { -// s_lastMousePos = mousePos; - -// var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos); - -// s_mousePosLabel.text = $"Mouse Position: {mousePos.ToString()}"; - -// float yFix = mousePos.y < 120 ? 80 : -80; -// s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0); -// } - -// internal static void OnHitGameObject(GameObject obj) -// { -// if (obj != s_lastHit) -// { -// s_lastHit = obj; -// s_objNameLabel.text = $"Click to Inspect: {obj.name}"; -// s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}"; -// } - -// if (InputManager.GetMouseButtonDown(0)) -// { -// StopInspect(); -// InspectorManager.Instance.Inspect(obj); -// } -// } - -// // Collider raycasting - -// internal static void RaycastWorld(Vector2 mousePos) -// { -// var ray = MainCamera.ScreenPointToRay(mousePos); -// Physics.Raycast(ray, out RaycastHit hit, 1000f); - -// if (hit.transform) -// { -// var obj = hit.transform.gameObject; -// OnHitGameObject(obj); -// } -// else -// { -// if (s_lastHit) -// ClearHitData(); -// } -// } - -// // UI Graphic raycasting - -// private static void SetupUIRaycast() -// { -// foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Canvas))) -// { -// var canvas = obj.Cast(typeof(Canvas)) as Canvas; -// if (!canvas || !canvas.enabled || !canvas.gameObject.activeInHierarchy) -// continue; -// if (!canvas.GetComponent()) -// { -// canvas.gameObject.AddComponent(); -// //ExplorerCore.Log("Added raycaster to " + canvas.name); -// _objectsAddedCastersTo.Add(canvas.gameObject); -// } -// } - -// // recache Graphic Raycasters each time we start -// var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster)); -// graphicRaycasters = new GraphicRaycaster[casters.Length]; -// for (int i = 0; i < casters.Length; i++) -// { -// graphicRaycasters[i] = casters[i].Cast(typeof(GraphicRaycaster)) as GraphicRaycaster; -// } - -// // enable raycastTarget on Graphics -// foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Graphic))) -// { -// var graphic = obj.Cast(typeof(Graphic)) as Graphic; -// if (!graphic || !graphic.enabled || graphic.raycastTarget || !graphic.gameObject.activeInHierarchy) -// continue; -// graphic.raycastTarget = true; -// //ExplorerCore.Log("Enabled raycastTarget on " + graphic.name); -// _wasDisabledGraphics.Add(graphic); -// } - -// // enable blocksRaycasts on CanvasGroups -// foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(CanvasGroup))) -// { -// var canvas = obj.Cast(typeof(CanvasGroup)) as CanvasGroup; -// if (!canvas || !canvas.gameObject.activeInHierarchy || canvas.blocksRaycasts) -// continue; -// canvas.blocksRaycasts = true; -// //ExplorerCore.Log("Enabled raycasts on " + canvas.name); -// _wasDisabledCanvasGroups.Add(canvas); -// } -// } - -// internal static void RaycastUI(Vector2 mousePos) -// { -// var ped = new PointerEventData(null) -// { -// position = mousePos -// }; - -// //ExplorerCore.Log("~~~~~~~~~ begin raycast ~~~~~~~~"); -// GameObject hitObject = null; -// int highestLayer = int.MinValue; -// int highestOrder = int.MinValue; -// int highestDepth = int.MinValue; -// foreach (var gr in graphicRaycasters) -// { -// var list = new List(); -// RuntimeProvider.Instance.GraphicRaycast(gr, ped, list); - -// //gr.Raycast(ped, list); - -// if (list.Count > 0) -// { -// foreach (var hit in list) -// { -// // Manual trying to determine which object is "on top". -// // Not perfect, but not terrible. - -// if (!hit.gameObject) -// continue; - -// if (hit.gameObject.GetComponent() is CanvasGroup group && group.alpha == 0) -// continue; - -// if (hit.gameObject.GetComponent() is Graphic graphic && graphic.color.a == 0f) -// continue; - -// if (hit.sortingLayer < highestLayer) -// continue; - -// if (hit.sortingLayer > highestLayer) -// { -// highestLayer = hit.sortingLayer; -// highestDepth = int.MinValue; -// } - -// if (hit.depth < highestDepth) -// continue; - -// if (hit.depth > highestDepth) -// { -// highestDepth = hit.depth; -// highestOrder = int.MinValue; -// } - -// if (hit.sortingOrder <= highestOrder) -// continue; - -// highestOrder = hit.sortingOrder; -// hitObject = hit.gameObject; -// } -// } -// else -// { -// if (s_lastHit) -// ClearHitData(); -// } -// } - -// if (hitObject) -// OnHitGameObject(hitObject); - -// //ExplorerCore.Log("~~~~~~~~~ end raycast ~~~~~~~~"); -// } - -// private static void StopUIInspect() -// { -// foreach (var obj in _objectsAddedCastersTo) -// { -// if (obj.GetComponent() is GraphicRaycaster raycaster) -// GameObject.Destroy(raycaster); -// } - -// foreach (var graphic in _wasDisabledGraphics) -// graphic.raycastTarget = false; - -// foreach (var canvas in _wasDisabledCanvasGroups) -// canvas.blocksRaycasts = false; - -// _objectsAddedCastersTo.Clear(); -// _wasDisabledCanvasGroups.Clear(); -// _wasDisabledGraphics.Clear(); -// } - -// internal static Text s_objNameLabel; -// internal static Text s_objPathLabel; -// internal static Text s_mousePosLabel; -// internal static GameObject s_UIContent; - -// internal static void ConstructUI() -// { -// s_UIContent = UIFactory.CreatePanel("InspectUnderMouse_UI", out GameObject content); - -// var baseRect = s_UIContent.GetComponent(); -// var half = new Vector2(0.5f, 0.5f); -// baseRect.anchorMin = half; -// baseRect.anchorMax = half; -// baseRect.pivot = half; -// baseRect.sizeDelta = new Vector2(700, 150); - -// var group = content.GetComponent(); -// group.childForceExpandHeight = true; - -// // Title text - -// UIFactory.CreateLabel(content, "InspectLabel", "Mouse Inspector (press ESC to cancel)", TextAnchor.MiddleCenter); - -// s_mousePosLabel = UIFactory.CreateLabel(content, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter); - -// s_objNameLabel = UIFactory.CreateLabel(content, "HitLabelObj", "No hits...", TextAnchor.MiddleLeft); -// s_objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow; - -// s_objPathLabel = UIFactory.CreateLabel(content, "PathLabel", "", TextAnchor.MiddleLeft); -// s_objPathLabel.fontStyle = FontStyle.Italic; -// s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap; - -// UIFactory.SetLayoutElement(s_objPathLabel.gameObject, minHeight: 75); - -// s_UIContent.SetActive(false); -// } -// } -//}