unity⾳效插件——FMOD学习
1. Unity内置了Audio功能,并且底层也是⽤FMOD来实现的,为什么还要不辞劳苦学习使⽤FMOD插件来进⾏⾳效的管理?
下⾯是摘录的部分内容:
The main reason to use this is that it enables your sound designers to edit, mix and master your game audio using the FMOD Studio interface while the game is running. Using FMOD studio, you can also create, e.g., a footstep sound event that will randomize the pitch and selected sound sample without the game having to know anything else than the name of the event. Using FMOD is free for non-commercial purposes and the license prices are also pretty reasonable for commercial work.
Most commercial games with decent sound design use such live mixing and centralized sound management approach, either using FMOD, WWise or some custom tools. Unity does use fmod for its own sound, but it does not provide any mixing tools and a sound designer will go insane if he/she has to manage hundreds of sound objects in the Unity editor.
The main limitation of the FMOD Studio integration is that it only supports iOS, Android, Windows, Mac
but no web player. If you want your sounds to work in the web player, I suggest you check out the Clockstone Audio Toolkit that provides mixing and management on top of Unity's internal sound system.
理解起来就是:
Unity内置的Audio内部使⽤的是FMOD,但是功能不够齐全,⾼级⼀点的功能如混合(Mix)等⽆法使⽤;
⾳效管理应该和Unity⼯程解耦合,这样⼦可以减轻⾳效设计师的负担;
使⽤FMOD后,游戏中我们只需要关⼼sound event名字就可以了,对具体⾳效资源不会产⽣依赖;
⽬前FMOD⽀持Windows, Mac OSX, Android, iOS,其实官⽅⽂档中说了对XBOX One,PlayStation系统等系统都有⽀持;结合FMOD Studio的官⽅⽂档,我们可以总结出使⽤FMOD的如下优点:
使⽤FMOD我们可以使⽤更少的资源创建更加⾼级和丰富的⾳效,减少运⾏时内存资源消耗;
⾳效管理只需要在FMOD Studio中管理好即可,不需关⼼具体Unity⼯程,⽅便⾳效管理;
编程⼈员只需要依赖于各种字符串形式的Sound Event和简单的播放API即可,使⽤简单;
平台⽀持较为完善。
2. FMOD的基本使⽤过程
FMOD的使⽤过程⽐较简单,复杂之处在于FMOD Studio的使⽤,⾳效资源编辑完成后的使⽤较为简单。具体流程参见下图:
具体过程如下:
1. 使⽤FMOD Studio编辑器编辑原始⾳效资源,创建各种⾳效;
2. 导⼊Unity Game Project;
3. 通过FMOD Plugin的API进⾏播放。
3. 简单例⼦
FMOD Studio部分:
1. 下载;
2. 打开FMOD Studio即创建⼀个新⼯程,Ctrl + S 确定⼯程保存位置;
3. Window->Audio Bin打开Audio Bin窗⼝,⽤于选择⼯程需要的声⾳⽂件,File->Import Audio Files选择⼯程需要的声⾳⽂件;
4. FMOD Studio中左侧⾯板Event Tab栏中,右击选择New Event,表⽰创建⼀个新的⾳效(下图创建了⼀个test⾳效);
5. 将Audio bin⾯板中的将⾳效⽂件拖到FMOD Studio的Audio 1中,如下图所⽰,拖⼊了两个声⾳资源进去组成了⼀个⾳效:
6. 右击test Event,选择Assign to Bank->Master bank;
7. Ctrl + s,然后File->Build,然后File->Export GUIDs。
8. over
Unity部分:
1. 下载,并导⼊Unity,此时菜单栏会多出⼀个FMOD选项;
2. FMOD->Import Banks,打开刚刚FMOD Studio创建的⼯程的Build⽬录,Import之,Import的资源会在Assets/StreamingAssets ⽬录下;
3. 选择Main Camera,然后选择Component->Scripts->FMOD Listener;
4. 创建⼀个Empty GameObject,然后Create And Add⼀个Script,附加如下代码内容,即可实现播放⾳效:
using UnityEngine;
using System.Collections;
using FMOD.Studio;
public class TestFMOD : MonoBehaviour {
FMOD.Studio.EventInstance testInstance;
string snd = "event:/test";
// Use this for initialization
void Start () {
testInstance = FMOD_StudioSystem.instance.GetEvent(snd);
if (testInstance != null)
{
testInstance.start();
}
}
}
2. 参数详解
FMOD Studio⼯程
FMOD Studio⼯程中,所有的声⾳⽂件都是以Event的形式存在的,这些Event都是通过字符串的形式来进⾏表⽰的;
⽬前对FMOD Studio⼯具的使⽤只是创建⼀个简单的Event,在FMOD Studio的安装⽬录中有⽂档可以参考。
是否是STREAM类型:
⾳效是否loop:
EventInstance参数
position:决定⾳量,距离越远,⾳量越⼩;
velocity:决定⾳⾼,速度越快,⾳量越⼤。
注意点:
需要封装的其实只有两个东西,⼀个是更新EventInstance.updateAttribute⽅法,FMOD的实现在其中不停的获取Transfrom Component,这是效率很低的地⽅,可以提供⼀个只需要Tranform参数的⽅法来供调⽤;另⼀个提供⼀个Play⽅法供外部进⾏调⽤,参数是Event即可。
以前出现的问题:
1. 连续播放多个声⾳的时候,后⾯的声⾳将前⾯的声⾳打断了。初步估计是以为当时的系统中使⽤了对象池,每次都是从对象池中进
⾏取得EventInstance,⽽不是创建新的。实际上如果对同⼀个EventInstance进⾏重复调⽤start函数时,会从头开始重新播放。
例⼦中的声⾳⽂件播放的时长为2.6s,没有出现后⾯的声⾳阻断前⾯的声⾳的情况。
IEnumerator Play2Sound()
{
for(int i=0; i<2; ++i)
{
EventInstance instance = FMOD_StudioSystem.instance.GetEvent(snd);
instance.start();
yield return new WaitForSeconds(1f);
}
}
⽽这个例⼦中,重复的start则会将上次的声⾳打断
IEnumerator Play2Sound()
{
for(int i=0; i<2; ++i)
{
if (null == testInstance)
{
testInstance = FMOD_StudioSystem.instance.GetEvent(snd);
}亲爱的对不起
testInstance.start();
yield return new WaitForSeconds(1f);
}
}
2. ⼀个⾳乐多次重复播放的时候容易出现刺刺的声⾳
泡爷的⽅案是在五帧之内则不允许再次播放,但是我没有遇到这种现象,测试代码如下:
IEnumerator TestLargetScaleSnd()
{
while (true)
{
for(int i=0; i<26; ++i)
{
EventInstance instance = FMOD_StudioSystem.instance.GetEvent(snd);
instance.start();
yield return new WaitForSeconds(0.1f);
}
}
}
3. 在使⽤FMOD的时候,york同学遇到⼀个情况,就是如果设置⼀个回调函数,则在MonoDevelop设置断点时总是crash掉。⽽我
实验的结果是在⼤量播放⼀个声⾳时,如果设置回调函数,则Unity会挂掉。因此暂且总结为不要使⽤EventInstance的callback ⽅法设置回调函数。
实验代码如下(五个声⾳以上同时播放则会挂掉,少则不会):
IEnumerator TestLargetScaleSnd()
{
//while (true)
{
for(int i=0; i<5; ++i)
{
梨花落 霍尊EventInstance instance = FMOD_StudioSystem.instance.GetEvent(snd);
instance.setCallback(callbackFunc);
instance.start();
yield return new WaitForSeconds(0.1f);
}
}
}
RESULT callbackFunc(EVENT_CALLBACK_TYPE type, IntPtr eventInstance, IntPtr parameters)
{
return RESULT.OK;
}
3. 实践总结
FMOD的已知实验总结出以下三点:
1. Unity Project只需要使⽤FMOD.Studio.EvenInstance就可以了,Unity Project中不需要对EventInstance的内存池管理,只需要对它的position属性进⾏管理;
周杰伦全部歌曲下载
2. 我们还需要⼀个封装的接⼝来提供声⾳的播放,这个接⼝为
void Play(string snd);
3. _3D_ATTRIBUTES⽤于设置EventInstance的属性,我们设置的属性⽬前只有position属性,⽽这个属性由于需要随着⾓⾊的移动⽽更新,因此需要频繁调⽤。⽽_3D_ATTRIBUTES的to3DAttributes⽅法每次都要去取GameObject的Transform,消耗较⼤,因此我们需要封装⼀个to3DAttributes(Transform)⽅法,根据缓存的Transform来进⾏。
to3DAttributes⽅法的封装如下:
public class FModUtils
{
static public _3D_ATTRIBUTES to3DAttributes(Transform go)
{
FMOD.Studio._3D_ATTRIBUTES attributes = new FMOD.Studio._3D_ATTRIBUTES();
attributes.forward = FMODVector(go.forward);
attributes.up = FMODVector(go.up);
attributes.position = FMODVector(go.position);
if (go.rigidbody)
attributes.velocity = FMODVector(go.gameObject.rigidbody.velocity);
return attributes;
}
}
总结1和2则通过封装⼀个FModCom即可,需要播放声⾳的GameObject需要挂接这个脚本,并在需要播放声⾳的时候调⽤其中的Play接⼝,代码如下:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using FMOD.Studio;
fun.kl;
public class FMODCom : MonoBehaviour {
夜之歌[HideInInspector]
public float updateTime = 0.5f;
private List<EventInstance> m_soundList;
private Transform m_trans;
void Awake()
{
m_trans = ansform;
m_soundList = new List<EventInstance>();
}爱恨情歌
// Use this for initialization
void Start () {
StartCoroutine(updateSnd());
}
// 播放声⾳接⼝
public void Play(string snd)
{
EventInstance sndEvent = FMOD_StudioSystem.instance.GetEvent(snd);
if (null == sndEvent) return;
sndEvent.3DAttributes(m_trans));
sndEvent.start();
addToUpdateAttribute(sndEvent);
}
IEnumerator updateSnd()
{
while (true)
{
PLAYBACK_STATE state;
//将stopped的⾳效从soundList中删除
//在循环中要删除对象,因此不能⽤foreachtwinkle 歌词
for (int i = 0; i < m_soundList.Count; ++i)
{