Unity的新动画系统Mecanim出来也有一阵子了,我的第一个Unity项目就是使用这个动画系统。但是只是做了最基本的应用,没有做深入的研究。
Mecanim的好处有三点:1、动画重定向,可以最大程度的复用动画资源 2、可视化编辑的状态机 3、Blend Tree可以方便的做动画融合,例如一边跑一边攻击。
但是对于我而言,Mecanim在使用上感觉有些蹩脚,最大的问题就是在可视化的状态机编辑上了。 这个状态机让动画编辑变得容易了,不需要一行代码就可以控制动作的流转,像Temple Run这样的游戏可以轻松搞定。 但是问题是,一个大型的mmo动画文件成百上千,如果都依托于这个编辑器的话,那状态机会变得像蜘蛛网一样密密麻麻,不可维护。更何况,连几千条线来处理动画,只有脑子抽疯的人才会这么干。
举个例子,人物有站立动画,拿不同武器的时候站立动作不一样,比如双持站立、空手站立、拿枪站立、拿弓站立等等;同样,跑步的动画也有十几种;死亡动画亦然。最后再加上几十个技能动画。这里我们不考虑骑马、马上攻击、飞行等等情况,可以算是简化的ARPG,但是即便如此,动画文件依然可能有一百多个。如果是按正常情况的话,光站立动作到技能就可能需要一千条线,每条线还要选择好正确的参数。正常人都应该意识到,这是不合理的。
解决方案一:
使用Mecanim Control插件,这个插件使用Mecanim系统提供的接口来模拟老式动画系统的接口(Play CrossFade等等)。把动画文件(Animation Clip)拖动到脚本上,然后就可以正常的使用Play等接口来进行动画播放了。由于内部依然使用的是Mecanim系统,所以支持动画重定向。如果没有什么特殊需求的话,这个应该是相对完善的解决方案。
要说缺点的话,也有一些,就是动画文件的流转完全交由代码来控制,并且不支持BlendTree动画融合。
解决方案二:
这个我还在初步尝试,理论上可行。 这个方案完全依托于Mecanim的操作方式和思路。只不过通过一些小技巧和没有广为流传的知识点使得Mecanim的编辑操作大大简化。
Mecanim 的状态机之所以变得复杂是因为多个站立动作与跑步动作以及技能之间的流转,这需要添加上千条线,而之所以会变得那么多,是因为站立动作本身就有十几个。如果有一种方法能够让十几个休闲动作作为一个整体去跟技能动作进行交互,那么连线数目直接降至几十条。 事实上,在Unity5里面会提供State Machine到State Machine之间的连线交互,每个Sub State Machine都有一个入口和出口,这就是我想要的。 可以再Unity4里面,Sub State Machine只是一个类似文件夹的概念,我们只能处理State之间的连线。
第一个知识点,通过animator.runtimeAnimatorController动态的替换动画控制器,通过AnimatorOverrideController动态的替换一个动画文件。 这个技术就是Mecanim Control插件所使用的。所以,我们只需要在状态机中添加一个Idle状态,然后控制这个状态和技能之间的动作流转。在代码中根据装备的武器动态的改变Idle的对应动画,然后就可以达到我们的目的了。参考代码如下:
private Animator animator; public AnimationClip clip; // Use this for initialization void Start () { animator = GetComponent<Animator>(); AnimatorOverrideController col = new AnimatorOverrideController(); col.runtimeAnimatorController = animator.runtimeAnimatorController; col["2HAxe1"] = clip; animator.runtimeAnimatorController = col; }
参考代码如下:
using UnityEngine; using UnityEditor; using UnityEditorInternal; using System.Collections; public class AutoCreateAnimator : Editor { [MenuItem("Tools/创建动画控制器")] static void CreateAnimator() { //创建animationController文件,保存在Assets路径下 AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath("Assets/animation.controller"); //得到它的Layer, 默认layer为base 你可以去拓展 AnimatorControllerLayer layer = animatorController.GetLayer(0); //把动画文件保存在我们创建的AnimationController中 AddStateTransition("Assets/Model/Media/player/human_f/attack/human_f_2haxe1.fbx",layer); AddStateTransition("Assets/Model/Media/player/human_f/attack/human_f_2haxe2.fbx",layer); AddStateTransition("Assets/Model/Media/player/human_f/attack/human_f_2haxe3.fbx",layer); } private static void AddStateTransition(string path, AnimatorControllerLayer layer) { UnityEditorInternal.StateMachine sm = layer.stateMachine; //根据动画文件读取它的AnimationClip对象 AnimationClip newClip = AssetDatabase.LoadAssetAtPath(path, typeof(AnimationClip)) as AnimationClip; //取出动画名子 添加到state里面 State state = sm.AddState(newClip.name); state.SetAnimationClip(newClip,layer); //把state添加在layer里面 sm.AddAnyStateTransition(state); } }