实现效果
核心功能
- 支持选定模型(带Animator)在非运行模式下,播放/暂停/停止动作。
- 支持动作单帧前进,单帧回退(帧时间默认0.05f,可以代码设置)。
- 支持滚动条拖拽,将动作调整到指定时间。
- 支持调整播放速度。
其他功能
- 支持循环播放。
- 支持选中Animator下的任一动作。
- 编辑器关闭时,会删除中间产生的临时GameObject
- 选中Project中的Prefab,如果符合条件,将会自己创建一份Clone到Hierarchy中。
效果图
实现原理
整体思路
- 获取指定GameObject上的Animator,并记录其所有的AniamtionClip。
- 模拟播放时间,通过AnimationMode.SampleAnimationClip方法设置播放位置。
- 添加进度条,单帧执行等方式修改播放时间。
注意细节
- 如果模型勾选了OptimizeGameObject选项,优化骨骼,那么动作可能会因为找不到骨骼而无法播放。需要使用AnimatorUtility.DeoptimizeTransformHierarchy方法,暂时屏蔽骨骼优化。
- 在使用AnimationMode.SampleAnimationClip方法时,参数的GameObject应该设置为Animator所对应的GameObject而不是根节点。
- 因为不是运行状态,所以Update需要由EditorApplication.update提供。(工具关闭时,需要反注册)
- OnGUI方法不是每帧都执行,当使用滚动条( Slider )和进度条(ProgressBar)时,显示不会及时更新,所以当数据更新时,需要调用Repaint方法,强制刷新UI。
- EditorWindow下,OnSelectionChange方法可以用来监听选择变化。但是在这个方法内部,修改当前选中的对象(Selection的属性)无效。
- ProgressBar不能使用Layout,需要自己设置Position。在Layout中获取上一个Rect的位置,然后计算ProcessBar的位置,实现方式:
if(Event.current.type == EventType.Repaint)
{
var lastRect = GUILayoutUtility.GetLastRect();
EditorGUI.ProgressBar(new Rect(lastRect.x, lastRect.y + lastRect.height, lastRect.width, 20), animationCtrl.escapeTime / animationCtrl.length, "");
}
存在疑问
- AnimationMode文档中说在使用 SampleAnimationClip时需要 StartAnimationMode,但没有调用,也是可以正常运行。BeginSampling,EndSampling作用什么?不加也可以正常运行。
- 在使用AnimatorUtility.OptimizeTransformHierarchy去还原模型时,需要知道exposedTransforms即需要保留的骨骼。但是没有找到获取exposedTransforms的方法,所以GameObject在被DeoptimizeTransformHierarchy之后无法还原。一种的思路是在调用DeoptimizeTransformHierarchy之前记录子Transform,并且去掉包含SkinnedMeshRender的Transform,使用这些Transform来当做exposedTransforms。但是这种方式不精确,对模型有限制。
ToDo
- 倒播。修改时间变化方式,添加符号判断。
- ParticleSystem.Simulate可以在非运行模式下制定特效播放到制定时间,可以用这个方法实现一套特效的播放回退机制。(特效比较复杂可能包含多个ParticleSystem,也可能包含动画)
- 结合动作和特效,实现特效与模型动作的匹配,指定特效延时时间,并且可以同时处理多个特效。(类似技能编辑器的表现)