407 lines
11 KiB
C#
407 lines
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.Audio;
|
|
|
|
public class BgmPlaybackManager : MonoBehaviour
|
|
{
|
|
public static BgmPlaybackManager Instance { get; private set; }
|
|
|
|
private const string MainSceneName = "Main_main";
|
|
private const string GameplaySceneName = "gamePlay_gamePlay";
|
|
private const string BeforeEverythingSceneName = "beforeEverything";
|
|
|
|
[Header("Audio")]
|
|
public AudioSource audioSource;
|
|
public AudioMixerGroup outputMixerGroup;
|
|
public AudioClip clip;
|
|
[Tooltip("Optional Music_Data resource path for the lobby BGM. Example: Data/Music/allAlong")]
|
|
public string musicDataResourcePath = "Data/Music/allAlong";
|
|
[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;
|
|
private Coroutine loadClipRoutine;
|
|
|
|
public static BgmPlaybackManager EnsureInstance()
|
|
{
|
|
if (Instance != null) return Instance;
|
|
var go = new GameObject("BGM_PlaybackManager");
|
|
Instance = go.AddComponent<BgmPlaybackManager>();
|
|
return Instance;
|
|
}
|
|
|
|
public void SetOutputMixerGroup(AudioMixerGroup mixerGroup)
|
|
{
|
|
outputMixerGroup = mixerGroup;
|
|
ApplyOutputMixerGroup();
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance != null && Instance != this)
|
|
{
|
|
Destroy(gameObject);
|
|
return;
|
|
}
|
|
Instance = this;
|
|
DontDestroyOnLoad(gameObject);
|
|
|
|
if (audioSource == null)
|
|
audioSource = gameObject.AddComponent<AudioSource>();
|
|
|
|
ApplyOutputMixerGroup();
|
|
|
|
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];
|
|
}
|
|
else if (clip == null)
|
|
{
|
|
ResolveDefaultClip();
|
|
}
|
|
if (clip != null)
|
|
audioSource.clip = clip;
|
|
|
|
SyncPlaybackForScene(SceneManager.GetActiveScene());
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
StopLobbyAudioAndParticles();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
if (Instance == this)
|
|
{
|
|
Instance = null;
|
|
}
|
|
}
|
|
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
SyncPlaybackForScene(scene);
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
}
|
|
|
|
private void SyncPlaybackForScene(Scene scene)
|
|
{
|
|
if (!ShouldPlayInScene(scene.name))
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
return;
|
|
}
|
|
|
|
// Allowed scenes should keep the lobby BGM alive.
|
|
if (audioSource == null || !audioSource.isPlaying)
|
|
EnsurePlaying();
|
|
}
|
|
|
|
private bool ShouldPlayInScene(string sceneName)
|
|
{
|
|
if (string.IsNullOrEmpty(sceneName))
|
|
return false;
|
|
|
|
if (string.Equals(sceneName, GameplaySceneName, StringComparison.OrdinalIgnoreCase))
|
|
return false;
|
|
|
|
if (string.Equals(sceneName, MainSceneName, StringComparison.OrdinalIgnoreCase))
|
|
return false;
|
|
|
|
if (string.Equals(sceneName, BeforeEverythingSceneName, StringComparison.OrdinalIgnoreCase))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public void EnsurePlaying()
|
|
{
|
|
if (!ShouldPlayInScene(SceneManager.GetActiveScene().name))
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
return;
|
|
}
|
|
|
|
if (audioSource == null)
|
|
audioSource = gameObject.AddComponent<AudioSource>();
|
|
|
|
ApplyOutputMixerGroup();
|
|
|
|
audioSource.loop = loop;
|
|
|
|
if (audioSource.clip == null && clip != null)
|
|
audioSource.clip = clip;
|
|
|
|
if (audioSource.clip == null)
|
|
{
|
|
ResolveDefaultClip();
|
|
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)
|
|
{
|
|
if (loadClipRoutine != null)
|
|
StopCoroutine(loadClipRoutine);
|
|
loadClipRoutine = StartCoroutine(LoadClipRoutine());
|
|
return;
|
|
}
|
|
|
|
if (audioSource.clip != null && !audioSource.isPlaying)
|
|
audioSource.Play();
|
|
}
|
|
|
|
private IEnumerator LoadClipRoutine()
|
|
{
|
|
isLoading = true;
|
|
|
|
if (audioSource.clip == null)
|
|
{
|
|
ResolveDefaultClip();
|
|
}
|
|
|
|
// 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 the checked-in lobby track in Assets/BGM.
|
|
var projectRoot = Path.GetFullPath(Path.Combine(Application.dataPath, ".."));
|
|
path = Path.Combine(projectRoot, "Assets", "BGM", "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 (!ShouldPlayInScene(SceneManager.GetActiveScene().name))
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
yield break;
|
|
}
|
|
|
|
if (audioSource.clip != null && !audioSource.isPlaying)
|
|
audioSource.Play();
|
|
|
|
if (loadClipRoutine != null)
|
|
{
|
|
loadClipRoutine = null;
|
|
}
|
|
}
|
|
|
|
private void EnsurePlaylist()
|
|
{
|
|
if (playlist == null)
|
|
playlist = new List<AudioClip>();
|
|
|
|
if (playlist.Count == 0)
|
|
{
|
|
if (ResolveDefaultClip(out AudioClip defaultClip))
|
|
{
|
|
playlist.Add(defaultClip);
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ResolveDefaultClip()
|
|
{
|
|
if (clip != null)
|
|
{
|
|
audioSource.clip = clip;
|
|
return;
|
|
}
|
|
|
|
if (ResolveDefaultClip(out AudioClip resolved))
|
|
{
|
|
clip = resolved;
|
|
audioSource.clip = resolved;
|
|
}
|
|
}
|
|
|
|
private bool ResolveDefaultClip(out AudioClip resolved)
|
|
{
|
|
resolved = null;
|
|
|
|
if (!string.IsNullOrEmpty(musicDataResourcePath))
|
|
{
|
|
var musicData = Resources.Load<Music_Data>(musicDataResourcePath);
|
|
if (musicData != null && musicData.Clip != null)
|
|
{
|
|
resolved = musicData.Clip;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
var musicDatas = Resources.LoadAll<Music_Data>("Data/Music");
|
|
if (musicDatas != null)
|
|
{
|
|
for (int i = 0; i < musicDatas.Length; i++)
|
|
{
|
|
var data = musicDatas[i];
|
|
if (data != null && data.Clip != null)
|
|
{
|
|
resolved = data.Clip;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void PlayIndex(int index)
|
|
{
|
|
if (!ShouldPlayInScene(SceneManager.GetActiveScene().name))
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
return;
|
|
}
|
|
|
|
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 (!ShouldPlayInScene(SceneManager.GetActiveScene().name))
|
|
{
|
|
StopLobbyAudioAndParticles();
|
|
return;
|
|
}
|
|
if (audioSource.isPlaying)
|
|
audioSource.Pause();
|
|
else
|
|
audioSource.Play();
|
|
}
|
|
|
|
private void StopLobbyAudioAndParticles()
|
|
{
|
|
if (loadClipRoutine != null)
|
|
{
|
|
StopCoroutine(loadClipRoutine);
|
|
loadClipRoutine = null;
|
|
}
|
|
|
|
isLoading = false;
|
|
|
|
if (audioSource != null)
|
|
{
|
|
audioSource.Stop();
|
|
}
|
|
|
|
}
|
|
|
|
private void ApplyOutputMixerGroup()
|
|
{
|
|
if (audioSource != null)
|
|
{
|
|
audioSource.outputAudioMixerGroup = outputMixerGroup;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|