using UnityEngine; using UnityEngine.SceneManagement; public static class BgmRuntimeBootstrap { private const string RuntimeAudioListenerName = "__RuntimeAudioListener"; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] private static void Bootstrap() { var mgr = BgmPlaybackManager.EnsureInstance(); mgr.EnsurePlaying(); EnsureAudioListener(SceneManager.GetActiveScene()); // Ensure UI binder exists if (Object.FindAnyObjectByType() == null) { var go = new GameObject("BGM_UI_Binder"); go.AddComponent(); Object.DontDestroyOnLoad(go); } RemoveBgmParticlesInLoadedScenes(); SceneManager.sceneLoaded -= OnSceneLoaded; SceneManager.sceneLoaded += OnSceneLoaded; } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { EnsureAudioListener(scene); RemoveBgmParticlesInLoadedScenes(); } private static void RemoveBgmParticlesInLoadedScenes() { var emitters = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < emitters.Length; i++) { var e = emitters[i]; if (e == null) continue; if (!e.gameObject.scene.IsValid() || !e.gameObject.scene.isLoaded) continue; Object.Destroy(e.gameObject); } var all = Resources.FindObjectsOfTypeAll(); for (int i = 0; i < all.Length; i++) { var t = all[i]; if (t == null) continue; if (!t.gameObject.scene.IsValid() || !t.gameObject.scene.isLoaded) continue; if (t.name.Equals("BgmParticles", System.StringComparison.OrdinalIgnoreCase)) Object.Destroy(t.gameObject); } } private static void EnsureAudioListener(Scene scene) { if (!scene.IsValid() || !scene.isLoaded) return; AudioListener[] allListeners = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); AudioListener activeEnabledInScene = null; AudioListener activeInScene = null; AudioListener anyInScene = null; for (int i = 0; i < allListeners.Length; i++) { AudioListener listener = allListeners[i]; if (listener == null) continue; if (!listener.gameObject.scene.IsValid() || listener.gameObject.scene != scene) continue; if (anyInScene == null) anyInScene = listener; if (listener.gameObject.activeInHierarchy && activeInScene == null) activeInScene = listener; if (listener.gameObject.activeInHierarchy && listener.enabled) { activeEnabledInScene = listener; break; } } if (activeEnabledInScene != null) return; if (activeInScene != null) { activeInScene.enabled = true; return; } if (anyInScene != null) { anyInScene.enabled = true; return; } Camera cameraInScene = FindBestCameraInScene(scene); if (cameraInScene != null) { AudioListener cameraListener = cameraInScene.GetComponent(); if (cameraListener == null) cameraListener = cameraInScene.gameObject.AddComponent(); cameraListener.enabled = true; return; } GameObject listenerRoot = FindNamedObjectInScene(scene, RuntimeAudioListenerName); if (listenerRoot == null) { listenerRoot = new GameObject(RuntimeAudioListenerName); try { SceneManager.MoveGameObjectToScene(listenerRoot, scene); } catch { } } if (listenerRoot.GetComponent() == null) listenerRoot.AddComponent(); } private static Camera FindBestCameraInScene(Scene scene) { Camera[] cameras = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); Camera first = null; for (int i = 0; i < cameras.Length; i++) { Camera cam = cameras[i]; if (cam == null || cam.gameObject.scene != scene) continue; if (first == null) first = cam; if (cam.gameObject.activeInHierarchy && cam.enabled && cam.CompareTag("MainCamera")) return cam; } if (first != null) return first; return null; } private static GameObject FindNamedObjectInScene(Scene scene, string objectName) { if (string.IsNullOrEmpty(objectName)) return null; Transform[] all = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < all.Length; i++) { Transform t = all[i]; if (t == null || t.gameObject.scene != scene) continue; if (string.Equals(t.name, objectName, System.StringComparison.OrdinalIgnoreCase)) return t.gameObject; } return null; } }