Unity 的人物动作模型fbx文件中一般会有一个或多个 Animation 动画,为了配合音效,我们需要给 Animation 添加事件,Unity自带的Inspector可以实现简单的事件编辑,不过缺点是不能预览动作和音效事件的配合。因此可以自己制作工具来实现在运行时的事件编辑器。
事件读取
在加载人物模型之后,可以获取到身上挂的Animator脚本,从而读取到人物所有的AnimationClip:
1
| AnimationClip[] animationClips = CurUnit.animator.runtimeAnimatorController.animationClips;
|
Animation的基本信息有以下内容:
1 2 3
| animationClip.name animationClip.averageDuration animationClip.events
|
新增事件
给指定的animationClip新增一个调用函数为SoundEvent的事件,持续时间为1s,string参数为”hit”。
1 2 3 4 5
| AnimationEvent evt = new AnimationEvent(); evt.functionName = "SoundEvent"; evt.time = float.Parse(1); evt.stringParameter = "hit"; animationClip.AddEvent(evt);
|
修改事件
不能直接修改 animationClip 的 events数组,需要先取出来,修改完再赋值回去。因此编辑时需要自己记住下标eventIndex 。
1 2 3 4 5 6 7
| AnimationEvent[] events = animationClip.events; AnimationEvent evt = new AnimationEvent(); evt.functionName = "SoundEvent"; evt.time = float.Parse(1); evt.stringParameter = "hit"; events[eventIndex] = evt; animationClip.events = events;
|
由于是运行时添加,游戏中会立即增加这个时间,这时播放对于的 animation 动作就可以预览最终效果了。不过这样关闭游戏不会保存到文件,因为数据没有写入到 AssetDatabase。
回退修改
刚才修改了事件,虽然没保存,但是必须重启游戏才能恢复,这样很麻烦。实际上我们可以通过重新导入来实现 Revert 功能。
1
| AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(animationClip));
|
一行代码即可搞定。
保存事件
先尝试Unity官方文档所说的方式:
1
| AnimationUtility.SetAnimationEvents(animationClip, animationClip.events);
|
看起来很容易,一行代码就搞定了,关掉游戏再运行也没问题,可是git却没有检测到任何变化,这是怎么回事?
其实网上有不少吐槽这个接口的讨论了,这个接口只会把信息保存到本地缓存中,并没有真的写入到文件里,所以实际上需要自己来做保存这件事。
保存事件要先知道 animation 的 event 信息记录到哪里,实际上不是在 anim 文件中,而是其对应的 meta 文件中。
因此要想保存到文件,要根据读取的 animationClip 找到 AssetPath ,再找到对应数据的 m_ClipAnimations 标签,这里就是所有的AnimationProperty,最后遍历就可以做我们想做的事情了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| string assetPath = AssetDatabase.GetAssetPath(animationClip); ModelImporter modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
SerializedObject serializedObject = new SerializedObject(modelImporter);
SerializedProperty clipAnimations = serializedObject.FindProperty("m_ClipAnimations"); if (clipAnimations == null || clipAnimations.arraySize == 0) { modelImporter.clipAnimations = modelImporter.defaultClipAnimations; serializedObject = new SerializedObject(modelImporter); clipAnimations = serializedObject.FindProperty("m_ClipAnimations"); } for (int i = 0; i < clipAnimations.arraySize; i++) { SerializedProperty clipAnimationProperty = clipAnimations.GetArrayElementAtIndex(i); if (clipAnimationProperty.displayName == "你要找的animationClip的名字") { SerializedProperty eventsProperty = clipAnimationProperty.FindPropertyRelative("events"); eventsProperty.ClearArray(); SerializedProperty eventProperty = eventsProperty.GetArrayElementAtIndex(index); eventProperty.FindPropertyRelative("time").floatValue = Time2Percent(evt.time); eventProperty.FindPropertyRelative("functionName").stringValue = evt.functionName; eventProperty.FindPropertyRelative("floatParameter").floatValue = evt.floatParameter; eventProperty.FindPropertyRelative("intParameter").intValue = evt.intParameter; eventProperty.FindPropertyRelative("data").stringValue = evt.stringParameter; serializedObject.ApplyModifiedProperties(); AssetDatabase.ImportAsset(assetPath); } }
AssetDatabase.Refresh();
|
总结
通过上面的方法就可以在运行时编辑animation的事件了,这个工具相比于Unity Inspector的功能可以更强大,可以实时预览事件在游戏中的效果,尤其是在编辑音效时,就体现出预览的重要性了。