116 lines
4.3 KiB
C#
116 lines
4.3 KiB
C#
using System.Collections;
|
||
using UnityEngine;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Events;
|
||
|
||
namespace BaseGames.Audio
|
||
{
|
||
/// <summary>BGM 状态机内部状态。</summary>
|
||
public enum MusicState
|
||
{
|
||
Exploration, // 默认:区域探索 BGM
|
||
Boss, // Boss 战:Boss 主题 BGM
|
||
Victory, // Boss 击败后短暂胜利音乐
|
||
None, // 过场/死亡/主菜单时由 BGMController 直接切换
|
||
}
|
||
|
||
/// <summary>
|
||
/// BGM 控制器:订阅世界/Boss/游戏状态事件,指挥 AudioManager 切换 BGM 和快照。
|
||
/// 挂在 Persistent 场景 [AudioManager] 子对象上。
|
||
/// </summary>
|
||
public class BGMController : MonoBehaviour
|
||
{
|
||
[SerializeField] private AudioManager _audioManager;
|
||
[SerializeField] private AudioConfigSO _config;
|
||
|
||
[Header("Event Channels - Subscribe")]
|
||
[SerializeField] private GameStateEventChannelSO _onGameStateChanged;
|
||
[SerializeField] private BoolEventChannelSO _onBossFightToggled; // true=开始, false=结束
|
||
[SerializeField] private StringEventChannelSO _onRegionEntered;
|
||
|
||
private MusicState _musicState = MusicState.Exploration;
|
||
private string _currentRegion = string.Empty;
|
||
private readonly CompositeDisposable _subscriptions = new();
|
||
|
||
private void Awake()
|
||
{
|
||
Debug.Assert(_config != null, "[BGMController] _config 未赋值,请在 Inspector 中指定 AudioConfigSO。", this);
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
_onBossFightToggled?.Subscribe(OnBossFightToggled).AddTo(_subscriptions);
|
||
_onRegionEntered?.Subscribe(OnRegionEntered).AddTo(_subscriptions);
|
||
_onGameStateChanged?.Subscribe(HandleStateChanged).AddTo(_subscriptions);
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
_subscriptions.Clear();
|
||
}
|
||
|
||
private void OnBossFightToggled(bool started)
|
||
{
|
||
if (started)
|
||
{
|
||
_musicState = MusicState.Boss;
|
||
var clip = _config.GetBossBGM(_currentRegion);
|
||
if (clip == null)
|
||
{
|
||
Debug.LogWarning($"[BGMController] 区域 '{_currentRegion}' 未配置 Boss BGM,将保持当前音乐。", this);
|
||
}
|
||
else
|
||
{
|
||
_audioManager.PlayBGM(clip, fadeOutDur: 1f, fadeInDur: 0.5f);
|
||
}
|
||
_audioManager.TransitionToSnapshot("BossFight", 0.5f);
|
||
}
|
||
else
|
||
{
|
||
StartCoroutine(PlayVictoryThenRestore());
|
||
}
|
||
}
|
||
|
||
private IEnumerator PlayVictoryThenRestore()
|
||
{
|
||
_musicState = MusicState.Victory;
|
||
_audioManager.PlayBGM(_config.VictoryStingBGM,
|
||
fadeOutDur: 0.3f, fadeInDur: 0.1f);
|
||
float dur = _config.VictoryStingDuration;
|
||
yield return new WaitForSecondsRealtime(dur);
|
||
_musicState = MusicState.Exploration;
|
||
OnRegionEntered(_currentRegion);
|
||
_audioManager.TransitionToSnapshot("Default", 1.0f);
|
||
}
|
||
|
||
private void OnRegionEntered(string regionId)
|
||
{
|
||
if (regionId == _currentRegion) return;
|
||
_currentRegion = regionId;
|
||
if (_musicState == MusicState.Exploration)
|
||
{
|
||
var clip = _config.GetZoneBGM(regionId);
|
||
if (clip == null)
|
||
{
|
||
Debug.LogWarning($"[BGMController] 区域 '{regionId}' 未配置 BGM,将保持当前音乐。", this);
|
||
return;
|
||
}
|
||
_audioManager.PlayBGM(clip, fadeOutDur: 1f, fadeInDur: 1f);
|
||
}
|
||
}
|
||
|
||
private void HandleStateChanged(GameStateId state)
|
||
{
|
||
if (state == GameStates.MainMenu)
|
||
_audioManager.PlayBGM(_config.MainMenuBGM,
|
||
fadeOutDur: 0.5f, fadeInDur: 1.0f);
|
||
else if (state == GameStates.Paused)
|
||
_audioManager.TransitionToSnapshot("Paused", 0.2f);
|
||
else if (state == GameStates.Dead)
|
||
_audioManager.TransitionToSnapshot("Dead", 1.5f);
|
||
else if (state == GameStates.Gameplay)
|
||
_audioManager.TransitionToSnapshot("Default", 0.3f);
|
||
}
|
||
}
|
||
}
|