using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; // Editor/PC-only helper — auto-injects at runtime, no need to place in scene. // On Quest builds this entire class is stripped. // // Features: // 1. Replaces TrackedDeviceGraphicRaycaster → GraphicRaycaster (enables mouse clicks) // 2. Keeps worldCamera up to date on all World Space canvases // 3. ESC key navigates back public class DesktopUIMode : MonoBehaviour { #if !UNITY_ANDROID || UNITY_EDITOR [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] private static void AutoCreate() { if (FindObjectOfType() != null) return; new GameObject("[DesktopUIMode]").AddComponent(); } private static readonly System.Collections.Generic.Dictionary BackMap = new() { { "SongSelect", "Menu" }, { "SongCreator", "Menu" }, { "MapEditorScene", "SongCreator" }, { "Game", "SongSelect" }, }; private void Awake() { if (FindObjectsByType(FindObjectsSortMode.None).Length > 1) { Destroy(gameObject); return; } DontDestroyOnLoad(gameObject); SceneManager.sceneLoaded += OnSceneLoaded; PatchCanvases(); } private void OnDestroy() => SceneManager.sceneLoaded -= OnSceneLoaded; private void OnSceneLoaded(Scene s, LoadSceneMode m) => StartCoroutine(PatchAfterFrame()); private System.Collections.IEnumerator PatchAfterFrame() { yield return null; PatchCanvases(); } private void Update() { RefreshCanvasCameras(); if (Keyboard.current?.escapeKey.wasPressedThisFrame == true) GoBack(); } private static void PatchCanvases() { foreach (var canvas in FindObjectsByType(FindObjectsSortMode.None)) { if (canvas.renderMode != RenderMode.WorldSpace) continue; var tracked = canvas.GetComponent("TrackedDeviceGraphicRaycaster"); if (tracked != null) { DestroyImmediate(tracked); if (canvas.GetComponent() == null) canvas.gameObject.AddComponent(); } } RemoveDuplicateAudioListeners(); RefreshCanvasCameras(); } private static void RemoveDuplicateAudioListeners() { var listeners = FindObjectsByType(FindObjectsSortMode.None); if (listeners.Length <= 1) return; AudioListener keep = null; foreach (var al in listeners) if (al.gameObject.scene.name != "DontDestroyOnLoad") { keep = al; break; } keep ??= listeners[0]; foreach (var al in listeners) if (al != keep) DestroyImmediate(al); } private static void RefreshCanvasCameras() { Camera cam = Camera.main; if (cam == null) foreach (var c in FindObjectsByType(FindObjectsSortMode.None)) if (c.enabled && c.gameObject.scene.name != "DontDestroyOnLoad") { cam = c; break; } cam ??= FindObjectOfType(); if (cam == null) return; foreach (var canvas in FindObjectsByType(FindObjectsSortMode.None)) if (canvas.renderMode == RenderMode.WorldSpace && canvas.worldCamera != cam) canvas.worldCamera = cam; } private static void GoBack() { if (BackMap.TryGetValue(SceneManager.GetActiveScene().name, out string target)) SceneManager.LoadScene(target); } #endif }