【Unity】【Wwise】在Unity中获取某个Wwise事件的持续时间
【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
- 需要每隔一段时间或每次音频内容有更新或打包之前,调用一次上面的菜单脚本,保持最新的长度信息
边边角角
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事件的持续时间相关推荐
- Unity在XR设备中获取手柄的按键信息
我们在平常的XR设备开发中,尤其适用VR设备的时候,会用到手柄的操作. 我们知道Oculus SDK提供了OVRInput,能够获取得到手柄的按键信息. // public variable that ...
- Web APIs /APIs --DOM简述/DOM中获取元素方法/事件(含鼠标事件)/操作(含案例)
Web APIs Web APIs 和 JS 的关联性: Web APIs是 W3C 组织的标准,主要学习DOM 和 BOM Web APIs是 JS 所独有的部分 主要学习页面交互功能 Web AP ...
- android全局监听onkeydown,在Fragment中监听onKeyDown事件
在Activity中可以很轻监听到onKeyDown事件,但大部分场景我们的操作是在Fragment中完成的,此时要获取到onKeyDown事件需要多做点事 1.首先在Fragment的宿主Activ ...
- 用 Wwise 和 Unity 制作 DLC 使用 Wwise 文件包(File Package)
用 Wwise 和 Unity 制作 DLC 使用 Wwise 文件包(File Package) https://www.audiokinetic.com/zh/library/edge/?sour ...
- unity片元着色器中获取屏幕坐标_Unity踩坑笔记(持续更新)
1.error CS0104: 'MinAttribute' is an ambiguous reference between 'UnityEngine.Rendering.PostProcessi ...
- Unity中获取一个物体下所有的子物体的方法
Unity中获取一个物体下所有的子物体的方法 方法1(获取全部子物体,无论子物体SetActive是否为true): using System.Collections; using System.Co ...
- Unity中获取本机IP地址的方法
做OptiTrack局域网数据通信时,需要设置本地IP和动捕数据服务器IP,来实现获取动捕数据.由于局域网搭建时需要手动设置电脑IP,因此想到如果可以获取本机IP,服务器IP通常设置固定后不会更改,如 ...
- 【100个 Unity踩坑小知识点】| Unity调用API ,动态获取Android权限,附带所有Android权限表格
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 . 包括游戏开发.美术.建筑.汽车设计.影视在内的所有创作者,借助 Unity 将创意 ...
- 【Unity Editor编辑器】 代码获取project面板选中资源路径(自定义右键菜单)
在Unity编辑器中,如果想要快捷的获取到Project面板中选中文件的路径,比如我们需要用Resources.Load的方式加载一个prefab,就需要知道这个prefab的路径,或者在自定义窗口中 ...
最新文章
- python获取maco句柄_python之subprocess模块
- 转载:概率与梳理统计||数学基础
- 有关 input默认宽度
- 修改 decimal 默认值为0.00 sql_书写高性能SQL语句技巧,网友都说好
- javaweb课程PSP(1)
- multiprocessing.manager管理的对象需要加锁吗_iOS内存管理布局及管理方案理论篇
- PTA 程序设计天梯赛(81~100题)
- kx3552驱动最佳连线图_意甲新赛季5大看点:C罗连线苏牙,皮尔洛执教初体验
- Spring AOP/DI/IOC 简述及使用
- PAT-两个数的简单计算器(简单编程题)
- 神奇的暴力数据结构——ODT
- [题解][CF-1292C]Xenon‘s Attack on the Gangs
- EditPlus格式化xml文档
- 啊哈添柴挑战Java1222. 输出菱形
- Lecture 13: Bernoulli Process
- NFC-PN532串口驱动编写
- Chino with Train to the Rabbit Town
- 2022年从零开始,用一篇博客掌握 nginx 的初级配置
- PDF417 (二维码)
- 冻结步态你了解多少呢?