Animator倒放

撰写于 2019-07-03 修改于 2019-07-03 分类 Unity 标签 Animator

Animator倒放

最近需要做一个技能编辑器,需要定位玩家的动作,在动作的某一帧,加入事件帧,用来触发特效。需要可以像在Animation编辑器中一样可以动态拖动时间条来定位某一帧,这就需要动作可以播放、暂停、倒放。但是玩家的动作是在Animator中播放的,处理起来有些复杂。以下是做的几种失败尝试。

通过speed实现倒放(错误1)

第一时间,想到的就是通过speed实现倒放,speed有两种:

  • Animator.speed
  • AnimatorStateInfo.speed
  • AnimatorState.speed

Animator.speed: 通过尝试,发现speed=1正常速度播放,speed<=0停止播放,并不能回放,所以这种方法不行!

AnimatorStateInfo.speed: 也就是通过Animator.GetCurrentAnimatorStateInfo方法获取到AnimatorStateInfo,然后设置AnimatorStateInfo.speed。是不是觉得一切顺利?!但是!AnimatorStateInfo.speed是只读!readonly!,所以这种方法不行!

AnimatorState.speed: 是不是没有见过这个类,是的,这个类不常用,除非你经常做插件,需要跟Unity编辑器打交道,可能会用的到。这个对象可以通过以下方式获取:

1
2
3
4
5
6
7
8
UnityEditor.Animations.AnimatorController ac = animator.runtimeAnimatorController as UnityEditor.Animations.AnimatorController;
UnityEditor.Animations.AnimatorStateMachine sm = ac.layers[0].stateMachine;
for (int i = 0; i < sm.states.Length; i++)
{
UnityEditor.Animations.ChildAnimatorState state = sm.states[i];
if (state.state.name.Equals(name))
state.state.speed = speed;
}

是不是觉得就要大功告成,然并卵!这个代码在第一行就会报错,就是animator.runtimeAnimatorController无法转为UnityEditor.Animations.AnimatorController类型,以前的版本是可以的,2018的版本是不行的,所以这种方法不行!

也就是说,通过speed方法,来达到动作的回放,是行不通的,Unity已经把这条路堵死了!

Time.timeScale(错误2)

Time.timeScale可以加速,可以暂停,那Time.timeScale = -1,会怎么样呢?直接提示:Time.timeScale不能设为-1。而且Time.timeScale会控制所有特效的播放,我只是想要控制当前角色动作的播放。所以这种方法不行!

Playback(错误3)

实在没有办法了,只能通过录像回放的形式来处理了。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//录制
int frameCount = (int)(currentClip.length * currentClip.frameRate);
animator.speed = 0;
animator.StopPlayback();
animator.recorderStartTime = 0;
animator.StartRecording(frameCount);
animator.Update(0);
for (var i = 0; i < frameCount; i++)
{
animator.Update(1.0f / currentClip.frameRate);
}
animator.StopRecording();
animator.speed = 1;
.
.
.
//回放
animator.StartPlayback();

是不是觉得很完美,但是在实际使用中问题很多:

  1. 无法正确开始录制。录制时间开始的帧数无法确定,比如我想从idle状态转到attack状态,需要播放attak动作,我用animator.GetCurrentAnimatorClipInfo(0)[0].clip.name来判断是否到这一状态,然后开始录制,但是实际中,录制的动作总会少几帧。其实我觉得使用 normalizeTime == 0 是可以用来判断 clip 是否已经开始播放了,可以准确的录制,但是我没有尝试,原因是录制回放会有一个很大的问题,就是下面这个问题。
  2. 事件帧会无限触发。暂时没有找到原因,我在某一帧加入事件后,在播放录像时该事件会无限触发,无解!

Animator.Play(正确)

就在绝望的时候,发现了一个接口:Animator.Play(string stateName, int layer = -1, float normalizedTime = float.NegativeInfinity),这个接口可以随意播放一个State,normalizedTime可以随意定位动作时间,normalizedTime是关键!!!需要注意的是,这个需要配合 animator.speed使用,如果想要定位到某一帧,需要计算出该帧的 normalizedTime, 并且设置 animator.speed=0,即可实现定位到某一帧,并暂停!

Site by ZHJ using Hexo & Random

Hide