上篇给大家介绍了 “Playables API” 的使用方法与背后的意义,本篇将会进一步带大家深入其中。 本篇重点还是会放到 “Playables API” 上,不会对 Timeline 进行集中讲解,但是搞明白了 “Playables API” 也就意味着理解了 Timeline 最复杂的部分,到时候学起来也会非常的快。

快速回顾

在本篇开始前我们先快速的回顾一下上篇的内容:

  1. “Playables API” 的意义 ?
    答:替换Legacy动画系统,并兼容Timeline 。

  2. 如何创建 “Playables 树 ?

答:五行代码就可以快速创建!

1
2
3
4
5
6
7
playableGraph = PlayableGraph.Create("test graph");
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "test Animation",_animator = GetComponent<Animator>());
var clipPlayable = AnimationClipPlayable.Create(playableGraph, clip);
playableOutput.SetSourcePlayable(clipPlayable);
playableGraph.Play();
  1. 没有图形化界面时我们该如何记忆节点链接方式 ?
    答:右输入(数量) 左输出(数量),根节点有左边口,普通节点两边有 。

正文:更复杂的API运用方式

使用 Animation Layer

上一篇我们提到使用 XXXMixerPlayable 可以用来融合多个动画/音频等。那么想在一个角色的身上同时播两个或多个互不干扰的动画该如何处理呢?

想必大家都想到了,没错,就是使用 Animation Layer (动画层) 的技术。在 Animator 中常使用 Avatar Mask 来设置不同部位的遮罩,让其他部分的动画各司其职。

现在我们来看一看 Animation Layer 技术如何在 “Playerable API” 中使用吧。

第一步先制作遮罩,关闭身体上半的动画控制

代码案例如下,您可以看见这里与上一篇我们讲到的 AnimationMixerPlayable 是不是保持了高度一致 ? 这可不是架构的功劳,而是得益于代码风格的统一。无论是在 AnimationMixerPlayableAnimationLayerMixerPlayable 其中都有统一的静态函数 Create ,良好的代码风格能让阅读者轻松掌握代码规律,从而减少阅读时间 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public AnimationClip clip1;
public AnimationClip clip2;
private Animator _animator;
private AnimationLayerMixerPlayable mixerPlayable;
public float weight = .5f;
public AvatarMask LowerMask;
public PlayableGraph playableGraph;
void Start()
{
playableGraph = PlayableGraph.Create("test graph");
var playableOutput = AnimationPlayableOutput.Create(playableGraph, "test Animation",_animator = GetComponent<Animator>());
mixerPlayable = AnimationLayerMixerPlayable.Create(playableGraph, 2);
mixerPlayable.SetLayerMaskFromAvatarMask(1,LowerMask);//设置遮罩到1层
playableOutput.SetSourcePlayable(mixerPlayable);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
var clipPlayable2 = AnimationClipPlayable.Create(playableGraph, clip2);
mixerPlayable.ConnectInput(0,clipPlayable1,0,1);//设置权重1
mixerPlayable.ConnectInput(1,clipPlayable2,0,1);//设置权重1
playableGraph.Play();
GraphVisualizerClient.Show(playableGraph);
}

请注意上面有一些不同与之前的看到的 AnimationMixerPlayable ,这里连接的两个动画权重均为1.为什么不是 0.5 与 0.5 ?
因为现在是同时播放不同于融合。融合需要两种动画过渡权重,而分层则是将模型分为两个区间,0区间播放Clip1,1区间播放Clip2,这是完全同步又独立的。

效果图:上身播放跳跃,下身播放跑动

动态添加动画

之前我们使用Animator的时候需要将所有使用到的动画都加入其中。现在我们使用 ConnectInput & DisconnectInput API 就能轻易的切换动画。

1
2
3
4
5
6
7
8
9
public void Update()
{
if (Input.GetKeyUp(KeyCode.A))
{
mixerPlayable.DisconnectInput(0);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip3);
mixerPlayable.ConnectInput(0,clipPlayable1,0,1);
}
}

效果图:上身切换到胜利动画,下身播放跑动

动态切换的优化

刚才我们实现了动态的切换动画。效果是达到了,可是动画是硬过渡。如果现在只有Connect & Disconnect接口那么API真的无法帮我们达到动画的融合过渡!
现在我们现在来优化一下这段动画硬切的过程,使其达到融合过渡的效果(淡入淡出)。

方式一 使用AnimationMixerPlayable API

使用动画融合节点控制权重实现动画融合,使用这种方式需要实现一个自定义Playable,以list作为容器才能实现复用,否则就是hardcode了 。下面代码展示了最简方式使用融合节点。
效果图:上身融合跳跃与胜利动画

1
2
3
4
5
6
7
8
9
10
mixerPlayable = AnimationMixerPlayable.Create(layerMixerPlayable.GetGraph(),2);
var clipPlayable1 = AnimationClipPlayable.Create(playableGraph, clip1);
var clipPlayable2 = AnimationClipPlayable.Create(playableGraph, clip2);
var clipPlayable3 = AnimationClipPlayable.Create(playableGraph, clip3);
mixerPlayable.ConnectInput(0,clipPlayable1,0,1);
mixerPlayable.ConnectInput(1,clipPlayable3,0,0);
layerMixerPlayable.ConnectInput(0,mixerPlayable,0,1);
layerMixerPlayable.ConnectInput(1,clipPlayable2,0,1);

方式二 使用Animator

效果图:创建融合状态机
同样的我们使用融合状态机实现这个过渡效果。这种方式(Animator+PlayableAPI))让策划或者美术配置也是一种不错的选择。

1
2
3
4
5
animatorController = AnimatorControllerPlayable.Create(layerMixerPlayable.GetGraph(), animationController);
var clipPlayable2 = AnimationClipPlayable.Create(playableGraph, clip2);
layerMixerPlayable.ConnectInput(0,animatorController,0,1);

效果图:上身融合跳跃与胜利动画

方式三 纯Playable API

下面我们要介绍的这种过渡模式是纯代码实现,类似与lengcy api里的Animation CrossFade. 原理就是利用Connect & Disconnect 动态开关节点实现过渡,完毕后重新连接回来。具体实现方式可参考 方式一

使用Bone IK

现在我们再来看看更高级的用法 骨骼IK动画。如果我们需要做一些更逼真的效果IK技术是少不了的。为了方便展示 我们在这边Clone一下代码。

在我写本篇的时候我发现官方在 2018.8.29日 发过关于Playable API 调用IK动画的范例,这里就再重复的赘述。下面是原文链接 🔗 [技术] 详解Animation C# Jobs

您可以直接克隆下面的代码范例中检验效果。

1
Clone https://github.com/KeyleXiao/animation-jobs-samples.git

结语

总体而言Playable API提供了更低层的操作方式,对于使用者的要求更高。如果您现在想要在项目中实践建议先不要用高级特性(ik部分),因为这部分的功能目前还是实验性功能,API的相关文档并不完善。使用动画的控制API进行 Lengacy Animation的替换工作还是很有意义,不仅在与效率,与Timeline结合使用更能发挥出事半功倍的效果。