using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; public static class BgmRuntimeBootstrap { private const string RuntimeAudioListenerName = "__RuntimeAudioListener"; private static readonly List _transformBuffer = new List(512); private static readonly List _transformStack = new List(512); [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)] private static void Bootstrap() { var mgr = BgmPlaybackManager.EnsureInstance(); var controller = SceneObjectLookupCache.FindAny(); if (controller != null && controller.bgmMixerGroup != null) { mgr.SetOutputMixerGroup(controller.bgmMixerGroup); } mgr.EnsurePlaying(); EnsureAudioListener(SceneManager.GetActiveScene()); // Ensure UI binder exists if (SceneObjectLookupCache.FindAny() == 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); var mgr = BgmPlaybackManager.Instance ?? BgmPlaybackManager.EnsureInstance(); mgr.EnsurePlaying(); RemoveBgmParticlesInLoadedScenes(); } private static void RemoveBgmParticlesInLoadedScenes() { int sceneCount = SceneManager.sceneCount; for (int i = 0; i < sceneCount; i++) { var scene = SceneManager.GetSceneAt(i); if (!scene.IsValid() || !scene.isLoaded) continue; RemoveBgmParticlesInScene(scene); } } private static void RemoveBgmParticlesInScene(Scene scene) { 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); } CollectTransformsInScene(scene, _transformBuffer); for (int i = 0; i < _transformBuffer.Count; i++) { var t = _transformBuffer[i]; if (t == null || t.gameObject.scene != scene) 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; CollectTransformsInScene(scene, _transformBuffer); for (int i = 0; i < _transformBuffer.Count; i++) { var t = _transformBuffer[i]; if (t == null || t.gameObject.scene != scene) continue; if (string.Equals(t.name, objectName, System.StringComparison.OrdinalIgnoreCase)) return t.gameObject; } return null; } private static void CollectTransformsInScene(Scene scene, List buffer) { buffer.Clear(); _transformStack.Clear(); var roots = scene.GetRootGameObjects(); for (int i = 0; i < roots.Length; i++) { var root = roots[i]; if (root == null) continue; _transformStack.Add(root.transform); while (_transformStack.Count > 0) { int last = _transformStack.Count - 1; var t = _transformStack[last]; _transformStack.RemoveAt(last); if (t == null) continue; buffer.Add(t); int childCount = t.childCount; for (int c = 0; c < childCount; c++) { _transformStack.Add(t.GetChild(c)); } } } } }