#if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE #define USE_INPUT_SYSTEM using UnityEngine.InputSystem; using UnityEngine.InputSystem.UI; using UnityEngine.InputSystem.EnhancedTouch; #endif using System; using System.Collections; using UnityEngine.EventSystems; namespace UnityEngine.Rendering { class DebugUpdater : MonoBehaviour { static DebugUpdater s_Instance = null; ScreenOrientation m_Orientation; bool m_RuntimeUiWasVisibleLastFrame = false; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] static void RuntimeInit() { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (DebugManager.instance.enableRuntimeUI) EnableRuntime(); #endif } internal static void SetEnabled(bool enabled) { if (enabled) EnableRuntime(); else DisableRuntime(); } static void EnableRuntime() { if (s_Instance != null) return; var go = new GameObject { name = "[Debug Updater]" }; s_Instance = go.AddComponent(); s_Instance.m_Orientation = Screen.orientation; DontDestroyOnLoad(go); DebugManager.instance.EnableInputActions(); #if USE_INPUT_SYSTEM EnhancedTouchSupport.Enable(); #endif } static void DisableRuntime() { DebugManager debugManager = DebugManager.instance; debugManager.displayRuntimeUI = false; debugManager.displayPersistentRuntimeUI = false; if (s_Instance != null) { CoreUtils.Destroy(s_Instance.gameObject); s_Instance = null; } } internal static void HandleInternalEventSystemComponents(bool uiEnabled) { if (s_Instance == null) return; if (uiEnabled) s_Instance.EnsureExactlyOneEventSystem(); else s_Instance.DestroyDebugEventSystem(); } void EnsureExactlyOneEventSystem() { var eventSystems = FindObjectsOfType(); var debugEventSystem = GetComponent(); if (eventSystems.Length > 1 && debugEventSystem != null) { Debug.Log($"More than one EventSystem detected in scene. Destroying EventSystem owned by DebugUpdater."); DestroyDebugEventSystem(); } else if (eventSystems.Length == 0) { Debug.Log($"No EventSystem available. Creating a new EventSystem to enable Rendering Debugger runtime UI."); CreateDebugEventSystem(); } else { StartCoroutine(DoAfterInputModuleUpdated(CheckInputModuleExists)); } } IEnumerator DoAfterInputModuleUpdated(Action action) { // EventSystem.current.currentInputModule is not updated immediately when EventSystem.current changes. It happens // with a delay in EventSystem.Update(), so wait a couple of frames to ensure that has happened. yield return new WaitForEndOfFrame(); yield return new WaitForEndOfFrame(); action.Invoke(); } void CheckInputModuleExists() { if (EventSystem.current != null && EventSystem.current.currentInputModule == null) { Debug.LogWarning("Found a game object with EventSystem component but no corresponding BaseInputModule component - Debug UI input might not work correctly."); } } #if USE_INPUT_SYSTEM void AssignDefaultActions() { if (EventSystem.current != null && EventSystem.current.currentInputModule is InputSystemUIInputModule inputSystemModule) { // FIXME: In order to activate default input actions in player builds (required for touch input to work), // we need to call InputSystemUIInputModule.AssignDefaultActions() which was added in com.unity.inputsystem@1.1.0-pre.5. // However, there is a problem in InputSystem package version ordering, where it sorts this version as an // older version than it should be. Hence we cannot write a version define to conditionally compile this function call. // Instead, we use reflection to see if the function is there and can be invoked. // // Once com.unity.inputsystem@1.1.0 is available, create an INPUTSYSTEM_1_1_0_OR_GREATER version define and use it // to conditionally call AssignDefaultActions(). System.Reflection.MethodInfo assignDefaultActionsMethod = inputSystemModule.GetType().GetMethod("AssignDefaultActions"); if (assignDefaultActionsMethod != null) { assignDefaultActionsMethod.Invoke(inputSystemModule, null); } } CheckInputModuleExists(); } #endif void CreateDebugEventSystem() { gameObject.AddComponent(); #if USE_INPUT_SYSTEM gameObject.AddComponent(); StartCoroutine(DoAfterInputModuleUpdated(AssignDefaultActions)); #else gameObject.AddComponent(); #endif } void DestroyDebugEventSystem() { var eventSystem = GetComponent(); #if USE_INPUT_SYSTEM var inputModule = GetComponent(); if (inputModule) { CoreUtils.Destroy(inputModule); StartCoroutine(DoAfterInputModuleUpdated(AssignDefaultActions)); } #else CoreUtils.Destroy(GetComponent()); CoreUtils.Destroy(GetComponent()); #endif CoreUtils.Destroy(eventSystem); } void Update() { DebugManager debugManager = DebugManager.instance; // Runtime UI visibility can change i.e. due to scene unload - allow component cleanup in this case. if (m_RuntimeUiWasVisibleLastFrame != debugManager.displayRuntimeUI) { HandleInternalEventSystemComponents(debugManager.displayRuntimeUI); } debugManager.UpdateActions(); if (debugManager.GetAction(DebugAction.EnableDebugMenu) != 0.0f || debugManager.GetActionToggleDebugMenuWithTouch()) { debugManager.displayRuntimeUI = !debugManager.displayRuntimeUI; } if (debugManager.displayRuntimeUI) { if (debugManager.GetAction(DebugAction.ResetAll) != 0.0f) debugManager.Reset(); if (debugManager.GetActionReleaseScrollTarget()) debugManager.SetScrollTarget(null); // Allow mouse wheel scroll without causing auto-scroll } if (m_Orientation != Screen.orientation) { StartCoroutine(RefreshRuntimeUINextFrame()); m_Orientation = Screen.orientation; } m_RuntimeUiWasVisibleLastFrame = debugManager.displayRuntimeUI; } static IEnumerator RefreshRuntimeUINextFrame() { yield return null; // Defer runtime UI refresh to next frame to allow canvas to update first. DebugManager.instance.ReDrawOnScreenDebug(); } } }