当前位置: 首页 > article >正文

Unity中的AudioManager

1.先贴代码

using UnityEngine;
using System.Collections.Generic;
using System.Collections;
using UnityEngine.SceneManagement;public class AudioManager : MonoSingleton<AudioManager>
{[Header("Audio Settings")][SerializeField] private int initialPoolSize = 5;      // 初始音效对象池大小[SerializeField] private float defaultVolume = 1f;     // 默认全局音量[SerializeField] private int maxMusicChannels = 3;     // 最大音乐通道数private Dictionary<string, AudioClip> audioClips = new Dictionary<string, AudioClip>();         // 普通音频资源字典private Dictionary<string, AudioClip> globalAudioClips = new Dictionary<string, AudioClip>();   // 全局音频资源字典private List<AudioSource> sfxSourcePool = new List<AudioSource>();                            // 音效对象池private Dictionary<string, AudioSource> musicChannels = new Dictionary<string, AudioSource>(); // 音乐通道字典private void Awake(){Initialize();if (Instance != this){Destroy(gameObject);}DontDestroyOnLoad(gameObject);}/// <summary>/// 初始化音频管理器/// </summary>private void Initialize(){LoadAudioResources("");          // 加载普通音频资源LoadGlobalAudioResources("");    // 加载全局音频资源InitializeSFXPool();             // 初始化音效对象池CreateMusicChannel("Main");      // 创建默认音乐通道}/// <summary>/// 加载音频资源/// </summary>/// <param name="subPath">子目录路径</param>/// <param name="isGlobal">是否为全局资源</param>private void LoadAudioResources(string subPath = "", bool isGlobal = false){string path = "AudioClips/" + subPath;if (isGlobal) path = "GlobalAudioClips/" + subPath;AudioClip[] clips = Resources.LoadAll<AudioClip>(path);var targetDict = isGlobal ? globalAudioClips : audioClips;foreach (AudioClip clip in clips){string clipName = clip.name;if (!targetDict.ContainsKey(clipName)){targetDict.Add(clipName, clip);}else{Debug.LogWarning($"发现重复的音频文件名: {clipName} ({(isGlobal ? "全局" : "普通")})");}}}/// <summary>/// 加载全局音频资源/// </summary>private void LoadGlobalAudioResources(string subPath = ""){LoadAudioResources(subPath, true);}/// <summary>/// 初始化音效对象池/// </summary>private void InitializeSFXPool(){for (int i = 0; i < initialPoolSize; i++){CreateNewSFXSource();}}#region 音效系统/// <summary>/// 创建新的音效源/// </summary>private AudioSource CreateNewSFXSource(){AudioSource newSource = gameObject.AddComponent<AudioSource>();newSource.playOnAwake = false;newSource.volume = defaultVolume;sfxSourcePool.Add(newSource);return newSource;}/// <summary>/// 获取可用音效源/// </summary>private AudioSource GetAvailableSFXSource(){foreach (AudioSource source in sfxSourcePool){if (!source.isPlaying) return source;}return CreateNewSFXSource();}/// <summary>/// 播放音效/// </summary>/// <param name="clipName">音频名称</param>/// <param name="volumeScale">音量缩放</param>/// <param name="isGlobal">是否为全局音频</param>public void PlaySFX(string clipName, float volumeScale = 1f, bool isGlobal = false){var targetDict = isGlobal ? globalAudioClips : audioClips;if (targetDict.TryGetValue(clipName, out AudioClip clip)){AudioSource source = GetAvailableSFXSource();source.clip = clip;source.volume = defaultVolume * volumeScale;source.Play();}else{Debug.LogError($"{(isGlobal ? "全局" : "")}音效文件未找到: {clipName}");}}/// <summary>/// 播放全局音效(便捷方法)/// </summary>public void PlayGlobalSFX(string clipName, float volumeScale = 1f){PlaySFX(clipName, volumeScale, true);}/// <summary>/// 停止指定音效/// </summary>public void StopSFX(string clipName){foreach (AudioSource source in sfxSourcePool){if (source.isPlaying && source.clip != null && source.clip.name == clipName){source.Stop();}}}/// <summary>/// 停止所有音效/// </summary>public void StopAllSFX(){foreach (AudioSource source in sfxSourcePool){source.Stop();}}#endregion#region 音乐系统/// <summary>/// 创建音乐通道/// </summary>private bool CreateMusicChannel(string channelName){if (musicChannels.ContainsKey(channelName)){Debug.LogWarning($"音乐通道已存在: {channelName}");return true;}if (musicChannels.Count >= maxMusicChannels){Debug.LogError($"已达到最大音乐通道数:{maxMusicChannels}");return false;}AudioSource newSource = gameObject.AddComponent<AudioSource>();newSource.loop = true;newSource.volume = defaultVolume;musicChannels.Add(channelName, newSource);return true;}/// <summary>/// 播放音乐(自动创建通道)/// </summary>public void PlayMusic(string channelName, string clipName, float volumeScale = 1f, bool isGlobal = false){// 确保通道存在if (!musicChannels.ContainsKey(channelName) && !CreateMusicChannel(channelName)){return; // 通道创建失败}var targetDict = isGlobal ? globalAudioClips : audioClips;AudioSource source = musicChannels[channelName];if (targetDict.TryGetValue(clipName, out AudioClip clip)){source.clip = clip;source.volume = defaultVolume * volumeScale;source.Play();}else{Debug.LogError($"{(isGlobal ? "全局" : "")}音乐文件未找到:{clipName}");}}/// <summary>/// 播放全局音乐(便捷方法)/// </summary>public void PlayGlobalMusic(string channelName, string clipName, float volumeScale = 1f){PlayMusic(channelName, clipName, volumeScale, true);}/// <summary>/// 停止指定通道的音乐/// </summary>public void StopMusic(string channelName){if (musicChannels.TryGetValue(channelName, out AudioSource source)){source.Stop();}}/// <summary>/// 暂停指定通道的音乐/// </summary>public void PauseMusic(string channelName){if (musicChannels.TryGetValue(channelName, out AudioSource source)){source.Pause();}}/// <summary>/// 恢复指定通道的音乐/// </summary>public void ResumeMusic(string channelName){if (musicChannels.TryGetValue(channelName, out AudioSource source)){source.UnPause();}}/// <summary>/// 设置通道音量/// </summary>public void SetChannelVolume(string channelName, float volume){if (musicChannels.TryGetValue(channelName, out AudioSource source)){source.volume = Mathf.Clamp01(volume);}}/// <summary>/// 获取通道音量/// </summary>public float GetChannelVolume(string channelName){if (musicChannels.TryGetValue(channelName, out AudioSource source)){return source.volume;}return 0f;}#endregion#region 全局控制/// <summary>/// 设置主音量/// </summary>public void SetMasterVolume(float volume){defaultVolume = Mathf.Clamp01(volume);// 更新所有音效源foreach (AudioSource source in sfxSourcePool){source.volume = defaultVolume;}// 更新所有音乐通道foreach (var channel in musicChannels.Values){channel.volume = defaultVolume;}}/// <summary>/// 停止所有音频/// </summary>public void StopAllAudio(bool immediate = true){if (immediate){StopAllSFX();foreach (var channel in musicChannels.Values){channel.Stop();}}else{StartCoroutine(DelayedStopAll());}}private IEnumerator DelayedStopAll(){yield return null;StopAllSFX();foreach (var channel in musicChannels.Values){channel.Stop();}}/// <summary>/// 获取所有音乐通道名称/// </summary>public List<string> GetMusicChannels(){return new List<string>(musicChannels.Keys);}/// <summary>/// 淡入淡出主音量/// </summary>public IEnumerator FadeMasterVolume(float targetVolume, float duration, System.Action onComplete = null){float startVolume = defaultVolume;float timer = 0f;while (timer < duration){defaultVolume = Mathf.Lerp(startVolume, targetVolume, timer / duration);SetMasterVolume(defaultVolume);timer += Time.deltaTime;yield return null;}defaultVolume = targetVolume;SetMasterVolume(defaultVolume);onComplete?.Invoke();}#endregion#region 扩展方法/// <summary>/// 淡入淡出指定通道的音量/// </summary>public IEnumerator FadeChannel(string channelName, float targetVolume, float duration){if (!musicChannels.TryGetValue(channelName, out AudioSource source))yield break;float startVolume = source.volume;float timer = 0f;while (timer < duration){source.volume = Mathf.Lerp(startVolume, targetVolume, timer / duration);timer += Time.deltaTime;yield return null;}source.volume = targetVolume;}#endregion
}

2.具体用法

提前说明一下,音频文件是放在Resources/AudioClips/ 目录下,可以将一开始的

LoadAudioResources函数公开就能传入深层路径。

1. 基本功能使用

// 播放普通音效
AudioManager.Instance.PlaySFX("ButtonClick");// 播放全局音效
AudioManager.Instance.PlayGlobalSFX("Notification");// 播放背景音乐(自动创建Main通道)
AudioManager.Instance.PlayMusic("Main", "BackgroundMusic");// 播放战斗音乐(自动创建Battle通道)
AudioManager.Instance.PlayMusic("Battle", "BattleTheme", 0.8f);// 暂停和恢复音乐
AudioManager.Instance.PauseMusic("Main");
AudioManager.Instance.ResumeMusic("Main");// 停止所有音频
AudioManager.Instance.StopAllAudio();

2. 音量控制

2.1代码在调用时控制当次音效音量大小

// 设置主音量(影响所有音效和音乐)
AudioManager.Instance.SetMasterVolume(0.7f);// 设置特定音乐通道音量
AudioManager.Instance.SetChannelVolume("Main", 0.5f);// 淡入淡出主音量
StartCoroutine(AudioManager.Instance.FadeMasterVolume(0f, 2f, () => {Debug.Log("音量淡出完成");
}));// 淡入淡出特定通道音量
StartCoroutine(AudioManager.Instance.FadeChannel("Battle", 0f, 1.5f));

2.2使用Slider控制音量

2.2.1主音量控制
public Slider masterVolumeSlider;private void Start()
{masterVolumeSlider.value = AudioManager.Instance.GetMasterVolume();masterVolumeSlider.onValueChanged.AddListener(SetMasterVolume);
}private void SetMasterVolume(float volume)
{AudioManager.Instance.SetMasterVolume(volume);
}
2.2.2音乐通道音量控制
public Slider musicVolumeSlider;private void Start()
{musicVolumeSlider.value = AudioManager.Instance.GetChannelVolume("Main");musicVolumeSlider.onValueChanged.AddListener(SetMusicVolume);
}private void SetMusicVolume(float volume)
{AudioManager.Instance.SetChannelVolume("Main", volume);
}

没啥好多的了,直接用就行了。

下面随便写点吧

  • 普通音频资源放在 Resources/AudioClips/ 目录下

  • 全局音频资源放在 Resources/GlobalAudioClips/ 目录下

  • 主音量控制所有声音

  • 通道音量相对于主音量

  • 最终音量 = 主音量 × 通道音量 × 播放音量缩放

  • 由于使用 DontDestroyOnLoad,AudioManager 会在场景切换时保留

  • 使用 StopAllAudio() 在场景切换时清理不需要的声音

http://www.lryc.cn/news/2394346.html

相关文章:

  • VM改MAC电脑密码(截图)
  • SpringBoot+Vue+微信小程序校园自助打印系统
  • 【论文精读】2024 CVPR--Upscale-A-Video现实世界视频超分辨率(RealWorld VSR)
  • 学术合作交流
  • 【线上故障排查】Redis缓存与数据库中数据不一致问题的排查与同步策略优化
  • 【Git命令】
  • 【LUT技术专题】图像自适应3DLUT
  • 德拜温度热容推导
  • 扫一扫的时候会经历哪些事
  • Typescript学习教程,从入门到精通,TypeScript 泛型与类型操作详解(二)(17)
  • 【iOS】源码阅读(五)——类类的结构分析
  • 基于CangjieMagic的RAG技术赋能智能问答系统
  • 算力租赁革命:弹性模式如何重构数字时代的创新门槛​
  • 图论回溯
  • 使用arthas热替换在线运行的java class文件
  • RFID测温芯片助力新能源产业安全与能效提升
  • S32K3 工具篇9:如何在无源码情况下灵活调试elf文件
  • Nacos 配置文件总结
  • ASP.NET Web Forms框架识别
  • LG P4119 [Ynoi2018] 未来日记 Solution
  • 流程引擎选型指南
  • 基于大模型预测带状疱疹(无并发症)诊疗方案的研究报告
  • 哈工大计统大作业-程序人生
  • 设计模式——装饰器设计模式(结构型)
  • 途景VR智拍APP:开启沉浸式VR拍摄体验
  • Linux环境搭建MCU开发环境
  • Android高级开发第一篇 - JNI(初级入门篇)
  • Kubernetes RBAC权限控制:从入门到实战
  • python实战项目71:基于Python的US News世界大学排名数据爬取
  • 【基础算法】高精度(加、减、乘、除)