最近在做一个根据配置表自动生成动画剪辑clip以及controller的功能.做法是根据配置表配置的动作以及每个动作的关键帧,自动为每个clip添加事件.这样做可以把动画的事件处理在游戏运行之前就计算好然后存到动画文件.anim中,可以提高游戏的运行效率,如果做的再细点,还可以自定义动画事件的传入的参数.比如act游戏中的多段攻击,这些数据一定是配到表中的,我们可以把表中的数据导入成Unity的ScriptableObject类型,然后打成.asset文件,在把他指定给AnimationEvent的objectReferenceParam参数,这样当动画播放的攻击帧并检测到攻击事件后直接把本段攻击的配置数据传入游戏运行时,可以减少不少的数据查找操作.



private static void AddClipEvent(AnimationClip clip, ActionData data)
     {
         List atvts = new List();
         float maxFrame = data.frameCount;
         if (!string.IsNullOrEmpty(data.detFrames))
         {
             string[] dets = data.detFrames.Split(',');
 
             for (int i = 0; i < dets.Length; i++)
             {
                 int frame;
                 if (int.TryParse(dets[i], out frame))
                 {
                     if (frame > 0 && frame <= maxFrame) 
                     {
                         AnimationEvent detEvt = new AnimationEvent();
                         detEvt.functionName = GCharacter.FRAME_EVENT_NAME;
                         detEvt.stringParameter = GCharacter.FrameType.Detec.ToString() + "_" + data.id + "_" + i;
                         detEvt.time = frame / clip.frameRate;
                         atvts.Add(detEvt);
                     }
                 }
             }
         }
 
         AnimationEvent endEvt = new AnimationEvent();
         endEvt.functionName = GCharacter.FRAME_EVENT_NAME;
         endEvt.stringParameter = GCharacter.FrameType.End.ToString() + "_" + data.id;
         endEvt.time = data.frameCount / clip.frameRate;
         atvts.Add(endEvt);
 
         AnimationUtility.SetAnimationEvents(clip, atvts.ToArray());
     }



不过在做到这的时候你会发现一个问题,那就是GCharacter.FrameType.End是最后一帧的帧事件,每当一个state切换到另一个state的时候这个帧尾的事件是不会被调用的!盯着Unity的状态机视图看了很久,终于找到了问题的所在!

unity 模型动画时长不正确 unity动画帧_置数据

Unity之所以收到很多人的青睐就是因为他给开发者实现了很多比较复杂的功能,这里就是因为两个动画过度时候的融合导致的,添加Transition的时候会默认指定一个过度时间,也就是上图中的Transition Duration,这里是0.1.

所以其实就相当于上个动画播放到距离最后一帧还有0.1秒的时候开始播放下一个动画,也就是此时Unity的"当前状态"已经是下一个state,而上一个state的后几帧的事件也不会触发.

有两种方法,一种是Transition Duration设为0,还有一种是让动画在离最后一帧还有0.1秒的时候就认为动画已经播放到最后一帧.我是用的后者,毕竟Unity"动画过度的融合"咱不能自己写吧,可是差0.1秒,到是可以在程序中自己控制.

把这句改成这样:endEvt.time = (data.frameCount / clip.frameRate) - 0.12f;