125 lines
3.8 KiB
C#
125 lines
3.8 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
|
||
namespace GameFramework
|
||
{
|
||
/// <summary>
|
||
/// 音频片段数据,可挂到 ScriptableObject 或直接 SerializeField。
|
||
/// </summary>
|
||
[System.Serializable]
|
||
public class AudioData
|
||
{
|
||
public AudioClip audioClip;
|
||
[Range(0f, 1f)] public float volume = 1f;
|
||
}
|
||
|
||
/// <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.audioClip == null) return;
|
||
// sfxPlayer.volume 已在 Awake 中设为 sfxVolume,
|
||
// PlayOneShot 的 volumeScale 再乘以 data.volume,
|
||
// 最终音量 = sfxVolume * data.volume
|
||
sfxPlayer.PlayOneShot(data.audioClip, 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);
|
||
}
|
||
|
||
public void SetSFXVolume(float volume)
|
||
{
|
||
sfxVolume = Mathf.Clamp01(volume);
|
||
PlayerPrefs.SetFloat(PREF_SFX_VOLUME, sfxVolume);
|
||
PlayerPrefs.Save();
|
||
}
|
||
#endregion
|
||
}
|
||
}
|