https://blog.csdn.net/UWA4D/article/details/106091666
fmod的音频有两种引入的模式,一个是streaming的方式,另外一个是assetbundle的方式。
第一种方式:Streaming Assets形式
我们将制作好的bank,导入到unity之后,在设置中:
选择:streaming assets
这是最通常的做法,fmod在运行的时候或者是Refresh bank的时候,会将bank拷贝到streaming文件夹下:
那么unity在何时加载呢?
代码在:
RuntimeManager——>FMOD.RESULT Initialize()——>LoadBanks
这样就将所有的bank加载到内存了。
第二种方式:Asset Bundle
当选择Asset Bundle的时候,fmod会自动将我们的Build Path下的bank,重新拷贝一份到当前的项目中,但是每个bank的后缀都更改为.bytes了。
这是因为unity打包的时候,对unity之外的一些文件后缀是不识别的,比如这里.bank。我记得lua也是要改为.txt才能进行打包。
ok,我们有了bytes文件,就好打包了。我们准备将其中的一个bytes文件,进行打包:
打包代码:
public class BuildBundle
{
[MenuItem("ArtTool/Build Single Bank")]
public static void BuildSingleBank()
{
AssetBundleBuild[] abs = new AssetBundleBuild[1];
abs[0].assetBundleName = "Tone.bundle";
abs[0].assetNames = new string[1];
abs[0].assetNames[0] = @"Assets/Desktop/bytes/Tone.bytes";
string outpath = Application.streamingAssetsPath + "/Desktop/bytes";
if(!Directory.Exists(outpath))
{
Directory.CreateDirectory(outpath);
}
BuildPipeline.BuildAssetBundles(outpath, abs, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.Android);
AssetDatabase.Refresh();
}
}
下面就是加载:
public void LoadBank()
{
//RuntimeManager.LoadBank();
AssetBundle ab = AssetBundle.LoadFromFile(Application.persistentDataPath + "/tone.bundle");
UnityEngine.Object obj = ab.LoadAsset("Tone"); // 无类型&无后缀加载,能加载到
UnityEngine.Object[] objs = ab.LoadAllAssets(); // 加载所有资源,能加载到
TextAsset txtAsset = ab.LoadAsset<TextAsset>("Tone"); // 有类型&无后缀,能加载到
TextAsset txtAsset2 = ab.LoadAsset<TextAsset>("Tone.bytes"); //有类型&有后缀,能加载到
RuntimeManager.LoadBank(txtAsset);
string[] names = ab.GetAllAssetNames();
for (int i = 0; i < names.Length; ++i)
{
Debug.LogError("name = " + names[i]);
}
TextAsset txtAsset3 = ab.LoadAsset<TextAsset>(names[0]); //这个names[0]=Assets/Desktop/bytes/Tone.bytes,其在tone.bundle.manifest中能找到
if (txtAsset3 != null) //经实验,能加载到
{
Debug.LogError("well done!");
}
}
其中:LoadBank(TextAsset asset, bool loadSamples = false)在RuntimeManager中:
public static void LoadBank(TextAsset asset, bool loadSamples = false)
{
……
}
ok,在pc上测试成功;如果是手机上,我们如果不是下载的bundle文件的话,也好模拟(android手机),只要打包之后,找到包路径,将其bundle文件拷贝到持久化目录下,然后加载,经过测试也是成功的。成功的判断标准是,在未加载之前,播放Tone包里面的一个音频,是听不到声音的,并且报错事件找不到;但是在加载Tone之后,再次进行播放,则播放出声音了。
这是我拷贝手动拷贝到手机安装包里的bundle文件:
pc上和手机上加载的代码是不变的,路径都是Application.persistentDataPath。
第三种,如果是以原生的bank的方式,直接拷贝到Application.persistentDataPath,下如何加载呢?
就是我们不打包,直接进行bank的加载,只是此时bank在持久化的目录下而已。
我同样做了实验,将一个原始Tone.bank直接拷贝到Application.persistentDataPath下,然后用下面的函数加载:
public static void LoadBank()
{
LoadedBank loadedBank = new LoadedBank();
string bankPath = Application.persistentDataPath + "/Tone.bank";
FMOD.RESULT loadResult = Instance.studioSystem.loadBankFile(bankPath, FMOD.Studio.LOAD_BANK_FLAGS.NORMAL, out loadedBank.Bank);
}
经过测试,加载争取。
综上,无论是Streaming Assets还是Asset Bundle,还是原生的bank只是拷贝到Application.persistentDataPath之后,都是可以正常加载的。
如果是考虑热更新的话,我是这样想的:
1、同名的bank,优先加载Application.persistentDataPath下的bank
2、音频制作方面,需要制作两份Master Bank,一个是在Streaming下的,一个是在Application.persistentDataPath下,如果是同名的bank,遵循规则1,这样就只加载一份的bank了。
3、也可是Streaming的不改,如果是新添加的音频,那么则直接在新的bank中,此bank热更到Application.persistentDataPath下。程序一运行,就直接读取两个目录下的所有的bank即可。
4、如果bank过大,考虑使用loadSampleData?这个没怎么研究,后面再补充下。结合profie分析下fmod的内存占用情况。还可以进一步的优化,进行bank的卸载:UnloadBank方法。
public static void UnloadBank(string bankName)
{
LoadedBank loadedBank;
if (Instance.loadedBanks.TryGetValue(bankName, out loadedBank))
{
loadedBank.RefCount--;
if (loadedBank.RefCount == 0)
{
loadedBank.Bank.unload();
Instance.loadedBanks.Remove(bankName);
return;
}
Instance.loadedBanks[bankName] = loadedBank;
}
}
5、判断以事件是否在bank中:
public static bool CheckEventInBank(string bankName, string eventName)
{
if (Instance.loadedBanks.ContainsKey(bankName))
{
LoadedBank bank = Instance.loadedBanks[bankName];
FMOD.Studio.EventDescription[] eventList;
var result = bank.Bank.getEventList(out eventList);
if (result == FMOD.RESULT.OK)
{
foreach (var eventDesc in eventList)
{
string path;
eventDesc.getPath(out path);
if (path.Equals(eventName)) return true;
}
}
}
return false;
}
ok,至此,fmod的音频打包,加载介绍完毕,后续在补充实际项目中遇到的问题。