【Unity】【Wwise】在Unity中获取某个Wwise事件的持续时间

  • 解决方案
  • 注意事项
  • 边边角角
  • 小吐槽

有一次接到这么一个需求:要在界面上显示出一段人物语音的长度,并且在播放的时候进行倒计时。当时看到这个需求的时候,心想就直接调官方提供的函数获取事件长度呗,但在做的时候就发现,自己天真了。

在官方提供的函数接口中(AkSoundEngine类),我翻来覆去找过很多次,并没有获取某个事件的持续时间的函数,只能自己想方设法的获取该信息,那从哪里能拿到呢?

当时的情况是,我们项目会在Timeline中使用Wwise事件来配置音频,在Timeline中,事件是可以看到长度的。(事件名涂掉了,放置泄露项目信息,虽然我也不知道为什么我要这么谨慎。。)


顺藤摸瓜找对应脚本看看,怎么得到事件长度的。

在项目中搜索AkTimelineEventPlayable脚本,可以发现是通过AkUtilities.GetEventDurations委托来获取的,最终这个委托是:


找到了?不要急,看脚本文件的最上面,该脚本内容只可用于UnityEditor下,在最终打包后是无法调用的,包括AkWwiseProjectInfo脚本也是。

但既然在UnityEditor下可以获取,那我自己取来复制一份在项目中就可以了呗。

那么,解决方案就来了~

解决方案

直接上代码

  • WwiseInfoCollector.cs,编辑器下的菜单脚本
[MenuItem("WWISE/Collect Event Duration")]
private static void GenerateCharacterVoiceInfo()
{// NOTE 此处读取资源的类和方法是我们项目封装的,须自行替换。// 读取判断是否已有.asset资源,若没有,则创建ScriptWwiseInfo wwiseInfo = AssetLibrary.LoadAsset<ScriptWwiseInfo>(WwiseInfoHolder.WwiseInfoDataPath, true);if (wwiseInfo == null){wwiseInfo = ScriptableObject.CreateInstance<ScriptWwiseInfo>();AssetDatabase.CreateAsset(wwiseInfo, WwiseInfoHolder.WwiseInfoDataPath);}var wwiseProjectData = AkWwiseProjectInfo.GetData();// 转存信息wwiseInfo.EventInfos = new List<WwiseEvent>();foreach (var wwu in wwiseProjectData.EventWwu){// 此处是根据事件的路径规则,只对人物语音部分进行转存,自行替换规则或删去该判断以用于所有事件。if (wwu.ParentPath.Contains("Character")){foreach (var wwuEvent in wwu.List){wwiseInfo.EventInfos.Add(new WwiseEvent(){Id = wwuEvent.Id,Name = wwuEvent.Name,DurationMin = wwuEvent.minDuration,DurationMax = wwuEvent.maxDuration,});}break;}}// 保存.asset文件EditorUtility.SetDirty(wwiseInfo);AssetDatabase.SaveAssets();
}
  • WwiseInfoHolder.cs,运行时获取事件长度使用的脚本。其中有一个叫PlayNormalPrefix的变量,是我们项目里面播放音频事件的统一前缀,不是必须(其他可能还有Stop_前缀的事件等等)。实现分别使用字符串(事件名)和uint(事件id,可从Wwise_IDs脚本中获取,具体查看下方边边角角第1条)两种参数的查询方法。
public class WwiseInfoHolder
{public const string WwiseInfoDataName = "WwiseEventDurations.asset";public static readonly string WwiseInfoDataPath = "Assets/Wwise/ScriptableObjects/" + WwiseInfoDataName;private readonly ScriptWwiseInfo _wwiseInfo;private const string PlayNormalPrefix = "Play_";public WwiseInfoHolder(){// NOTE 此处读取资源的类和方法是我们项目封装的,须自行替换。_wwiseInfo = AssetLibrary.LoadAsset<ScriptWwiseInfo>(WwiseInfoDataPath, true);if (_wwiseInfo == null)WwiseLogger.LogMessage(WwiseLogger.LogLevel.Warning, "WwiseInfoData load failed.");}/// <summary>/// Get sound duration.Only for character voice currently./// </summary>/// <param name="soundName"></param>/// <param name="durationMin"></param>/// <param name="durationMax"></param>public bool TryGetCharacterVoiceDuration(string soundName, out float durationMin, out float durationMax){durationMin = -1;durationMax = -1;if (_wwiseInfo == null){WwiseLogger.LogMessage(WwiseLogger.LogLevel.Warning, "WwiseInfoData was not loaded.");return false;}var eventName = PlayNormalPrefix + soundName;var result = _wwiseInfo.EventInfos.Find((wwiseEvent) => wwiseEvent.Name.ToLower() == eventName.ToLower());if (result == null){WwiseLogger.LogMessage(WwiseLogger.LogLevel.Warning,string.Format("Didn't find duration of event named <{0}>", eventName));return false;}durationMin = result.DurationMin;durationMax = result.DurationMax;return true;}/// <summary>/// Get sound duration.Only for character voice currently./// </summary>/// <param name="eventId">Wwise event id, defined in <see cref="AK.EVENTS"/></param>/// <param name="durationMin"></param>/// <param name="durationMax"></param>public bool TryGetCharacterVoiceDuration(uint eventId, out float durationMin, out float durationMax){durationMin = -1;durationMax = -1;if (_wwiseInfo == null){WwiseLogger.LogMessage(WwiseLogger.LogLevel.Warning, "WwiseInfoData was not loaded.");return false;}var result = _wwiseInfo.EventInfos.Find((wwiseEvent) => wwiseEvent.Id == eventId);if (result == null){WwiseLogger.LogMessage(WwiseLogger.LogLevel.Warning,string.Format("Didn't find event duration with id <{0}>", eventId));return false;}durationMin = result.DurationMin;durationMax = result.DurationMax;return true;}
}
  • ScriptWwiseInfo.cs,用于生成.asset文件的数据结构脚本。
[Serializable]
public class ScriptWwiseInfo : ScriptableObject
{public List<WwiseEvent> EventInfos;
}[Serializable]
public class WwiseEvent
{public uint Id;public string Name;public float DurationMin;public float DurationMax;
}

注意事项

  • 有的事件是循环播放类型的,上述方法不可用于循环播放类型
  • 非播放类型的事件持续时长为0
  • 需要每隔一段时间或每次音频内容有更新或打包之前,调用一次上面的菜单脚本,保持最新的长度信息

边边角角

  1. uint型事件ID

    通过官方提供的方法来看(比如AkSoundEngine.Post()),有参数为uint类型的重载方法,标识事件的uint型ID是存于导出内容的Wwise_IDs.h脚本中的,可用于虚幻引擎。如果要用于Unity,需要进行转换。

    在Unity菜单栏Assets>Wwise>Convert Wwise SoundBank IDs,但是这个方法总是需要去手选目录,然后将生成的脚本放入项目。所以直接在IDE中搜索他的菜单项名字,找到其转换的脚本AkWwiseIDConverter,将转换的函数抄过来,写一个直接根据路径读取.c文件转换到.cs并存于项目的函数。

private static readonly string s_converterScript = Path.Combine(Path.Combine(Path.Combine(Application.dataPath, "Wwise"), "Tools"),"WwiseIDConverter.py");private static readonly string s_progTitle = "WwiseUnity: Converting SoundBank IDs";[MenuItem("WWISE/Convert Wwise_Ids.h into C# and replace")]
public static void ConvertWwiseId()
{var wwiseAssetsPath = Path.Combine(Application.streamingAssetsPath, AkWwiseEditorSettings.Instance.SoundbankPath);var bankIdHeaderPath = Path.Combine(wwiseAssetsPath, "Wwise_IDs.h");if (string.IsNullOrEmpty(bankIdHeaderPath)){Debug.Log("WwiseUnity: User canceled the action.");return;}var start = new System.Diagnostics.ProcessStartInfo();start.FileName = "python";start.Arguments = string.Format("\"{0}\" \"{1}\"", s_converterScript, bankIdHeaderPath);start.UseShellExecute = false;start.RedirectStandardOutput = true;var progMsg = "WwiseUnity: Converting C++ SoundBank IDs into C# ...";EditorUtility.DisplayProgressBar(s_progTitle, progMsg, 0.5f);using (var process = System.Diagnostics.Process.Start(start)){process.WaitForExit();try{//ExitCode throws InvalidOperationException if the process is hangingif (process.ExitCode == 0){EditorUtility.DisplayProgressBar(s_progTitle, progMsg, 1.0f);Debug.Log(string.Format("WwiseUnity: SoundBank ID conversion succeeded. Find generated Unity script under {0}.", bankIdHeaderPath));ReplaceOldWwiseIDsFile(wwiseAssetsPath);}elseDebug.LogError("WwiseUnity: Conversion failed.");AssetDatabase.Refresh();}catch (Exception ex){AssetDatabase.Refresh();EditorUtility.ClearProgressBar();Debug.LogError(string.Format("WwiseUnity: SoundBank ID conversion process failed with exception: {0}. Check detailed logs under the folder: Assets/Wwise/Logs.",ex));}EditorUtility.ClearProgressBar();}
}private static void ReplaceOldWwiseIDsFile(string wwiseAssetsPath)
{var bankIdCsharp = "Wwise_IDs.cs";var bankIdCSharpPath = Path.Combine(wwiseAssetsPath, bankIdCsharp);var wwisePackagePath = Path.Combine(Path.Combine(Application.dataPath, "Wwise"), bankIdCsharp);File.Replace(bankIdCSharpPath, wwisePackagePath, null);
}

小吐槽

Wwise资料真的少,官方文档倒是有,但真要解决个什么问题,网上都找不到方案,当然,也可能是我搜索的姿势不对。。

【Unity】【Wwise】在Unity中获取某个Wwise事件的持续时间相关推荐

  1. Unity在XR设备中获取手柄的按键信息

    我们在平常的XR设备开发中,尤其适用VR设备的时候,会用到手柄的操作. 我们知道Oculus SDK提供了OVRInput,能够获取得到手柄的按键信息. // public variable that ...

  2. Web APIs /APIs --DOM简述/DOM中获取元素方法/事件(含鼠标事件)/操作(含案例)

    Web APIs Web APIs 和 JS 的关联性: Web APIs是 W3C 组织的标准,主要学习DOM 和 BOM Web APIs是 JS 所独有的部分 主要学习页面交互功能 Web AP ...

  3. android全局监听onkeydown,在Fragment中监听onKeyDown事件

    在Activity中可以很轻监听到onKeyDown事件,但大部分场景我们的操作是在Fragment中完成的,此时要获取到onKeyDown事件需要多做点事 1.首先在Fragment的宿主Activ ...

  4. 用 Wwise 和 Unity 制作 DLC 使用 Wwise 文件包(File Package)

    用 Wwise 和 Unity 制作 DLC 使用 Wwise 文件包(File Package) https://www.audiokinetic.com/zh/library/edge/?sour ...

  5. unity片元着色器中获取屏幕坐标_Unity踩坑笔记(持续更新)

    1.error CS0104: 'MinAttribute' is an ambiguous reference between 'UnityEngine.Rendering.PostProcessi ...

  6. Unity中获取一个物体下所有的子物体的方法

    Unity中获取一个物体下所有的子物体的方法 方法1(获取全部子物体,无论子物体SetActive是否为true): using System.Collections; using System.Co ...

  7. Unity中获取本机IP地址的方法

    做OptiTrack局域网数据通信时,需要设置本地IP和动捕数据服务器IP,来实现获取动捕数据.由于局域网搭建时需要手动设置电脑IP,因此想到如果可以获取本机IP,服务器IP通常设置固定后不会更改,如 ...

  8. 【100个 Unity踩坑小知识点】| Unity调用API ,动态获取Android权限,附带所有Android权限表格

    Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 . 包括游戏开发.美术.建筑.汽车设计.影视在内的所有创作者,借助 Unity 将创意 ...

  9. 【Unity Editor编辑器】 代码获取project面板选中资源路径(自定义右键菜单)

    在Unity编辑器中,如果想要快捷的获取到Project面板中选中文件的路径,比如我们需要用Resources.Load的方式加载一个prefab,就需要知道这个prefab的路径,或者在自定义窗口中 ...

最新文章

  1. python获取maco句柄_python之subprocess模块
  2. 转载:概率与梳理统计||数学基础
  3. 有关 input默认宽度
  4. 修改 decimal 默认值为0.00 sql_书写高性能SQL语句技巧,网友都说好
  5. javaweb课程PSP(1)
  6. multiprocessing.manager管理的对象需要加锁吗_iOS内存管理布局及管理方案理论篇
  7. PTA 程序设计天梯赛(81~100题)
  8. kx3552驱动最佳连线图_意甲新赛季5大看点:C罗连线苏牙,皮尔洛执教初体验
  9. Spring AOP/DI/IOC 简述及使用
  10. PAT-两个数的简单计算器(简单编程题)
  11. 神奇的暴力数据结构——ODT
  12. [题解][CF-1292C]Xenon‘s Attack on the Gangs
  13. EditPlus格式化xml文档
  14. 啊哈添柴挑战Java1222. 输出菱形
  15. Lecture 13: Bernoulli Process
  16. NFC-PN532串口驱动编写
  17. Chino with Train to the Rabbit Town
  18. 2022年从零开始,用一篇博客掌握 nginx 的初级配置
  19. PDF417 (二维码)
  20. 冻结步态你了解多少呢?

热门文章

  1. Seq2Seq 粗浅理解
  2. Measurement Studio​ 2019 R3
  3. Python 练习实例100例—3
  4. 「译」Web安全快速入门
  5. Dell电脑Fn与功能键的切换
  6. Webpack——ES6转ES5
  7. 四十六、Fluent壁面函数的选取依据
  8. 写给自己的Java程序员学习路线图
  9. 32岁医生放弃医院编制,转行去做程序员!
  10. bytearray函数