Files
BeatSaber/Assets/Script/DesktopUIMode.cs
T
2026-05-26 18:54:56 +09:00

111 lines
3.6 KiB
C#

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 (FindFirstObjectByType<DesktopUIMode>() != null) return;
new GameObject("[DesktopUIMode]").AddComponent<DesktopUIMode>();
}
private static readonly System.Collections.Generic.Dictionary<string, string> BackMap =
new()
{
{ "SongSelect", "Menu" },
{ "SongCreator", "Menu" },
{ "MapEditorScene", "SongCreator" },
{ "Game", "SongSelect" },
};
private void Awake()
{
if (FindObjectsByType<DesktopUIMode>(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<Canvas>(FindObjectsSortMode.None))
{
if (canvas.renderMode != RenderMode.WorldSpace) continue;
var tracked = canvas.GetComponent("TrackedDeviceGraphicRaycaster");
if (tracked != null)
{
DestroyImmediate(tracked);
if (canvas.GetComponent<GraphicRaycaster>() == null)
canvas.gameObject.AddComponent<GraphicRaycaster>();
}
}
RemoveDuplicateAudioListeners();
RefreshCanvasCameras();
}
private static void RemoveDuplicateAudioListeners()
{
var listeners = FindObjectsByType<AudioListener>(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<Camera>(FindObjectsSortMode.None))
if (c.enabled && c.gameObject.scene.name != "DontDestroyOnLoad") { cam = c; break; }
cam ??= FindFirstObjectByType<Camera>();
if (cam == null) return;
foreach (var canvas in FindObjectsByType<Canvas>(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
}