实现效果
核心功能
支持选定模型(带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,也可能包含动画)
结合动作和特效,实现特效与模型动作的匹配,指定特效延时时间,并且可以同时处理多个特效。(类似技能编辑器的表现)