139 lines
4.3 KiB
C#
139 lines
4.3 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace GameFramework
|
||
{
|
||
/// <summary>
|
||
/// 全局音频管理器(持久单例)。
|
||
/// 管理 BGM(背景音乐,循环)和 SFX(音效,一次性)。
|
||
/// 音量通过 PlayerPrefs 持久化。
|
||
/// </summary>
|
||
public class AudioManager : PersistentSingleton<AudioManager>
|
||
{
|
||
const string PREF_BGM_VOLUME = "AudioManager_BGMVolume";
|
||
const string PREF_SFX_VOLUME = "AudioManager_SFXVolume";
|
||
|
||
AudioSource bgmPlayer; // 背景音乐播放器
|
||
AudioSource sfxPlayer; // 音效播放器
|
||
|
||
float bgmVolume = 1f;
|
||
float sfxVolume = 1f;
|
||
|
||
public float BGMVolume => bgmVolume;
|
||
public float SFXVolume => sfxVolume;
|
||
|
||
protected override void Awake()
|
||
{
|
||
base.Awake();
|
||
// 如果是重复实例,base.Awake 已调用 Destroy,这里直接返回
|
||
if (Instance != this) return;
|
||
|
||
// 创建两个 AudioSource
|
||
bgmPlayer = gameObject.AddComponent<AudioSource>();
|
||
bgmPlayer.loop = true;
|
||
bgmPlayer.playOnAwake = false;
|
||
|
||
sfxPlayer = gameObject.AddComponent<AudioSource>();
|
||
sfxPlayer.loop = false;
|
||
sfxPlayer.playOnAwake = false;
|
||
|
||
// 读取持久化音量
|
||
bgmVolume = PlayerPrefs.GetFloat(PREF_BGM_VOLUME, 1f);
|
||
sfxVolume = PlayerPrefs.GetFloat(PREF_SFX_VOLUME, 1f);
|
||
bgmPlayer.volume = bgmVolume;
|
||
sfxPlayer.volume = sfxVolume;
|
||
}
|
||
|
||
#region BGM
|
||
/// <summary>
|
||
/// 播放背景音乐(循环)。
|
||
/// </summary>
|
||
public void PlayBGM(AudioClip clip, bool restart = false)
|
||
{
|
||
if (clip == null) return;
|
||
if (bgmPlayer.isPlaying && bgmPlayer.clip == clip && !restart)
|
||
return;
|
||
bgmPlayer.clip = clip;
|
||
bgmPlayer.volume = bgmVolume;
|
||
bgmPlayer.Play();
|
||
}
|
||
|
||
public void StopBGM()
|
||
{
|
||
if (bgmPlayer.isPlaying)
|
||
bgmPlayer.Stop();
|
||
}
|
||
|
||
public void PauseBGM() => bgmPlayer.Pause();
|
||
public void UnpauseBGM() => bgmPlayer.UnPause();
|
||
|
||
public void SetBGMVolume(float volume)
|
||
{
|
||
bgmVolume = Mathf.Clamp01(volume);
|
||
bgmPlayer.volume = bgmVolume;
|
||
PlayerPrefs.SetFloat(PREF_BGM_VOLUME, bgmVolume);
|
||
PlayerPrefs.Save();
|
||
}
|
||
#endregion
|
||
|
||
#region SFX
|
||
/// <summary>
|
||
/// 播放单个音效。
|
||
/// </summary>
|
||
public void PlaySFX(AudioData data)
|
||
{
|
||
if (data == null || data.clip == null) return;
|
||
// sfxPlayer.volume 已在 Awake 中设为 sfxVolume,
|
||
// PlayOneShot 的 volumeScale 再乘以 data.volume,
|
||
// 最终音量 = sfxVolume * data.volume
|
||
sfxPlayer.PlayOneShot(data.clip, data.volume);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从列表中随机播放一个音效。
|
||
/// </summary>
|
||
public void PlayRandomSFX(AudioData data, List<AudioData> variants = null)
|
||
{
|
||
if (variants != null && variants.Count > 0)
|
||
{
|
||
PlaySFX(variants[Random.Range(0, variants.Count)]);
|
||
return;
|
||
}
|
||
PlaySFX(data);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 直接通过 AudioClip 播放音效。
|
||
/// </summary>
|
||
public void PlaySFX(AudioClip clip, float volume = 1f)
|
||
{
|
||
if (clip == null) return;
|
||
sfxPlayer.PlayOneShot(clip, volume);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通过 Resources 路径加载并播放音效。
|
||
/// 路径相对于 Assets/audios/Resources/,例如 "SFX/Bell"
|
||
/// </summary>
|
||
public void PlaySFXFromResources(string resourcePath, float volume = 1f)
|
||
{
|
||
var clip = Resources.Load<AudioClip>(resourcePath);
|
||
if (clip == null)
|
||
{
|
||
Debug.LogWarning($"[AudioManager] SFX not found: {resourcePath}");
|
||
return;
|
||
}
|
||
PlaySFX(clip, volume);
|
||
}
|
||
|
||
public void SetSFXVolume(float volume)
|
||
{
|
||
sfxVolume = Mathf.Clamp01(volume);
|
||
PlayerPrefs.SetFloat(PREF_SFX_VOLUME, sfxVolume);
|
||
PlayerPrefs.Save();
|
||
}
|
||
#endregion
|
||
}
|
||
}
|