Files
bansonic_beta_main/Assets/scripts/Audio/BgmUiBinder.cs
T

986 lines
33 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.SceneManagement;
using UnityEngine.Audio;
using Object = UnityEngine.Object;
[ExecuteAlways]
public class BgmUiBinder : MonoBehaviour
{
public static BgmUiBinder Instance { get; private set; }
[Header("Lookup Names")]
public string musicRootName = "MusicPic";
public string timeRootName = "TIME";
public string progressName = "BGM_Progress";
public string pointerName = "Zhizhen";
public string spectrumRootName = "SpectrumRoot";
public string musicPicCanvasName = "MusicPicCanvas";
[Header("Buttons")]
public string prevButtonName = "Previous_Song";
public string nextButtonName = "Next_Song";
public string pauseButtonName = "pause_Song";
public string musicToggleButtonName = "Button_Music";
public Button prevButton;
public Button nextButton;
public Button pauseButton;
public Button musicToggleButton;
public bool useManualMusicToggle = true;
public bool bindMusicToggleButton = false;
[Header("Bindings (optional)")]
public Image progressImage;
public Slider progressSlider;
public TMP_Text timeTmp;
public Text timeText;
public RectTransform pointerRect;
public Vector2 pointerOffset = Vector2.zero;
public bool enableMusicPicGyro = false;
[Header("MusicPic Toggle")]
public bool keepMusicPicAcrossScenes = false;
public bool showMusicPicInLobby = false;
public bool forceHideOnSceneLoad = true;
public string lobbySceneName = "UI_UI";
public string gameplaySceneName = "gamePlay_gamePlay";
[Tooltip("Main menu scene name where spectrum should be hidden.")]
public string mainSceneName = "Main_main";
[Tooltip("Startup/EULA scene name where spectrum should be hidden.")]
public string beforeEverythingSceneName = "beforeEverything";
public bool hideSpectrumInMainScene = true;
public float musicPicFadeDuration = 0.3f;
public bool useUnscaledTime = true;
[Header("Auto Create")]
public bool autoCreateProgress = false;
public Vector2 defaultProgressSize = new Vector2(320f, 8f);
public Vector2 defaultProgressPosition = Vector2.zero;
public Color progressColor = Color.white;
public Vector2 pointerStartPos = new Vector2(-190.98f, -87.05f);
public Vector2 pointerEndPos = new Vector2(195.3f, -87.05f);
public Vector2 spectrumPosition = new Vector2(-238.9f, -111.5f);
[Header("Audio")]
public AudioMixerGroup outputMixerGroup;
private readonly List<TMP_Text> timeTmps = new List<TMP_Text>();
private readonly List<Text> timeTexts = new List<Text>();
private System.Text.StringBuilder _sb = new System.Text.StringBuilder(32);
private int _lastSeconds = -1;
private int _lastTotalSeconds = -1;
private RectTransform musicRoot;
private bool hasExplicitMusicRoot = false;
private RectTransform progressRect;
private float pointerBaseY = 0f;
private bool musicPicVisible = true;
private Coroutine musicPicTween;
private Coroutine lobbyShowRoutine;
private CanvasGroup musicPicGroup;
private RectTransform spectrumRoot;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
}
private string _cachedGameplaySceneName;
private bool _shouldHideSpectrumForCurrentScene;
private bool _spectrumHiddenForCurrentScene = false;
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
_cachedGameplaySceneName = gameplaySceneName;
_shouldHideSpectrumForCurrentScene = ShouldHideSpectrumInScene(SceneManager.GetActiveScene().name);
_spectrumHiddenForCurrentScene = false;
_buttonsBound = false;
Bind();
ApplyDefaultMusicPicVisibility(SceneManager.GetActiveScene().name);
if (Application.isPlaying)
{
var mgr = BgmPlaybackManager.EnsureInstance();
if (outputMixerGroup != null)
mgr.SetOutputMixerGroup(outputMixerGroup);
mgr.EnsurePlaying();
}
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
_shouldHideSpectrumForCurrentScene = ShouldHideSpectrumInScene(scene.name);
_spectrumHiddenForCurrentScene = false;
_buttonsBound = false;
Bind();
ApplyDefaultMusicPicVisibility(scene.name);
}
public void Bind()
{
musicRoot = null;
hasExplicitMusicRoot = false;
Canvas sceneCanvas = FindSceneCanvas();
var controller = SceneObjectLookupCache.FindAny<btmandtopController>();
if (controller != null && controller.bgmMixerGroup != null)
{
outputMixerGroup = controller.bgmMixerGroup;
if (Application.isPlaying)
{
var mgr = BgmPlaybackManager.Instance ?? BgmPlaybackManager.EnsureInstance();
mgr.SetOutputMixerGroup(outputMixerGroup);
}
}
if (controller != null && controller.musicPicRoot != null)
{
SetMusicRoot(controller.musicPicRoot, sceneCanvas);
return;
}
RectTransform persistentMusic = null;
var persistentCanvasGo = SceneObjectLookupCache.Find(musicPicCanvasName);
if (!keepMusicPicAcrossScenes && persistentCanvasGo != null)
{
if (Application.isPlaying)
Destroy(persistentCanvasGo);
else
DestroyImmediate(persistentCanvasGo);
persistentCanvasGo = null;
}
if (persistentCanvasGo != null)
{
var candidate = persistentCanvasGo.transform.Find(musicRootName);
if (candidate != null)
persistentMusic = candidate as RectTransform;
}
var candidates = FindAllMusicPic();
RectTransform sceneMusic = null;
var activeScene = SceneManager.GetActiveScene();
for (int i = 0; i < candidates.Count; i++)
{
var c = candidates[i];
if (c == null || c.gameObject.scene != activeScene)
continue;
if (sceneMusic == null)
sceneMusic = c;
if (c.GetComponentInParent<btmandtopController>(true) != null)
{
sceneMusic = c;
break;
}
}
if (persistentMusic != null)
musicRoot = persistentMusic;
else
musicRoot = sceneMusic;
hasExplicitMusicRoot = musicRoot != null;
if (candidates.Count > 1)
{
for (int i = 0; i < candidates.Count; i++)
{
var c = candidates[i];
if (c == null) continue;
if (c == musicRoot) continue;
if (Application.isPlaying)
Destroy(c.gameObject);
else
DestroyImmediate(c.gameObject);
}
}
if (musicRoot == null)
{
var canvas = SceneObjectLookupCache.FindAny<Canvas>();
if (canvas != null)
musicRoot = canvas.GetComponent<RectTransform>();
}
if (hasExplicitMusicRoot)
{
EnsureMusicPicCanvas(sceneCanvas);
EnsureMusicPicCanvasGroup();
if (Application.isPlaying && forceHideOnSceneLoad)
SetMusicPicVisible(false, true);
}
if (progressSlider == null && progressImage == null)
{
if (musicRoot != null)
{
var target = musicRoot.Find(progressName);
if (target != null)
{
progressSlider = target.GetComponent<Slider>();
progressImage = target.GetComponent<Image>();
}
}
}
if (autoCreateProgress && progressSlider == null && progressImage == null && musicRoot != null)
{
var go = new GameObject(progressName, typeof(RectTransform), typeof(Image));
var rt = go.GetComponent<RectTransform>();
rt.SetParent(musicRoot, false);
rt.sizeDelta = defaultProgressSize;
rt.anchoredPosition = defaultProgressPosition;
var img = go.GetComponent<Image>();
img.type = Image.Type.Filled;
img.fillMethod = Image.FillMethod.Horizontal;
img.fillOrigin = 0;
img.fillAmount = 0f;
img.color = progressColor;
progressImage = img;
}
// If a progress bar exists outside MusicPic, ignore it and let cleanup remove it.
if (musicRoot != null)
{
if (progressSlider != null && !progressSlider.transform.IsChildOf(musicRoot))
progressSlider = null;
if (progressImage != null && !progressImage.transform.IsChildOf(musicRoot))
progressImage = null;
}
// Cleanup any BGM_Progress that isn't under MusicPic
CleanupProgressOutsideMusicRoot();
progressRect = null;
if (progressSlider != null) progressRect = progressSlider.GetComponent<RectTransform>();
if (progressRect == null && progressImage != null) progressRect = progressImage.rectTransform;
if (pointerRect == null)
{
Transform pointer = null;
if (musicRoot != null)
pointer = musicRoot.Find(pointerName);
if (pointer == null)
{
var go = FindByNameContains(pointerName);
if (go != null) pointer = go.transform;
}
if (pointer != null)
pointerRect = pointer.GetComponent<RectTransform>();
}
if (pointerRect != null)
{
pointerBaseY = pointerRect.anchoredPosition.y;
pointerRect.anchoredPosition = pointerStartPos;
pointerBaseY = pointerStartPos.y;
}
if (enableMusicPicGyro && musicRoot != null)
{
var gyro = musicRoot.GetComponent<BgmGyroRotator>();
if (gyro == null) gyro = musicRoot.gameObject.AddComponent<BgmGyroRotator>();
gyro.target = musicRoot;
}
BindButtons();
EnsureSpectrum();
CacheTimeTargets();
HideSpectrumInGameplay(SceneManager.GetActiveScene().name);
}
public void SetMusicRoot(RectTransform root, Canvas sceneCanvas = null)
{
musicRoot = root;
hasExplicitMusicRoot = musicRoot != null;
if (!hasExplicitMusicRoot)
return;
EnsureMusicPicCanvas(sceneCanvas);
EnsureMusicPicCanvasGroup();
EnsureSpectrum();
CacheTimeTargets();
}
private void CacheTimeTargets()
{
timeTmps.Clear();
timeTexts.Clear();
if (timeTmp != null) timeTmps.Add(timeTmp);
if (timeText != null) timeTexts.Add(timeText);
if (timeTmps.Count == 0 && timeTexts.Count == 0)
{
Transform timeRoot = null;
if (musicRoot != null)
{
var all = musicRoot.GetComponentsInChildren<Transform>(true);
for (int i = 0; i < all.Length; i++)
{
var t = all[i];
if (t == null) continue;
if (string.Equals(t.name, timeRootName, StringComparison.OrdinalIgnoreCase))
{
timeRoot = t;
break;
}
}
}
if (timeRoot == null)
{
var timeGo = SceneObjectLookupCache.Find(timeRootName);
if (timeGo != null) timeRoot = timeGo.transform;
}
if (timeRoot != null)
{
timeTmps.AddRange(timeRoot.GetComponentsInChildren<TMP_Text>(true));
timeTexts.AddRange(timeRoot.GetComponentsInChildren<Text>(true));
if (!timeRoot.gameObject.activeSelf)
timeRoot.gameObject.SetActive(true);
for (int i = 0; i < timeTmps.Count; i++)
{
if (timeTmps[i] != null && !timeTmps[i].gameObject.activeSelf)
timeTmps[i].gameObject.SetActive(true);
}
for (int i = 0; i < timeTexts.Count; i++)
{
if (timeTexts[i] != null && !timeTexts[i].gameObject.activeSelf)
timeTexts[i].gameObject.SetActive(true);
}
}
}
if (timeTmps.Count == 0 && timeTexts.Count == 0)
{
var allTmps = Object.FindObjectsByType<TMP_Text>(FindObjectsInactive.Include, FindObjectsSortMode.None);
foreach (var t in allTmps)
{
if (t == null) continue;
if (t.name.ToUpper().Contains("TIME") || (t.text != null && t.text.ToUpper().Contains("TIME")))
{
timeTmps.Add(t);
break;
}
}
}
if (timeTmps.Count == 0 && timeTexts.Count == 0)
{
var allTexts = Object.FindObjectsByType<Text>(FindObjectsInactive.Include, FindObjectsSortMode.None);
foreach (var t in allTexts)
{
if (t == null) continue;
if (t.name.ToUpper().Contains("TIME") || (t.text != null && t.text.ToUpper().Contains("TIME")))
{
timeTexts.Add(t);
break;
}
}
}
}
private bool _buttonsBound = false;
private string _cachedMusicRootNameNormalized;
private void Update()
{
if (!Application.isPlaying)
return;
if (_shouldHideSpectrumForCurrentScene && !_spectrumHiddenForCurrentScene)
{
HideSpectrumInGameplay(SceneManager.GetActiveScene().name);
_spectrumHiddenForCurrentScene = true;
}
if (!_buttonsBound)
{
BindButtons();
_buttonsBound = true;
}
if (useManualMusicToggle && musicToggleButton != null && Input.GetMouseButtonDown(0))
{
if (IsPointerOverButton(musicToggleButton))
ToggleMusicPic();
}
// keep spectrum alive if MusicPic gets rebuilt or moved
if (spectrumRoot == null && musicRoot != null)
{
EnsureSpectrum();
}
var mgr = BgmPlaybackManager.Instance;
if (mgr == null || !mgr.IsReady)
return;
float total = mgr.TotalTime;
float current = mgr.CurrentTime;
if (total <= 0.01f) return;
float t = Mathf.Clamp01(current / total);
if (progressSlider != null)
progressSlider.value = t;
if (progressImage != null)
progressImage.fillAmount = t;
UpdatePointer(t);
int curS = Mathf.FloorToInt(current);
int totS = Mathf.FloorToInt(total);
if (curS != _lastSeconds || totS != _lastTotalSeconds)
{
_lastSeconds = curS;
_lastTotalSeconds = totS;
_sb.Clear();
AppendTime(_sb, curS);
_sb.Append(" / ");
AppendTime(_sb, totS);
string timeStr = _sb.ToString();
for (int i = 0; i < timeTmps.Count; i++)
{
if (timeTmps[i] != null) timeTmps[i].text = timeStr;
}
for (int i = 0; i < timeTexts.Count; i++)
{
if (timeTexts[i] != null) timeTexts[i].text = timeStr;
}
}
}
private void AppendTime(System.Text.StringBuilder sb, int totalSeconds)
{
int m = totalSeconds / 60;
int s = totalSeconds % 60;
if (m < 10) sb.Append('0');
sb.Append(m);
sb.Append(':');
if (s < 10) sb.Append('0');
sb.Append(s);
}
private void LateUpdate()
{
if (musicRoot != null && spectrumRoot != null && enableMusicPicGyro)
spectrumRoot.localRotation = musicRoot.localRotation;
}
private void UpdatePointer(float t)
{
if (pointerRect == null || progressRect == null) return;
var parent = pointerRect.parent as RectTransform;
if (parent == null) return;
float x = Mathf.Lerp(pointerStartPos.x, pointerEndPos.x, t);
pointerRect.anchoredPosition = new Vector2(x + pointerOffset.x, pointerStartPos.y + pointerOffset.y);
}
private void CleanupProgressOutsideMusicRoot()
{
var all = Resources.FindObjectsOfTypeAll<RectTransform>();
for (int i = 0; i < all.Length; i++)
{
var rt = all[i];
if (rt == null) continue;
if (!string.Equals(rt.name, progressName, StringComparison.OrdinalIgnoreCase)) continue;
if (musicRoot != null && rt.transform.IsChildOf(musicRoot)) continue;
if (Application.isPlaying)
Destroy(rt.gameObject);
else
DestroyImmediate(rt.gameObject);
}
}
private void BindButtons()
{
if (prevButton == null) prevButton = FindButton(prevButtonName);
if (nextButton == null) nextButton = FindButton(nextButtonName);
if (pauseButton == null) pauseButton = FindButton(pauseButtonName);
if (musicToggleButton == null) musicToggleButton = FindButton(musicToggleButtonName);
useManualMusicToggle = musicToggleButton == null;
var mgr = BgmPlaybackManager.Instance ?? BgmPlaybackManager.EnsureInstance();
if (prevButton != null)
{
prevButton.onClick.RemoveAllListeners();
prevButton.onClick.AddListener(mgr.Previous);
}
if (nextButton != null)
{
nextButton.onClick.RemoveAllListeners();
nextButton.onClick.AddListener(mgr.Next);
}
if (pauseButton != null)
{
pauseButton.onClick.RemoveAllListeners();
pauseButton.onClick.AddListener(mgr.TogglePause);
}
if (musicToggleButton != null)
{
if (bindMusicToggleButton)
{
musicToggleButton.onClick.RemoveAllListeners();
musicToggleButton.onClick.AddListener(ToggleMusicPic);
}
}
}
private Button FindButton(string name)
{
if (string.IsNullOrEmpty(name)) return null;
var go = FindByNameContains(name);
if (go == null) return null;
return go.GetComponent<Button>();
}
private bool IsPointerOverButton(Button button)
{
if (button == null) return false;
RectTransform rect = button.GetComponent<RectTransform>();
if (rect == null)
rect = button.targetGraphic != null ? button.targetGraphic.rectTransform : null;
if (rect == null)
{
var graphics = button.GetComponentsInChildren<Graphic>(true);
rect = GetLargestRect(graphics);
}
if (rect == null) return false;
var canvas = rect.GetComponentInParent<Canvas>();
Camera cam = (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay) ? canvas.worldCamera : null;
return RectTransformUtility.RectangleContainsScreenPoint(rect, Input.mousePosition, cam);
}
private RectTransform GetLargestRect(Graphic[] graphics)
{
if (graphics == null || graphics.Length == 0) return null;
RectTransform best = null;
float bestArea = 0f;
for (int i = 0; i < graphics.Length; i++)
{
var g = graphics[i];
if (g == null) continue;
var rt = g.rectTransform;
float area = Mathf.Abs(rt.rect.width * rt.rect.height);
if (area > bestArea)
{
bestArea = area;
best = rt;
}
}
return best;
}
private GameObject FindByNameContains(string name)
{
if (string.IsNullOrEmpty(name)) return null;
string target = name.Replace(" ", "");
var exact = SceneObjectLookupCache.Find(name);
if (exact != null) return exact;
var all = Object.FindObjectsByType<Transform>(FindObjectsInactive.Include, FindObjectsSortMode.None);
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;
var n = t.name.Replace(" ", "");
if (n.Contains(target))
return t.gameObject;
}
return null;
}
private void EnsureSpectrum()
{
if (musicRoot == null)
{
var foundMusic = FindByNameContains(musicRootName);
if (foundMusic != null)
musicRoot = foundMusic.GetComponent<RectTransform>();
}
if (musicRoot == null)
{
CleanupSpectrumRoots(true);
return;
}
RectTransform existing = musicRoot.Find(spectrumRootName) as RectTransform;
var activeScene = SceneManager.GetActiveScene();
var all = Resources.FindObjectsOfTypeAll<RectTransform>();
List<RectTransform> found = new List<RectTransform>();
for (int i = 0; i < all.Length; i++)
{
var rt = all[i];
if (rt == null) continue;
if (!rt.gameObject.scene.IsValid() || rt.gameObject.scene != activeScene) continue;
if (!string.Equals(rt.name, spectrumRootName, StringComparison.OrdinalIgnoreCase)) continue;
found.Add(rt);
}
RectTransform preferred = null;
for (int i = 0; i < found.Count; i++)
{
var rt = found[i];
if (rt != null && rt.transform.IsChildOf(musicRoot))
{
preferred = rt;
break;
}
}
if (preferred == null && found.Count > 0)
preferred = found[0];
if (preferred != null)
{
if (preferred.parent != musicRoot)
preferred.SetParent(musicRoot, false);
existing = preferred;
}
if (existing == null)
{
var any = FindByNameContains(spectrumRootName);
if (any != null)
existing = any.transform as RectTransform;
}
if (existing == null)
{
var go = new GameObject(spectrumRootName, typeof(RectTransform));
var rt = go.GetComponent<RectTransform>();
rt.SetParent(musicRoot, false);
rt.sizeDelta = new Vector2(420f, 120f);
rt.anchoredPosition = spectrumPosition;
existing = rt;
}
else if (existing.parent != musicRoot)
{
existing.SetParent(musicRoot, false);
}
spectrumRoot = existing as RectTransform;
if (spectrumRoot != null)
{
spectrumRoot.localRotation = Quaternion.identity;
spectrumRoot.localScale = Vector3.one;
spectrumRoot.anchoredPosition = spectrumPosition;
spectrumRoot.gameObject.SetActive(true);
}
var viz = existing.GetComponent<BgmSpectrumVisualizer>();
if (viz == null)
viz = existing.gameObject.AddComponent<BgmSpectrumVisualizer>();
if (viz != null)
viz.enabled = true;
CleanupSpectrumRoots(false);
// If this scene should not show spectrum (e.g. gameplay or main menu), hide after ensuring.
HideSpectrumInGameplay(SceneManager.GetActiveScene().name);
}
private void EnsureMusicPicCanvas()
{
EnsureMusicPicCanvas(null);
}
private void EnsureMusicPicCanvas(Canvas sceneCanvas)
{
if (!keepMusicPicAcrossScenes || musicRoot == null)
return;
var currentCanvas = musicRoot.GetComponentInParent<Canvas>();
var persistent = SceneObjectLookupCache.Find(musicPicCanvasName);
Canvas persistentCanvas = null;
if (persistent == null)
{
var go = new GameObject(musicPicCanvasName, typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
persistentCanvas = go.GetComponent<Canvas>();
persistentCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
var scaler = go.GetComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920f, 1080f);
scaler.matchWidthOrHeight = 0.5f;
DontDestroyOnLoad(go);
persistent = go;
}
else
{
persistentCanvas = persistent.GetComponent<Canvas>();
}
var sourceCanvas = sceneCanvas != null ? sceneCanvas : currentCanvas;
if (sourceCanvas != null && persistentCanvas != null)
{
persistentCanvas.renderMode = sourceCanvas.renderMode;
persistentCanvas.worldCamera = sourceCanvas.worldCamera;
persistentCanvas.planeDistance = sourceCanvas.planeDistance;
persistentCanvas.sortingLayerID = sourceCanvas.sortingLayerID;
persistentCanvas.overrideSorting = true;
persistentCanvas.sortingOrder = sourceCanvas.sortingOrder + 50;
var srcScaler = sourceCanvas.GetComponent<CanvasScaler>();
var dstScaler = persistentCanvas.GetComponent<CanvasScaler>();
if (srcScaler != null && dstScaler != null)
{
dstScaler.uiScaleMode = srcScaler.uiScaleMode;
dstScaler.referenceResolution = srcScaler.referenceResolution;
dstScaler.matchWidthOrHeight = srcScaler.matchWidthOrHeight;
dstScaler.referencePixelsPerUnit = srcScaler.referencePixelsPerUnit;
}
}
if (musicRoot != null && musicRoot.transform.parent != persistent.transform)
musicRoot.SetParent(persistent.transform, false);
}
private Canvas FindSceneCanvas()
{
var canvases = Object.FindObjectsByType<Canvas>(FindObjectsInactive.Include, FindObjectsSortMode.None);
var activeScene = SceneManager.GetActiveScene();
for (int i = 0; i < canvases.Length; i++)
{
var c = canvases[i];
if (c == null) continue;
if (c.gameObject.scene != activeScene) continue;
if (c.name == musicPicCanvasName) continue;
return c;
}
return null;
}
private void EnsureMusicPicCanvasGroup()
{
if (musicRoot == null)
return;
if (musicPicGroup == null)
musicPicGroup = musicRoot.GetComponent<CanvasGroup>();
if (musicPicGroup == null)
musicPicGroup = musicRoot.gameObject.AddComponent<CanvasGroup>();
musicRoot.SetAsLastSibling();
}
private void ApplyDefaultMusicPicVisibility(string sceneName)
{
bool shouldShow = false;
if (lobbyShowRoutine != null)
{
StopCoroutine(lobbyShowRoutine);
lobbyShowRoutine = null;
}
if (musicRoot != null && hasExplicitMusicRoot)
SetMusicPicVisible(shouldShow, true);
if (ShouldHideSpectrumInScene(sceneName))
{
CleanupSpectrumRoots(true);
HideSpectrumInGameplay(sceneName);
}
}
public void ToggleMusicPic()
{
if (musicRoot == null || !hasExplicitMusicRoot)
Bind();
if (musicRoot == null || !hasExplicitMusicRoot)
{
var go = FindByNameContains(musicRootName);
if (go != null)
{
musicRoot = go.GetComponent<RectTransform>();
hasExplicitMusicRoot = musicRoot != null;
}
}
if (musicRoot == null || !hasExplicitMusicRoot) return;
SetMusicPicVisible(!musicPicVisible, false);
}
public RectTransform GetSpectrumRoot()
{
return spectrumRoot;
}
private void SetMusicPicVisible(bool visible, bool instant)
{
if (musicRoot == null) return;
EnsureMusicPicCanvasGroup();
if (musicPicTween != null)
{
StopCoroutine(musicPicTween);
musicPicTween = null;
}
musicPicVisible = visible;
if (instant)
{
if (musicPicGroup != null)
{
musicPicGroup.alpha = visible ? 1f : 0f;
musicPicGroup.interactable = visible;
musicPicGroup.blocksRaycasts = visible;
}
musicRoot.gameObject.SetActive(visible);
if (!visible)
CleanupSpectrumRoots(true);
return;
}
musicRoot.gameObject.SetActive(true);
musicPicTween = StartCoroutine(FadeMusicPic(visible));
}
private System.Collections.IEnumerator FadeMusicPic(bool visibleAtEnd)
{
if (musicPicGroup == null)
yield break;
float start = musicPicGroup.alpha;
float target = visibleAtEnd ? 1f : 0f;
float t = 0f;
while (t < 1f)
{
float dt = useUnscaledTime ? Time.unscaledDeltaTime : Time.deltaTime;
t += dt / Mathf.Max(0.0001f, musicPicFadeDuration);
float eased = Mathf.SmoothStep(0f, 1f, t);
musicPicGroup.alpha = Mathf.LerpUnclamped(start, target, eased);
yield return null;
}
musicPicGroup.alpha = target;
musicPicGroup.interactable = visibleAtEnd;
musicPicGroup.blocksRaycasts = visibleAtEnd;
if (!visibleAtEnd && musicRoot != null)
musicRoot.gameObject.SetActive(false);
if (!visibleAtEnd)
CleanupSpectrumRoots(true);
}
private void CleanupSpectrumRoots(bool disableOnly)
{
var all = Resources.FindObjectsOfTypeAll<RectTransform>();
for (int i = 0; i < all.Length; i++)
{
var rt = all[i];
if (rt == null) continue;
if (!string.Equals(rt.name, spectrumRootName, StringComparison.OrdinalIgnoreCase)) continue;
if (musicRoot != null && rt.transform.IsChildOf(musicRoot)) continue;
if (disableOnly)
{
rt.gameObject.SetActive(false);
}
else
{
if (Application.isPlaying)
Destroy(rt.gameObject);
else
DestroyImmediate(rt.gameObject);
}
}
}
private void HideSpectrumInGameplay(string sceneName)
{
bool shouldHide = ShouldHideSpectrumInScene(sceneName);
if (!shouldHide) return;
var all = Resources.FindObjectsOfTypeAll<RectTransform>();
for (int i = 0; i < all.Length; i++)
{
var rt = all[i];
if (rt == null) continue;
if (!string.Equals(rt.name, spectrumRootName, StringComparison.OrdinalIgnoreCase)) continue;
if (!rt.gameObject.scene.IsValid() || !rt.gameObject.scene.isLoaded) continue;
var viz = rt.GetComponent<BgmSpectrumVisualizer>();
if (viz != null) viz.enabled = false;
rt.gameObject.SetActive(false);
}
if (spectrumRoot != null)
{
var viz = spectrumRoot.GetComponent<BgmSpectrumVisualizer>();
if (viz != null) viz.enabled = false;
spectrumRoot.gameObject.SetActive(false);
}
}
private bool ShouldHideSpectrumInScene(string sceneName)
{
if (string.IsNullOrEmpty(sceneName))
return false;
if (string.Equals(sceneName, gameplaySceneName, StringComparison.OrdinalIgnoreCase))
return true;
if (hideSpectrumInMainScene && string.Equals(sceneName, mainSceneName, StringComparison.OrdinalIgnoreCase))
return true;
if (string.Equals(sceneName, beforeEverythingSceneName, StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
private System.Collections.IEnumerator ShowLobbyMusicPicNextFrame()
{
int tries = 0;
while (tries < 3)
{
yield return null;
if (musicRoot == null || !hasExplicitMusicRoot)
Bind();
if (musicRoot != null && hasExplicitMusicRoot)
break;
tries++;
}
if (musicRoot == null || !hasExplicitMusicRoot)
yield break;
EnsureSpectrum();
EnsureMusicPicCanvasGroup();
if (musicPicGroup != null)
musicPicGroup.alpha = 0f;
SetMusicPicVisible(true, false);
}
private List<RectTransform> FindAllMusicPic()
{
var list = new List<RectTransform>();
var all = Resources.FindObjectsOfTypeAll<RectTransform>();
for (int i = 0; i < all.Length; i++)
{
var rt = all[i];
if (rt == null) continue;
if (!rt.gameObject.scene.IsValid() || !rt.gameObject.scene.isLoaded) continue;
var n = rt.name.Replace(" ", "");
var target = musicRootName.Replace(" ", "");
if (n.IndexOf("Canvas", StringComparison.OrdinalIgnoreCase) >= 0)
continue;
bool match = n.Equals(target, StringComparison.OrdinalIgnoreCase) ||
n.StartsWith(target + "(", StringComparison.OrdinalIgnoreCase) ||
n.StartsWith(target + "_", StringComparison.OrdinalIgnoreCase);
if (match)
list.Add(rt);
}
return list;
}
}