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

都设置好之后就算做好了

结语

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

posted @   StaDark  阅读(240)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示