【Unity】如何简单制作一个可跨场景,方便调用的音频播放管理器
前言
在游戏制作中,我们为了提升游戏的试听效果需要搭建起一个差不多的音频管理器。对于老鸟来说这简直手拿把掐,但是对于新手来说可能就有些头大了——对unity的组件了解有限不说,太复杂的学不来,太简陋的又不方便用。因此笔者在这里分享一下在最近gamejam比赛项目中使用到的一个可跨场景,且方便调用,可拓展性强还不复杂的音频播放管理系统(适用于中小型项目)。
配置准备
混音器
在正式开始制作之前,我们得先配置一下当前所需且音频系统不可或缺的东西——混音器!
混音器可以帮我们控制不同音频的输出轨道,简单来说就是方便调整BGM(背景音乐)和FX(特效)之间的声音大小关系,至于其他混音效果就交给音乐去做吧——不如说专业的事就得让专业的人干,作为程序给音乐配置了混音器已经是仁至义尽了……
首先我们(win系统的我们)在上方工具栏中依次选中【Window
】→【Audio
】→【Audio Mixer
】调出混音器窗口,然后创建主混音器,这里取名为MainMixer
。再在Groups下的Master通道下再根据自己需求创建分支混音组(一般的需求创建BGM和FX也够用了),也可以把BGM和FX的输出音量改一下。
Audio Listener,Audio Clip和Audio Source
在配置完混音器后,我们再来简单了解一下unity中音频播放绕不开的这三位
- Audio Listener
顾名思义,是用来监听音频的组件,但不需要我们手动添加,因为我们的摄像机上默认挂载了Audio Listener,不用动就行。如果同时挂载了多个摄像机的话可以会有同一场景中有多个Listener的报错
- Audio Clip
音频片段,是我们需要播放的音频。我们的音频资源导入后就会被unity归类到这个类型里
- Audio Source
可以理解为播放器,这里有很多播放相关的设置,比如挂载需要播放的音频片段或者是否组件激活就播放的设置之类
开始制作
音频类型(AudioType
)
我们虽然可以直接播放我们的音频片段,但是这不方便我们管理和调用,所以我们先创建一个音频类型的类。
创建脚本取名为AudioType
,代码如下:
using UnityEngine; using UnityEngine.Audio; [System.Serializable] //在Inspector窗口中可见 public class AudioType //音频类型 { [HideInInspector] public AudioSource Source; //音频源(在Inspector中隐藏) public AudioClip Clip; //音频片段 public AudioMixerGroup MixerGroup; //音频混音组 public string Name; //音频名称 [Range(0f, 1f)] public float Volume; //音量(滑动条) [Range(0.1f, 5f)] public float Pitch; //音调(滑动条) public bool Loop; //是否循环播放 public bool PlayOnAwake; //是否在Awake时自动播放 }
代码解释
这里使用了音频相关的字段,因此需要引用一下UnityEngine.Audio
。
这里为音频类型提供了音频源(播放器),音频片段,输出混音组,音频名称的自定义设置以及音量,音高,是否循环播放或者在Awake时播放的选项。
不难理解,但看着就觉得可拓展性强。
音频管理器(Audio Manager
)
我们先创建一个脚本,取名为AudioManager
,并使用单例模式
代码如下:
using UnityEngine; using UnityEngine.Audio; public class AudioManager : MonoBehaviour { /// <summary> /// 获得AudioManager的单例 /// </summary> public static AudioManager instance; [Header("音频类型")] public AudioType[] AudioTypes; // 音频类型数组,存放需要播放的音频 [Header("音频设置")] public AudioMixer mixer; // 音频混合器 private void Awake() { if (instance != null) Destroy(instance.gameObject); else instance = this; DontDestroyOnLoad(gameObject); // 保证切换场景时不被销毁 } private void OnEnable() { foreach (var type in AudioTypes) // 遍历AudioTypes数组进行初始化 { type.Source = gameObject.AddComponent<AudioSource>(); // 在调用了AudioManager的脚本的GameObject上添加AudioSource(喇叭)组件 type.Source.name = type.Name; // 设置AudioSource的名字 type.Source.volume = type.Volume; // 音量 type.Source.pitch = type.Pitch; // 音调 type.Source.loop = type.Loop; // 是否循环播放 type.Source.playOnAwake = type.PlayOnAwake; // 是否在Awake时自动播放 if (type.MixerGroup != null) // 如果有设置AudioMixerGroup { type.Source.outputAudioMixerGroup = type.MixerGroup; // 设置AudioMixerGroup } } }
这部分代码就是AudioManger
的核心了。但我们还没播放,暂停和停止的功能。我这里给几个功能模板,了解原理后可以根据自己的需求高度自定义,代码如下:
public void PlayBGM(string name) // 播放音频时调用 { foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { type.Source.Stop(); // 停止所有音频 } foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { if (type.Name == name) // 如果找到名字name对应的音频 { type.Source.clip = type.Clip; // 设置音频Clip type.Source.Play(); // 播放音频 return; } } Debug.LogError("没有找到"+name+"音频"); // 没找到音频时输出错误信息 } public void PlayBGM(AudioClip clip) // 播放音频时调用 { foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { type.Source.Stop(); // 停止所有音频 } foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { if (type.Clip == clip) // 如果找到名字name对应的音频 { type.Source.clip = type.Clip; // 设置音频Clip type.Source.Play(); // 播放音频 return; } } Debug.LogError("没有找到"+clip+"音频"); // 没找到音频时输出错误信息 } public void PlayFX(string name) // 播放音效时调用 { foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { if (type.Name == name) // 如果找到名字name对应的音频 { type.Source.PlayOneShot(type.Clip); // 播放音效 return; } } Debug.LogError("没有找到"+name+"音效"); // 没找到音效时输出错误信息 } public void Pause(string name) // 暂停音频时调用 { foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { if (type.Name == name) { type.Source.Pause(); // 暂停音频 return; } } Debug.LogError("没有找到"+name+"音频"); } public void Stop(string name) // 停止音频时调用 { foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { if (type.Name == name) { type.Source.Stop(); // 停止音频 return; } } Debug.LogError("没有找到"+name+"音频"); } public void StopAll() // 停止所有音频时调用 { foreach (AudioType type in AudioTypes) // 遍历AudioTypes数组 { type.Source.Stop(); // 停止音频 } Debug.LogError("没有找到"+name+"音频"); } public bool IsPlaying(string name) // 判断音频是否正在播放 { foreach (AudioType type in AudioTypes) { if (type.Name == name) { return type.Source.isPlaying; } } Debug.LogError("没有找到"+name+"音频"); return false; }
代码解释
我们使用单例模式方便在每个地方随时调用
public声明一个AudioType
的数组方便我们在Unity的Inspector窗口下设置,public一个AudioMixer
来索引混音器
Awake()
里做完声明单例的仪式后,在OnEnable()
里遍历AudioTypes
数组进行初始化
给每一个音频都添加一个播放器(不知道会不会很吃性能,如果你不想的话可以想想其他办法),然后把他们的基本信息比如这个音频的名字,音量,音高啥的赋值给播放器上的对应属性。但这时不能直接赋值音频片段(clip),不然可能会启用就群魔乱舞般地播放所有音频
功能方法对音频的索引主要靠自己取的名字,也就是Name
。BGM是个特例,因为我的场景类型直接挂载了音频片段,所以加载场景时可以直接使用音频片段播放。可以直接在你想播放音频的地方播放,一般情况下的调用是这个样子的:
AudioManager.instance.PlayBGM("主题曲BGM");
我这里的播放方法是BGM和FX分开创建的,因为调用一首BGM时,一般也不可能和另一首BGM共存,所以干脆直接先停掉其他BGM再播放需要的BGM。但音效不一样,同一场景同一时间内,可能会有多个音效存在,所以不必停止其他FX播放。而切换音乐和停止音乐我懒得调试所以没做淡入淡出,有想法的小伙伴可以自己尝试下。
回到Unity
我们回到Unity,在场景中创建一个空物体,取名为Audio Manager,挂载上我们刚写完的AudioManager
脚本,对其内容进行设置
都设置好之后就算做好了
结语
这个音频播放系统虽然简陋,但是拓展性很高,我后续也在里面加了几个广播和监听事件来实现场景切换时,切换对应切换场景的背景音乐等功能,也可以用这个系统做一个进入碰撞体后自动播放对应音频的物体。希望这篇文章能帮助到你!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库