技能主要更新,修复卡顿并加入动画,以及各种其他更新。
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class BgmGyroRotator : MonoBehaviour
|
||||
{
|
||||
public RectTransform target;
|
||||
public float maxRotation = 3f;
|
||||
public float rotX = 1f;
|
||||
public float rotY = 1f;
|
||||
public float rotZ = 0.35f;
|
||||
public float smooth = 8f;
|
||||
public bool useUnscaled = true;
|
||||
|
||||
private Quaternion baseRotation;
|
||||
private bool baseCaptured = false;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (target == null) target = transform as RectTransform;
|
||||
CaptureBase();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (target == null) target = transform as RectTransform;
|
||||
CaptureBase();
|
||||
}
|
||||
|
||||
private void CaptureBase()
|
||||
{
|
||||
if (target == null) return;
|
||||
baseRotation = target.localRotation;
|
||||
baseCaptured = true;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (target == null) return;
|
||||
if (!baseCaptured) CaptureBase();
|
||||
|
||||
Vector2 norm = GetMouseNormalized();
|
||||
Vector3 delta = new Vector3(-norm.y * maxRotation * rotX,
|
||||
norm.x * maxRotation * rotY,
|
||||
norm.x * maxRotation * rotZ);
|
||||
Quaternion targetRot = baseRotation * Quaternion.Euler(delta);
|
||||
float dt = useUnscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
float t = 1f - Mathf.Exp(-Mathf.Max(0.01f, smooth) * dt);
|
||||
target.localRotation = Quaternion.Slerp(target.localRotation, targetRot, t);
|
||||
}
|
||||
|
||||
private static Vector2 GetMouseNormalized()
|
||||
{
|
||||
Vector2 pos = Input.mousePosition;
|
||||
if (Screen.width <= 1 || Screen.height <= 1)
|
||||
return Vector2.zero;
|
||||
float x = (pos.x / Screen.width) * 2f - 1f;
|
||||
float y = (pos.y / Screen.height) * 2f - 1f;
|
||||
return new Vector2(Mathf.Clamp(x, -1f, 1f), Mathf.Clamp(y, -1f, 1f));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17e6aaba9c8c0c84cbbeaedff83ef6f2
|
||||
@@ -0,0 +1,430 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.IO;
|
||||
|
||||
public class BgmParticleEmitter : MonoBehaviour
|
||||
{
|
||||
[Header("Audio")]
|
||||
public AudioSource source;
|
||||
public FFTWindow fftWindow = FFTWindow.BlackmanHarris;
|
||||
|
||||
[Header("Rendering")]
|
||||
public Material particleMaterial;
|
||||
public Texture2D particleTexture;
|
||||
public string particleTextureRelativePath = "artworks/UI_UI/新版主界面 (2)/矩形 2-1.png";
|
||||
public string fallbackTextureRelativePath = "artworks/UI_UI/新版主界面 (2)/矩形 2-1.png";
|
||||
public bool useTextureAlphaOnly = true;
|
||||
|
||||
[Header("Emission")]
|
||||
public int spectrumSize = 256;
|
||||
public float minRate = 2f;
|
||||
public float maxRate = 30f;
|
||||
public float minSpeed = 25f;
|
||||
public float maxSpeed = 80f;
|
||||
public float minSize = 4f;
|
||||
public float maxSize = 8f;
|
||||
public float energyScale = 50f;
|
||||
public float startLifetime = 34.2f;
|
||||
|
||||
[Header("Rotation Drift")]
|
||||
public Vector2 startRotationRange = new Vector2(0f, 360f);
|
||||
public Vector2 angularVelocityRange = new Vector2(3f, 12f);
|
||||
|
||||
[Header("Spawn Area")]
|
||||
public float spawnMargin = 40f;
|
||||
public float spawnHeight = 10f;
|
||||
|
||||
private ParticleSystem mainPs;
|
||||
private float[] spectrum;
|
||||
private RectTransform canvasRect;
|
||||
private static Material defaultParticleMat;
|
||||
private static Texture2D circleTex;
|
||||
private Material runtimeMat;
|
||||
private Vector2 lastCanvasSize;
|
||||
private bool hasLastCanvasSize;
|
||||
private float lastSpawnMargin;
|
||||
private float lastSpawnHeight;
|
||||
private bool spawnAreaInitialized;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
BuildIfNeeded();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
BuildIfNeeded();
|
||||
}
|
||||
|
||||
private void BuildIfNeeded()
|
||||
{
|
||||
if (mainPs == null)
|
||||
mainPs = GetComponent<ParticleSystem>();
|
||||
|
||||
if (mainPs == null)
|
||||
{
|
||||
mainPs = gameObject.AddComponent<ParticleSystem>();
|
||||
}
|
||||
|
||||
if (particleTexture == null)
|
||||
particleTexture = LoadTextureFromFile();
|
||||
#if UNITY_EDITOR
|
||||
if (particleTexture == null)
|
||||
particleTexture = LoadTextureFromAsset();
|
||||
#endif
|
||||
if (particleTexture != null && useTextureAlphaOnly)
|
||||
particleTexture = ConvertTextureToWhiteAlpha(particleTexture);
|
||||
if (particleTexture == null)
|
||||
particleTexture = GetCircleTexture();
|
||||
|
||||
ConfigureSystem(mainPs, Color.white, 10);
|
||||
|
||||
var shadow = transform.Find("Shadow");
|
||||
if (shadow != null)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
Destroy(shadow.gameObject);
|
||||
else
|
||||
DestroyImmediate(shadow.gameObject);
|
||||
}
|
||||
|
||||
if (canvasRect == null)
|
||||
{
|
||||
var canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
canvasRect = canvas.GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
if (spectrum == null || spectrum.Length != spectrumSize)
|
||||
spectrum = new float[spectrumSize];
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateSpawnArea();
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
var mgr = BgmPlaybackManager.Instance;
|
||||
if (mgr != null) source = mgr.audioSource;
|
||||
}
|
||||
|
||||
if (source == null || source.clip == null)
|
||||
return;
|
||||
|
||||
if (spectrum == null || spectrum.Length != spectrumSize)
|
||||
spectrum = new float[spectrumSize];
|
||||
|
||||
source.GetSpectrumData(spectrum, 0, fftWindow);
|
||||
|
||||
float energy = 0f;
|
||||
int start = 2;
|
||||
int end = Mathf.Min(64, spectrumSize - 1);
|
||||
for (int i = start; i <= end; i++)
|
||||
energy += spectrum[i];
|
||||
energy /= Mathf.Max(1, end - start + 1);
|
||||
|
||||
float level = Mathf.Clamp01(energy * Mathf.Max(1f, energyScale));
|
||||
float rate = Mathf.Lerp(minRate, maxRate, level);
|
||||
float speed = Mathf.Lerp(minSpeed, maxSpeed, level);
|
||||
float size = Mathf.Lerp(minSize, maxSize, level);
|
||||
|
||||
ApplyRuntimeParams(mainPs, rate, speed, size);
|
||||
}
|
||||
|
||||
private void ConfigureSystem(ParticleSystem ps, Color color, int order)
|
||||
{
|
||||
var main = ps.main;
|
||||
main.loop = true;
|
||||
main.playOnAwake = true;
|
||||
main.startLifetime = startLifetime;
|
||||
main.startSpeed = minSpeed;
|
||||
main.startSize = minSize;
|
||||
main.startColor = color;
|
||||
main.simulationSpace = ParticleSystemSimulationSpace.Local;
|
||||
main.scalingMode = ParticleSystemScalingMode.Hierarchy;
|
||||
main.maxParticles = 2000;
|
||||
main.startRotation3D = true;
|
||||
main.startRotationX = new ParticleSystem.MinMaxCurve(startRotationRange.x * Mathf.Deg2Rad, startRotationRange.y * Mathf.Deg2Rad);
|
||||
main.startRotationY = new ParticleSystem.MinMaxCurve(startRotationRange.x * Mathf.Deg2Rad, startRotationRange.y * Mathf.Deg2Rad);
|
||||
main.startRotationZ = new ParticleSystem.MinMaxCurve(startRotationRange.x * Mathf.Deg2Rad, startRotationRange.y * Mathf.Deg2Rad);
|
||||
|
||||
var emission = ps.emission;
|
||||
emission.rateOverTime = minRate;
|
||||
|
||||
var shape = ps.shape;
|
||||
shape.enabled = true;
|
||||
shape.shapeType = ParticleSystemShapeType.Box;
|
||||
shape.scale = new Vector3(200f, spawnHeight, 0f);
|
||||
|
||||
var vel = ps.velocityOverLifetime;
|
||||
vel.enabled = true;
|
||||
vel.space = ParticleSystemSimulationSpace.Local;
|
||||
vel.x = new ParticleSystem.MinMaxCurve(-5f, 5f);
|
||||
vel.y = new ParticleSystem.MinMaxCurve(-minSpeed, -minSpeed * 1.2f);
|
||||
vel.z = new ParticleSystem.MinMaxCurve(0f, 0f);
|
||||
|
||||
var renderer = ps.GetComponent<ParticleSystemRenderer>();
|
||||
renderer.sortingOrder = order;
|
||||
renderer.renderMode = ParticleSystemRenderMode.Billboard;
|
||||
var mat = GetParticleMaterial();
|
||||
renderer.material = mat;
|
||||
renderer.sharedMaterial = mat;
|
||||
|
||||
var rot = ps.rotationOverLifetime;
|
||||
rot.enabled = true;
|
||||
rot.separateAxes = true;
|
||||
rot.x = new ParticleSystem.MinMaxCurve(angularVelocityRange.x * Mathf.Deg2Rad, angularVelocityRange.y * Mathf.Deg2Rad);
|
||||
rot.y = new ParticleSystem.MinMaxCurve(angularVelocityRange.x * Mathf.Deg2Rad, angularVelocityRange.y * Mathf.Deg2Rad);
|
||||
rot.z = new ParticleSystem.MinMaxCurve(angularVelocityRange.x * Mathf.Deg2Rad, angularVelocityRange.y * Mathf.Deg2Rad);
|
||||
}
|
||||
|
||||
private void ApplyRuntimeParams(ParticleSystem ps, float rate, float speed, float size)
|
||||
{
|
||||
if (ps == null) return;
|
||||
var main = ps.main;
|
||||
main.startSpeed = speed;
|
||||
main.startSize = size;
|
||||
main.startLifetime = startLifetime;
|
||||
var emission = ps.emission;
|
||||
emission.rateOverTime = rate;
|
||||
var vel = ps.velocityOverLifetime;
|
||||
vel.x = new ParticleSystem.MinMaxCurve(-5f, 5f);
|
||||
vel.y = new ParticleSystem.MinMaxCurve(-speed, -speed * 1.2f);
|
||||
vel.z = new ParticleSystem.MinMaxCurve(0f, 0f);
|
||||
}
|
||||
|
||||
private void UpdateSpawnArea()
|
||||
{
|
||||
var canvas = GetComponentInParent<Canvas>();
|
||||
if (canvas != null)
|
||||
{
|
||||
var rt = canvas.GetComponent<RectTransform>();
|
||||
if (canvasRect == null || canvasRect != rt)
|
||||
canvasRect = rt;
|
||||
}
|
||||
|
||||
Vector2 size = canvasRect != null ? canvasRect.rect.size : new Vector2(Screen.width, Screen.height);
|
||||
bool sizeChanged = !hasLastCanvasSize || size != lastCanvasSize;
|
||||
bool marginChanged = !spawnAreaInitialized || !Mathf.Approximately(lastSpawnMargin, spawnMargin);
|
||||
bool heightChanged = !spawnAreaInitialized || !Mathf.Approximately(lastSpawnHeight, spawnHeight);
|
||||
|
||||
if (!sizeChanged && !marginChanged && !heightChanged)
|
||||
return;
|
||||
|
||||
hasLastCanvasSize = true;
|
||||
lastCanvasSize = size;
|
||||
lastSpawnMargin = spawnMargin;
|
||||
lastSpawnHeight = spawnHeight;
|
||||
spawnAreaInitialized = true;
|
||||
|
||||
var shape = mainPs.shape;
|
||||
shape.scale = new Vector3(size.x, spawnHeight, 0f);
|
||||
|
||||
var rect = transform as RectTransform;
|
||||
if (rect != null)
|
||||
{
|
||||
if (rect.anchorMin != new Vector2(0.5f, 1f)) rect.anchorMin = new Vector2(0.5f, 1f);
|
||||
if (rect.anchorMax != new Vector2(0.5f, 1f)) rect.anchorMax = new Vector2(0.5f, 1f);
|
||||
if (rect.pivot != new Vector2(0.5f, 0.5f)) rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
if (rect.anchoredPosition != new Vector2(0f, spawnMargin)) rect.anchoredPosition = new Vector2(0f, spawnMargin);
|
||||
if (rect.localScale != Vector3.one) rect.localScale = Vector3.one;
|
||||
}
|
||||
else
|
||||
{
|
||||
var targetPos = new Vector3(0f, size.y * 0.5f + spawnMargin, 0f);
|
||||
if (transform.localPosition != targetPos) transform.localPosition = targetPos;
|
||||
if (transform.localScale != Vector3.one) transform.localScale = Vector3.one;
|
||||
}
|
||||
}
|
||||
|
||||
public void ForceLayoutUpdate()
|
||||
{
|
||||
UpdateSpawnArea();
|
||||
}
|
||||
|
||||
private static Material GetDefaultParticleMaterial()
|
||||
{
|
||||
if (defaultParticleMat == null)
|
||||
{
|
||||
defaultParticleMat = Resources.GetBuiltinResource<Material>("Default-Particle.mat");
|
||||
if (defaultParticleMat == null || defaultParticleMat.shader == null || !defaultParticleMat.shader.isSupported)
|
||||
{
|
||||
Shader shader = Shader.Find("Particles/Standard Unlit");
|
||||
if (shader == null) shader = Shader.Find("Universal Render Pipeline/Particles/Unlit");
|
||||
if (shader == null) shader = Shader.Find("Particles/Standard");
|
||||
if (shader == null) shader = Shader.Find("Unlit/Texture");
|
||||
if (shader != null)
|
||||
defaultParticleMat = new Material(shader);
|
||||
}
|
||||
|
||||
if (defaultParticleMat != null && defaultParticleMat.mainTexture == null)
|
||||
defaultParticleMat.mainTexture = GetCircleTexture();
|
||||
}
|
||||
return defaultParticleMat;
|
||||
}
|
||||
|
||||
private Material GetParticleMaterial()
|
||||
{
|
||||
Material baseMat = GetSafeParticleMaterial();
|
||||
if (baseMat == null)
|
||||
baseMat = CreateFallbackMaterial();
|
||||
if (baseMat == null)
|
||||
baseMat = GetDefaultParticleMaterial();
|
||||
if (baseMat == null)
|
||||
baseMat = CreateFallbackMaterial();
|
||||
if (baseMat != null && baseMat.mainTexture == null && particleTexture == null)
|
||||
baseMat = CreateFallbackMaterial();
|
||||
if (runtimeMat == null || runtimeMat.shader != baseMat.shader)
|
||||
runtimeMat = new Material(baseMat);
|
||||
if (particleTexture != null)
|
||||
runtimeMat.mainTexture = particleTexture;
|
||||
else if (runtimeMat.mainTexture == null)
|
||||
runtimeMat.mainTexture = GetCircleTexture();
|
||||
|
||||
if (runtimeMat.HasProperty("_Color"))
|
||||
runtimeMat.SetColor("_Color", Color.white);
|
||||
if (runtimeMat.HasProperty("_TintColor"))
|
||||
runtimeMat.SetColor("_TintColor", Color.white);
|
||||
if (runtimeMat.HasProperty("_BaseColor"))
|
||||
runtimeMat.SetColor("_BaseColor", Color.white);
|
||||
if (runtimeMat.HasProperty("_MainTex"))
|
||||
runtimeMat.SetTexture("_MainTex", runtimeMat.mainTexture);
|
||||
if (runtimeMat.HasProperty("_BaseMap"))
|
||||
runtimeMat.SetTexture("_BaseMap", runtimeMat.mainTexture);
|
||||
return runtimeMat;
|
||||
}
|
||||
|
||||
private Material CreateFallbackMaterial()
|
||||
{
|
||||
Shader shader = Shader.Find("Universal Render Pipeline/Particles/Unlit");
|
||||
if (shader == null) shader = Shader.Find("Particles/Standard Unlit");
|
||||
if (shader == null) shader = Shader.Find("Particles/Standard");
|
||||
if (shader == null) shader = Shader.Find("Unlit/Transparent");
|
||||
if (shader == null) shader = Shader.Find("Unlit/Texture");
|
||||
if (shader == null) return null;
|
||||
return new Material(shader);
|
||||
}
|
||||
|
||||
private Material GetSafeParticleMaterial()
|
||||
{
|
||||
if (particleMaterial == null) return null;
|
||||
Shader shader = particleMaterial.shader;
|
||||
if (shader == null) return null;
|
||||
string name = shader.name;
|
||||
if (string.IsNullOrEmpty(name)) return null;
|
||||
string lower = name.ToLowerInvariant();
|
||||
if (lower.Contains("particle"))
|
||||
return particleMaterial;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Texture2D GetCircleTexture()
|
||||
{
|
||||
if (circleTex != null)
|
||||
return circleTex;
|
||||
|
||||
const int size = 32;
|
||||
circleTex = new Texture2D(size, size, TextureFormat.ARGB32, false);
|
||||
circleTex.wrapMode = TextureWrapMode.Clamp;
|
||||
circleTex.filterMode = FilterMode.Bilinear;
|
||||
|
||||
Vector2 center = new Vector2((size - 1) * 0.5f, (size - 1) * 0.5f);
|
||||
float radius = size * 0.5f * 0.9f;
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float dist = Vector2.Distance(new Vector2(x, y), center);
|
||||
float t = Mathf.InverseLerp(radius * 0.9f, radius, dist);
|
||||
float alpha = Mathf.Clamp01(1f - t);
|
||||
circleTex.SetPixel(x, y, new Color(1f, 1f, 1f, alpha));
|
||||
}
|
||||
}
|
||||
circleTex.Apply();
|
||||
return circleTex;
|
||||
}
|
||||
|
||||
private Texture2D LoadTextureFromFile()
|
||||
{
|
||||
string relPath = particleTextureRelativePath;
|
||||
if (string.IsNullOrEmpty(relPath))
|
||||
relPath = fallbackTextureRelativePath;
|
||||
if (string.IsNullOrEmpty(relPath))
|
||||
return null;
|
||||
|
||||
string path = Path.Combine(Application.dataPath, relPath);
|
||||
if (!File.Exists(path) && !string.IsNullOrEmpty(fallbackTextureRelativePath))
|
||||
{
|
||||
string fallback = Path.Combine(Application.dataPath, fallbackTextureRelativePath);
|
||||
if (File.Exists(fallback))
|
||||
{
|
||||
particleTextureRelativePath = fallbackTextureRelativePath;
|
||||
path = fallback;
|
||||
}
|
||||
}
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
|
||||
var data = File.ReadAllBytes(path);
|
||||
var tex = new Texture2D(2, 2, TextureFormat.ARGB32, false);
|
||||
if (tex.LoadImage(data))
|
||||
return tex;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Texture2D ConvertTextureToWhiteAlpha(Texture2D src)
|
||||
{
|
||||
if (src == null) return null;
|
||||
try
|
||||
{
|
||||
var pixels = src.GetPixels32();
|
||||
if (pixels == null || pixels.Length == 0) return src;
|
||||
var tex = new Texture2D(src.width, src.height, TextureFormat.ARGB32, false);
|
||||
for (int i = 0; i < pixels.Length; i++)
|
||||
{
|
||||
byte a = pixels[i].a;
|
||||
pixels[i] = new Color32(255, 255, 255, a);
|
||||
}
|
||||
tex.SetPixels32(pixels);
|
||||
tex.Apply();
|
||||
tex.wrapMode = TextureWrapMode.Clamp;
|
||||
tex.filterMode = FilterMode.Bilinear;
|
||||
return tex;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return src;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private Texture2D LoadTextureFromAsset()
|
||||
{
|
||||
string relPath = particleTextureRelativePath;
|
||||
if (string.IsNullOrEmpty(relPath))
|
||||
relPath = fallbackTextureRelativePath;
|
||||
if (string.IsNullOrEmpty(relPath))
|
||||
return null;
|
||||
string assetPath = "Assets/" + relPath.TrimStart('/', '\\');
|
||||
return UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>(assetPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
if (particleTexture == null)
|
||||
{
|
||||
const string path = "Assets/artworks/UI_UI/新版主界面 (2)/矩形 2-1.png";
|
||||
particleTexture = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
||||
}
|
||||
|
||||
if (runtimeMat != null && particleTexture != null)
|
||||
runtimeMat.mainTexture = particleTexture;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4276f055c7596a4b8edd5b90b320148
|
||||
@@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public class BgmPlaybackManager : MonoBehaviour
|
||||
{
|
||||
public static BgmPlaybackManager Instance { get; private set; }
|
||||
|
||||
[Header("Audio")]
|
||||
public AudioSource audioSource;
|
||||
public AudioClip clip;
|
||||
[Tooltip("Optional playlist. If empty, will load all clips in Resources/BGM.")]
|
||||
public List<AudioClip> playlist = new List<AudioClip>();
|
||||
public int startIndex = 0;
|
||||
[Tooltip("Resources path without extension. Example: BGM/Bansonic OST")]
|
||||
public string resourcesPath = "BGM/Bansonic OST";
|
||||
[Tooltip("Optional absolute file path to mp3/wav (fallback).")]
|
||||
public string fallbackFilePath = "";
|
||||
public bool loop = true;
|
||||
public bool autoPlay = true;
|
||||
public string gameplaySceneName = "gamePlay_gamePlay";
|
||||
public bool stopOnGameplayScene = true;
|
||||
|
||||
private bool isLoading = false;
|
||||
private int currentIndex = 0;
|
||||
|
||||
public static BgmPlaybackManager EnsureInstance()
|
||||
{
|
||||
if (Instance != null) return Instance;
|
||||
var go = new GameObject("BGM_PlaybackManager");
|
||||
Instance = go.AddComponent<BgmPlaybackManager>();
|
||||
return Instance;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
if (audioSource == null)
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
audioSource.playOnAwake = false;
|
||||
audioSource.loop = loop;
|
||||
|
||||
if (clip == null)
|
||||
clip = audioSource.clip;
|
||||
|
||||
EnsurePlaylist();
|
||||
if (playlist.Count > 0)
|
||||
{
|
||||
currentIndex = Mathf.Clamp(startIndex, 0, playlist.Count - 1);
|
||||
clip = playlist[currentIndex];
|
||||
}
|
||||
if (clip != null)
|
||||
audioSource.clip = clip;
|
||||
|
||||
if (autoPlay)
|
||||
EnsurePlaying();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
if (!stopOnGameplayScene)
|
||||
return;
|
||||
|
||||
if (scene.name == gameplaySceneName)
|
||||
StopLobbyAudioAndParticles();
|
||||
}
|
||||
|
||||
public void EnsurePlaying()
|
||||
{
|
||||
if (audioSource == null)
|
||||
audioSource = gameObject.AddComponent<AudioSource>();
|
||||
|
||||
audioSource.loop = loop;
|
||||
|
||||
if (audioSource.clip == null && clip != null)
|
||||
audioSource.clip = clip;
|
||||
|
||||
if (audioSource.clip == null)
|
||||
{
|
||||
EnsurePlaylist();
|
||||
if (playlist.Count > 0)
|
||||
{
|
||||
currentIndex = Mathf.Clamp(currentIndex, 0, playlist.Count - 1);
|
||||
audioSource.clip = playlist[currentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
if (audioSource.clip == null && !isLoading)
|
||||
{
|
||||
StartCoroutine(LoadClipRoutine());
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioSource.clip != null && !audioSource.isPlaying)
|
||||
audioSource.Play();
|
||||
}
|
||||
|
||||
private IEnumerator LoadClipRoutine()
|
||||
{
|
||||
isLoading = true;
|
||||
|
||||
// Try Resources first
|
||||
if (audioSource.clip == null && !string.IsNullOrEmpty(resourcesPath))
|
||||
{
|
||||
var res = Resources.Load<AudioClip>(resourcesPath);
|
||||
if (res != null)
|
||||
{
|
||||
audioSource.clip = res;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to file path if still missing
|
||||
if (audioSource.clip == null)
|
||||
{
|
||||
string path = fallbackFilePath;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
// Default to project root /??/Bansonic OST.mp3
|
||||
var projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
||||
path = Path.Combine(projectRoot, "??", "Bansonic OST.mp3");
|
||||
}
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
string uri = new System.Uri(path).AbsoluteUri;
|
||||
using (var req = UnityWebRequestMultimedia.GetAudioClip(uri, AudioType.MPEG))
|
||||
{
|
||||
yield return req.SendWebRequest();
|
||||
if (req.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
audioSource.clip = DownloadHandlerAudioClip.GetContent(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
|
||||
if (autoPlay && audioSource.clip != null && !audioSource.isPlaying)
|
||||
audioSource.Play();
|
||||
}
|
||||
|
||||
private void EnsurePlaylist()
|
||||
{
|
||||
if (playlist == null)
|
||||
playlist = new List<AudioClip>();
|
||||
|
||||
if (playlist.Count == 0)
|
||||
{
|
||||
var clips = Resources.LoadAll<AudioClip>("BGM");
|
||||
if (clips != null && clips.Length > 0)
|
||||
{
|
||||
Array.Sort(clips, (a, b) => string.CompareOrdinal(a.name, b.name));
|
||||
playlist.AddRange(clips);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayIndex(int index)
|
||||
{
|
||||
EnsurePlaylist();
|
||||
if (playlist == null || playlist.Count == 0)
|
||||
return;
|
||||
|
||||
currentIndex = (index % playlist.Count + playlist.Count) % playlist.Count;
|
||||
audioSource.clip = playlist[currentIndex];
|
||||
clip = audioSource.clip;
|
||||
audioSource.loop = loop;
|
||||
audioSource.time = 0f;
|
||||
audioSource.Play();
|
||||
}
|
||||
|
||||
public void Next()
|
||||
{
|
||||
EnsurePlaylist();
|
||||
if (playlist == null || playlist.Count == 0)
|
||||
return;
|
||||
PlayIndex(currentIndex + 1);
|
||||
}
|
||||
|
||||
public void Previous()
|
||||
{
|
||||
EnsurePlaylist();
|
||||
if (playlist == null || playlist.Count == 0)
|
||||
return;
|
||||
PlayIndex(currentIndex - 1);
|
||||
}
|
||||
|
||||
public void TogglePause()
|
||||
{
|
||||
if (audioSource == null || audioSource.clip == null)
|
||||
return;
|
||||
if (audioSource.isPlaying)
|
||||
audioSource.Pause();
|
||||
else
|
||||
audioSource.Play();
|
||||
}
|
||||
|
||||
private void StopLobbyAudioAndParticles()
|
||||
{
|
||||
if (audioSource != null)
|
||||
audioSource.Stop();
|
||||
|
||||
}
|
||||
|
||||
public float CurrentTime => audioSource != null ? audioSource.time : 0f;
|
||||
public float TotalTime => (audioSource != null && audioSource.clip != null) ? audioSource.clip.length : 0f;
|
||||
public bool IsReady => audioSource != null && audioSource.clip != null;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 305b5ad12645e8545a89e36bc600b124
|
||||
@@ -0,0 +1,51 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public static class BgmRuntimeBootstrap
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
{
|
||||
var mgr = BgmPlaybackManager.EnsureInstance();
|
||||
mgr.EnsurePlaying();
|
||||
|
||||
// Ensure UI binder exists
|
||||
if (Object.FindAnyObjectByType<BgmUiBinder>() == null)
|
||||
{
|
||||
var go = new GameObject("BGM_UI_Binder");
|
||||
go.AddComponent<BgmUiBinder>();
|
||||
Object.DontDestroyOnLoad(go);
|
||||
}
|
||||
|
||||
RemoveBgmParticlesInLoadedScenes();
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
RemoveBgmParticlesInLoadedScenes();
|
||||
}
|
||||
|
||||
private static void RemoveBgmParticlesInLoadedScenes()
|
||||
{
|
||||
var emitters = Object.FindObjectsByType<BgmParticleEmitter>(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<Transform>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a470f0e76d5042a43b2919b3c552a5e9
|
||||
@@ -0,0 +1,245 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
[ExecuteAlways]
|
||||
public class BgmSpectrumVisualizer : MonoBehaviour
|
||||
{
|
||||
[Header("Audio")]
|
||||
public AudioSource source;
|
||||
public FFTWindow fftWindow = FFTWindow.BlackmanHarris;
|
||||
|
||||
[Header("Bars")]
|
||||
public RectTransform barPrefab;
|
||||
public int bars = 64;
|
||||
public float barWidth = 4.8f;
|
||||
public float barSpacing = 2f;
|
||||
public float maxHeight = 100f;
|
||||
|
||||
[Header("Spectrum")]
|
||||
public int spectrumSize = 512;
|
||||
public float amplitude = 60f;
|
||||
public float smooth = 12f;
|
||||
public float noiseFloor = 0.002f;
|
||||
|
||||
[Header("Wave")]
|
||||
public float waveAmplitude = 6f;
|
||||
public float waveSpeed = 1.6f;
|
||||
public float waveFrequency = 0.45f;
|
||||
[Tooltip("If true, bars expand upward from baseline only.")]
|
||||
public bool growUpOnly = true;
|
||||
|
||||
[Header("Edit Preview")]
|
||||
public bool previewInEditMode = true;
|
||||
public float previewAmplitude = 0.6f;
|
||||
public float previewNoiseSpeed = 0.25f;
|
||||
|
||||
private float[] spectrum;
|
||||
private RectTransform[] barRects;
|
||||
private float[] barHeights;
|
||||
private Vector2[] barBasePos;
|
||||
private bool rebuildQueued;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
else
|
||||
{
|
||||
ScheduleRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (barRects == null || barRects.Length == 0)
|
||||
{
|
||||
if (Application.isPlaying) Init();
|
||||
else ScheduleRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (bars < 1) bars = 1;
|
||||
if (barWidth < 1f) barWidth = 1f;
|
||||
if (barSpacing < 0f) barSpacing = 0f;
|
||||
if (maxHeight < 1f) maxHeight = 1f;
|
||||
|
||||
ScheduleRebuild();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
RebuildBars();
|
||||
}
|
||||
|
||||
private void ScheduleRebuild()
|
||||
{
|
||||
if (rebuildQueued) return;
|
||||
rebuildQueued = true;
|
||||
#if UNITY_EDITOR
|
||||
EditorApplication.delayCall -= RebuildIfQueued;
|
||||
EditorApplication.delayCall += RebuildIfQueued;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void RebuildIfQueued()
|
||||
{
|
||||
if (!rebuildQueued) return;
|
||||
rebuildQueued = false;
|
||||
if (this == null) return;
|
||||
|
||||
RebuildBars();
|
||||
if (!Application.isPlaying && previewInEditMode)
|
||||
UpdateBarsPreview();
|
||||
}
|
||||
#endif
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
if (previewInEditMode)
|
||||
UpdateBarsPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
if (source == null)
|
||||
{
|
||||
var mgr = BgmPlaybackManager.Instance;
|
||||
if (mgr != null) source = mgr.audioSource;
|
||||
}
|
||||
|
||||
if (source == null || source.clip == null)
|
||||
return;
|
||||
|
||||
if (spectrum == null || spectrum.Length != spectrumSize)
|
||||
spectrum = new float[spectrumSize];
|
||||
|
||||
source.GetSpectrumData(spectrum, 0, fftWindow);
|
||||
|
||||
for (int i = 0; i < bars; i++)
|
||||
{
|
||||
float t = (float)i / (bars - 1);
|
||||
int idx = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(1, spectrumSize - 1, t * t)), 1, spectrumSize - 1);
|
||||
|
||||
float v = Mathf.Max(0f, spectrum[idx] - noiseFloor) * amplitude;
|
||||
float baseHeight = Mathf.Clamp01(v) * maxHeight;
|
||||
|
||||
float wave = Mathf.Sin(Time.time * waveSpeed + i * waveFrequency) * waveAmplitude;
|
||||
float target = Mathf.Clamp(baseHeight + wave, 0f, maxHeight);
|
||||
|
||||
barHeights[i] = Mathf.Lerp(barHeights[i], target, Time.deltaTime * smooth);
|
||||
|
||||
var rt = barRects[i];
|
||||
float h = Mathf.Max(1f, barHeights[i]);
|
||||
var sd = rt.sizeDelta;
|
||||
if (sd.x != barWidth || sd.y != h)
|
||||
rt.sizeDelta = new Vector2(barWidth, h);
|
||||
if (growUpOnly && barBasePos != null && i < barBasePos.Length)
|
||||
{
|
||||
var ap = rt.anchoredPosition;
|
||||
float nx = barBasePos[i].x;
|
||||
float ny = barBasePos[i].y + h * 0.5f;
|
||||
if (ap.x != nx || ap.y != ny)
|
||||
rt.anchoredPosition = new Vector2(nx, ny);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildBars()
|
||||
{
|
||||
if (barPrefab == null)
|
||||
{
|
||||
var go = new GameObject("Bar", typeof(RectTransform), typeof(Image), typeof(Outline));
|
||||
var img = go.GetComponent<Image>();
|
||||
img.color = Color.white;
|
||||
var outline = go.GetComponent<Outline>();
|
||||
outline.effectColor = new Color(0f, 0f, 0f, 1f);
|
||||
outline.effectDistance = new Vector2(1f, 1f);
|
||||
outline.useGraphicAlpha = true;
|
||||
barPrefab = go.GetComponent<RectTransform>();
|
||||
barPrefab.sizeDelta = new Vector2(barWidth, 10f);
|
||||
go.SetActive(false);
|
||||
}
|
||||
|
||||
// destroy existing bars
|
||||
for (int i = transform.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
var child = transform.GetChild(i);
|
||||
if (Application.isPlaying)
|
||||
Destroy(child.gameObject);
|
||||
else
|
||||
DestroyImmediate(child.gameObject);
|
||||
}
|
||||
|
||||
spectrum = new float[Mathf.Max(64, spectrumSize)];
|
||||
barRects = new RectTransform[bars];
|
||||
barHeights = new float[bars];
|
||||
barBasePos = new Vector2[bars];
|
||||
|
||||
for (int i = 0; i < bars; i++)
|
||||
{
|
||||
var bar = Instantiate(barPrefab, transform);
|
||||
bar.gameObject.SetActive(true);
|
||||
barRects[i] = bar;
|
||||
var outline = bar.GetComponent<Outline>();
|
||||
if (outline == null)
|
||||
{
|
||||
outline = bar.gameObject.AddComponent<Outline>();
|
||||
outline.effectColor = new Color(0f, 0f, 0f, 1f);
|
||||
outline.effectDistance = new Vector2(1f, 1f);
|
||||
outline.useGraphicAlpha = true;
|
||||
}
|
||||
barRects[i].sizeDelta = new Vector2(barWidth, 1f);
|
||||
var basePos = new Vector2(i * (barWidth + barSpacing), 0f);
|
||||
barRects[i].anchoredPosition = basePos;
|
||||
barBasePos[i] = basePos;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBarsPreview()
|
||||
{
|
||||
if (barRects == null || barRects.Length == 0)
|
||||
return;
|
||||
|
||||
float time = GetEditorTime();
|
||||
for (int i = 0; i < bars; i++)
|
||||
{
|
||||
float t = (float)i / (bars - 1);
|
||||
float noise = Mathf.PerlinNoise(t * 2.5f, time * previewNoiseSpeed);
|
||||
float baseHeight = Mathf.Lerp(0.2f, 1f, noise) * maxHeight * previewAmplitude;
|
||||
float wave = Mathf.Sin(time * waveSpeed + i * waveFrequency) * waveAmplitude;
|
||||
float target = Mathf.Clamp(baseHeight + wave, 0f, maxHeight);
|
||||
barHeights[i] = Mathf.Lerp(barHeights[i], target, 0.2f);
|
||||
var rt = barRects[i];
|
||||
float h = Mathf.Max(1f, barHeights[i]);
|
||||
var sd = rt.sizeDelta;
|
||||
if (sd.x != barWidth || sd.y != h)
|
||||
rt.sizeDelta = new Vector2(barWidth, h);
|
||||
if (growUpOnly && barBasePos != null && i < barBasePos.Length)
|
||||
{
|
||||
var ap = rt.anchoredPosition;
|
||||
float nx = barBasePos[i].x;
|
||||
float ny = barBasePos[i].y + h * 0.5f;
|
||||
if (ap.x != nx || ap.y != ny)
|
||||
rt.anchoredPosition = new Vector2(nx, ny);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float GetEditorTime()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return (float)EditorApplication.timeSinceStartup;
|
||||
#else
|
||||
return Time.realtimeSinceStartup;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e9b030cf9fb62746885cdd04004e33a
|
||||
@@ -0,0 +1,941 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using UnityEngine.SceneManagement;
|
||||
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";
|
||||
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);
|
||||
|
||||
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 _isGameplayScene;
|
||||
private bool _spectrumHiddenInGameplay = false;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
_cachedGameplaySceneName = gameplaySceneName;
|
||||
_isGameplayScene = SceneManager.GetActiveScene().name == _cachedGameplaySceneName;
|
||||
_spectrumHiddenInGameplay = false;
|
||||
_buttonsBound = false;
|
||||
Bind();
|
||||
ApplyDefaultMusicPicVisibility(SceneManager.GetActiveScene().name);
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
var mgr = BgmPlaybackManager.EnsureInstance();
|
||||
mgr.EnsurePlaying();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
_isGameplayScene = scene.name == _cachedGameplaySceneName;
|
||||
_spectrumHiddenInGameplay = false;
|
||||
_buttonsBound = false;
|
||||
Bind();
|
||||
ApplyDefaultMusicPicVisibility(scene.name);
|
||||
}
|
||||
|
||||
public void Bind()
|
||||
{
|
||||
musicRoot = null;
|
||||
hasExplicitMusicRoot = false;
|
||||
|
||||
Canvas sceneCanvas = FindSceneCanvas();
|
||||
var controller = Object.FindAnyObjectByType<btmandtopController>();
|
||||
if (controller != null && controller.musicPicRoot != null)
|
||||
{
|
||||
SetMusicRoot(controller.musicPicRoot, sceneCanvas);
|
||||
return;
|
||||
}
|
||||
RectTransform persistentMusic = null;
|
||||
var persistentCanvasGo = GameObject.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 = Object.FindAnyObjectByType<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 = GameObject.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 void Update()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
if (_isGameplayScene && !_spectrumHiddenInGameplay)
|
||||
{
|
||||
HideSpectrumInGameplay(_cachedGameplaySceneName);
|
||||
_spectrumHiddenInGameplay = 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;
|
||||
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(" ", "");
|
||||
var target = 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);
|
||||
}
|
||||
|
||||
private void EnsureMusicPicCanvas()
|
||||
{
|
||||
EnsureMusicPicCanvas(null);
|
||||
}
|
||||
|
||||
private void EnsureMusicPicCanvas(Canvas sceneCanvas)
|
||||
{
|
||||
if (!keepMusicPicAcrossScenes || musicRoot == null)
|
||||
return;
|
||||
|
||||
var currentCanvas = musicRoot.GetComponentInParent<Canvas>();
|
||||
var persistent = GameObject.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 (sceneName == "gamePlay_gamePlay")
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (sceneName != gameplaySceneName)
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea68bc988222e7f4e89b5d312fb18c49
|
||||
Reference in New Issue
Block a user