【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脚本,对其内容进行设置

都设置好之后就算做好了
结语
这个音频播放系统虽然简陋,但是拓展性很高,我后续也在里面加了几个广播和监听事件来实现场景切换时,切换对应切换场景的背景音乐等功能,也可以用这个系统做一个进入碰撞体后自动播放对应音频的物体。希望这篇文章能帮助到你!

浙公网安备 33010602011771号