目标及介绍
目标:
实现一个
- 可复用集中管理音效
- 3D音效
- 音频clip播放不冲突
- 可以由制作的音量面板调节(AudioMixer)。
的音效系统。
(BGM无需3D音效)
如何制作音量面板同步控制AudioMixer这里不涉及,从csdn或b站都能找到一大片教学。
AudioSource有三个播放方法:
Play()
成员方法。PlayOneShot(clip等参数)
成员方法。PlayClipAtPoint(clip,position)
,是静态方法。
遇到的问题
AudioSource组件设置中可以改为播放3D音效。
但是,
Play()不能同时播放多个clip,会产生冲突,不能指定位置。
PlayOneShot()能同时播放,但是不能指定位置。
PlayClipAtPoint是静态方法不能由AudioMixer控制。
目标第四点,想可以由AudioMixer调节,只能是确切的AudioSource组件,相应的只能用属于这个AudioSource的对象的成员方法。(目前知识储备是这样理解的,只有成员方法才能由对象的成员变量挂钩,于是无法使用PlayClipAtPoint
静态方法)。
思路
针对1.,可以实现一个AudioManager的空物体并且挂在上AudioManager.cs脚本,所有代码和Clip在AudioManager集中管理。并且AudioManager实现单例模式,多场景可用(DontDestroyOnLoad)。需要时可以直接通过instance单例对象调用相应的方法进行播放。
其实如果不集中管理,在每个音源物体上挂一个AudioSource组件就解决问题了。但是这样对于后续的AudioMixer调整也确实不友好。
针对2.,起初从网上查找教程以为只有PlayClipAtPoint(唯一可以指认音效位置的方法)能播放3D效果。着实不好办了。
后来发现Play和PlayOnShot也可以实现3D音效,本质上是在AudioSource组件把
SpatialBlend改为3D就可以实现3D效果了。
然后其他具体的调整可以在3D Sound Settings里进行设置。
针对3.,播放不冲突,那么只能使用PlayOnShot或者PlayClipAtPoint(不满足可控音量的限制)。
针对4.,确保用成员方法,并且有一个AudioSource组件。
一个解决方案
上面每一条限制取一个交集,那么其中一个解决方法是:
GameObject.Instantiate(prefab)
,prefab
是一个预制体(AudioSpot),上面挂载一个AudioSource组件并且设置为3D音效,并且在其上挂一个AudioSpot.cs,在一定时长之后自动摧毁(放置占用过多硬件资源)。这样Play和PlayOnShot都可以用,具体用哪个看代码写法。
在AudioManager里,哪里需要播放3D音效,则在对应地方实例化一个AudioSpot,然后调用其方法进行播放。同时此音效可以受AudioMixer控制。
这么一来就可以实现这四点要求。
统一的音效管理系统(关键代码)
AudioManager.cs
单例类,一个游戏场景一个即可
public static AudioManager instance;
public AudioClip ShootClip;
public AudioClip RelodClip;
public AudioClip thunderClip;
//协调好每个场景中的设置面板---emm有其他更好的实现方式,直接看这里挺突兀,可以不管
private void Start()
{
if (Launcher.instance.bgmVolume == -100 && Launcher.instance.soundVolume == -100)
{
//audioMixer.GetFloat();
Launcher.instance.bgmVolume = 10;
Launcher.instance.soundVolume = 10;
}
else
{
audioMixer.SetFloat("bgm1", Launcher.instance.bgmVolume);
audioMixer.SetFloat("eff1", Launcher.instance.soundVolume);
//UI
soundSettingsPanel.transform.GetChild(1).GetComponent<Slider>().value = Launcher.instance.bgmVolume;
soundSettingsPanel.transform.GetChild(2).GetComponent<Slider>().value = Launcher.instance.soundVolume;
}
}
//一个3d音效例子,
//别的类只需要调用AudioManager.instance.Thunder(position)
//即可在position那里释放一个打雷音效。
public void Thunder(Vector3 pos)
{
PlaySound(thunderClip, pos);
}
private void PlaySound(AudioClip clip,Vector3 pos)//在指定地方播放指定3d音效
{
//AudioSource.PlayClipAtPoint(clip, pos, sfxVoluePercent * masterVolumePercent);
AudioSpot spot = GameObject.Instantiate(audioSpot, pos, Quaternion.identity);
spot.PlayClip(clip);
/*
audioSource2D.clip = ShootClip;
audioSource2D.Play();//Pasuse//stop不能同时播放多个,重复播放会打断上一次播放
audioSource2D.PlayOneShot(RelodClip);//不是3D音效
//static
AudioSource.PlayClipAtPoint(ShootClip, pos);//是3D音效但是静态方法不受AudioMixer控制
*/
}
//播放2d音效
private void PlaySound2D(AudioClip clip, float volumeScale = 1)
{
audioSource2D.PlayOneShot(clip, volumeScale);
}
AudioSpot
预制体与AudioSpot.cs
预制体上挂着AudioSpot
脚本,添加一个AudioSource组件,并设其为3D模式。
AudioSpot.cs
public class AudioSpot : MonoBehaviour
{
public void PlayClip(AudioClip clip)
{
GetComponent<AudioSource>().PlayOneShot(clip);
Invoke("DestroyThis", clip.length);
}
public void DestroyThis()
{
Destroy(gameObject);
}
}
总结
完成了3D音效同时受AudioMixer控制的音效系统。
不过当游戏中突发很多音效的时候会不会卡呢?毕竟在瞬时要实例化很多个预制体。不清楚这方面的开销,会不会相当于反向优化游戏呢?
目前知识储备还不太能找到一个完美解决方案,等待后续学习吧。
初入开发Unity,如果这种设计存在缺陷等问题,欢迎留言指正,十分感谢。