在UNITY中使用FMOD插件,直接控制音乐,音效。
FMOD下载
FMOD STUDIO的版本与插件要保持一致。
UNITY引入package
FMOD - Edit Setting 3个选项:Project,Single Platform Build,Multiple Platform Build
Project路径指定FMOD工程,其余2个路径指定FMOD导出的bank所在文件夹。
可以指定UNITY工程外路径,如果需要多人同步也可以用Assets\路径 (Mac下用 [/])
指定后插件会自动在Assets/StreamingAssets将bank导入。
注意:导出安卓工程时,FMOD的BANK文件没有被加密放在assets\bin文件夹内,而是直接在assets文件夹下
Live Update选项Enabled时,可以直接在FMOD STUDIO中调试音乐音效。
UNITY内代码部分:
using UnityEngine;
using System;
using System.Collections.Generic;
using FMOD.Studio;
using FMODUnity;
public class SoundManager <T>: MonoBehaviour where T: Component{
protected static T _instance;
public static T instance {
get {
if (_instance.IsNull())
_instance = FindObjectOfType<T>();
return _instance;
}
}
public static bool DoesInstanceExist {
get {
return instance != null;
}
}
public BGMPathDefinition[] BGMPathDefinitions;
protected Dictionary<BGMType, string> BGMPathDict;
protected BGMType currentAmbientAudio;
//一个声音实例,可控制。
//循环播放的可以创建一个,要手动release, OneShot 为true播放完自动release.
//http://www.fmod.org/docs/content/generated/studio_api_EventInstance.html
//http://www.fmod.org/docs/content/generated/FMOD_Studio_EventDescription_IsOneshot.html
protected EventInstance BGMEvent;
用来读取声音在工程里面的设置信息,用不到
http://www.fmod.org/docs/content/generated/studio_api_EventDescription.html
//private EventDescription musicDescription;
protected Dictionary<string, EventInstance> loopingSoundEvents = new Dictionary<string, EventInstance>();
protected List<EventInstance> pausedSoundEvents;
protected const int minPathLength = 7;
protected virtual void Awake() {
_instance = GetComponent<T>();
BGMPathDict = new Dictionary<BGMType, string>();
foreach (var ambientAudioDefinition in BGMPathDefinitions) {
BGMPathDict.Add(ambientAudioDefinition.ambientAudioType, ambientAudioDefinition.path);
}
}
protected virtual void OnDestroy() {
StopAndReleaseAll();
if (BGMEvent != null) {
BGMEvent.stop(STOP_MODE.IMMEDIATE);
BGMEvent.release();
}
_instance = null;
}
public virtual void StopAndReleaseAll() {
foreach (var sound in loopingSoundEvents.Values) {
if (sound == null)
continue;
sound.stop(STOP_MODE.IMMEDIATE);
sound.release();
}
loopingSoundEvents.Clear();
}
public virtual void pauseAll() {
pausedSoundEvents = new List<EventInstance>();
foreach (var loopingInstance in loopingSoundEvents.Values) {
if (loopingInstance == null)
continue;
PLAYBACK_STATE state;
loopingInstance.getPlaybackState(out state);
if (state == PLAYBACK_STATE.PLAYING
|| state == PLAYBACK_STATE.STARTING) {
loopingInstance.setPaused(true);
pausedSoundEvents.Add(loopingInstance);
}
}
PauseBGM(true);
}
public virtual void resumeAll() {
PauseBGM(!GameController.Instance.MusicEnable);
if (!GameController.Instance.SFXEnable || pausedSoundEvents == null)
return;
foreach (var pausedSound in pausedSoundEvents) {
pausedSound.setPaused(false);
}
}
#region BGM
public virtual void PlayBGM(BGMType type) {
currentAmbientAudio = type;
if (GameController.Instance.MusicEnable) {
CheckBGMEventInstance();
}
}
public virtual void PauseBGM(bool pause) {
if (BGMEvent == null)
CheckBGMEventInstance();
if (BGMEvent != null)
BGMEvent.setPaused(pause);
}
protected void CheckBGMEventInstance() {
if (BGMPathDict.ContainsKey(currentAmbientAudio)) {
//停止之前的,最好可以通过参数变化背景音乐
if (BGMEvent != null) {
BGMEvent.stop(STOP_MODE.IMMEDIATE);
BGMEvent.release();
}
BGMEvent = RuntimeManager.CreateInstance(BGMPathDict[currentAmbientAudio]);
BGMEvent.start();
}
}
#endregion
#region SFX
/// <summary>
/// 播放一次,不能改变任何参数
/// </summary>
public virtual void PlaySoundOnce(string sound) {
if (!GameController.Instance.SFXEnable || string.IsNullOrEmpty(sound))
return;
if (sound.Length < minPathLength) {
Logger.Warning("Wrong Sound Path:" + sound);
return;
}
RuntimeManager.PlayOneShot(sound);
}
/ <summary>
/ 目标位置播放一次(位置不变)
/ </summary>
//public void PlaySoundOnce(string Event, Vector3 position) {
// if (GameController.Instance.SFXEnable)
// RuntimeManager.PlayOneShot(Event, position);
//}
/ <summary>
/ 播放一次,位置在attach上(跟着attach移动)
/ </summary>
//public void PlaySoundOnce(string Event, GameObject attach) {
// if (GameController.Instance.SFXEnable)
// RuntimeManager.PlayOneShotAttached(Event, attach);
//}
/// <summary>
/// 循环播放,要Fmod工程内OneShot为false
/// </summary>
public virtual EventInstance PlayLoopSounds(string sound) {
if (!GameController.Instance.SFXEnable || string.IsNullOrEmpty(sound))
return null;
if (sound.Length < minPathLength) {
Logger.Warning("Wrong Sound Path:" + sound);
return null;
}
if (HavaEventInstance(sound)) {
loopingSoundEvents[sound].start();
return loopingSoundEvents[sound];
}
var newInstance = RuntimeManager.CreateInstance(sound);
newInstance.start();
loopingSoundEvents[sound] = newInstance;
return newInstance;
}
/// <summary>
/// 暂停指定循环音效
/// </summary>
public bool PauseSound(string sound, bool pause = true) {
if (HavaEventInstance(sound)) {
var result = loopingSoundEvents[sound].setPaused(pause && GameController.Instance.SFXEnable);
return result == FMOD.RESULT.OK;
}
return false;
}
/// <summary>
/// 停止指定循环音效,并释放
/// </summary>
public bool StopSound(string sound) {
if (HavaEventInstance(sound)) {
var result = loopingSoundEvents[sound].stop(STOP_MODE.IMMEDIATE);
loopingSoundEvents[sound].release();
loopingSoundEvents.Remove(sound);
return result == FMOD.RESULT.OK;
}
return false;
}
protected bool HavaEventInstance(string sound) {
return !string.IsNullOrEmpty(sound) && loopingSoundEvents.ContainsKey(sound) && !loopingSoundEvents[sound].IsNull();
}
#endregion
//void OnApplicationPause(bool didPause) {
// if (didPause) {
// pauseAll();
// }
// else {
// resumeAll();
// }
//}
void OnApplicationFocus(bool focus) {
if (focus) {
resumeAll();
}
else {
pauseAll();
}
}
}
[Serializable]
public class BGMPathDefinition {
public BGMType ambientAudioType;
[EventRef]
public string path;
}
public enum BGMType {
Mainmenu = 0,
IngameRunning = 20,
}
FMOD STUDIO的版本与插件要保持一致。
FMOD STUDIO的版本与插件要保持一致。