动画脚本 Animation Scripting
Unity动画系统允许你创建一个漂亮的动画蒙皮角色,动画系统支持动画融合,混合,添加动画,步调周期时间同步,动画层,控制动画回放的所有方面(时间、速度、混合权重) 每个顶点有1.2.4个骨骼影响的mesh,基于物理系统的布娃娃系统,另外还有程序动画。为了获得最佳效果推荐您在制作模型和动画绑定前阅读一下Modeling Optimized Characters章节.
制作一个动画角色主要包括两个方面:在世界中移动和由此产生的动画。实际上角色动画是由Unity脚本界面完成的。
在现今的游戏中Animation Blending是一项保证游戏动画顺畅过渡的基本的特性。动画师创建的动画例如: walk 循环、run 循环、idle原地空闲动画或射击动画。在游戏的任何时间点你都有可能从空闲站立转换到走动,反之亦然。当然你不希望两个不同的动作之间突然跳转, 你需要动画平滑过渡。
而这个问题的解决就依赖动画融合技术,在Unity中你可以让同一个角色拥有任意数量的动画,所有这些动画融合添加成为一个总的动画。
首先为一个角色添加两个动画原地空闲站立和走动并平滑的使这两个动画过渡,为了在写脚本时简单些, 首先要设置动画的Wrap Mode为Loop,然后关闭Play Automatically来让我们的脚本来独占动画的播放。
我们第一个动画脚本很简单:我们需要一些方法来探查角色移动的有多快, 然后在走和站立之间淡入淡出,在这个简单的测试中我们使用 pre-setup input axes。
function Update () {
if (Input.GetAxis("Vertical") > 0.2)
animation.CrossFade ("walk");
else
animation.CrossFade ("idle");
}
下面来让这个脚本运行:
1.创建一个js脚本 Assets->Create Other->Javascript.
2.拷贝代码。
3.把脚本拖拽给角色 character (It needs to be the same GameObject as the animation)。
点击Play 按钮, 当你按上下键时角色会走动,松开上下键时角色站立不动。
动画层Animation Layers
层是一个非常有用的概念它可以让你将动画片段任意成组并且区分优先顺序。在Unity动画系统中, 你可以混合任意数量的动画片段,你可以手工分配权重或者直接使用animation.CrossFade(),来自动分配权重。
混合权重混合权重总是在应用前被规格化normalized。比如说我们现在有一个walk cycle和一个run cycle, 权重都是1 (100%).当Unity计算最终动画时会规格化权重, 这意味着walk占50%权重,run cycle占50%权重。
这在大多数情况下都是不错的, 但当两个动画片段同时运行而其中一个权重明显大于另外一个,那么你需要手动调整权重值,但如果你使用动画层来解决这个问题过程会容易得多。
制作动画层的范例Layering Example
例如现在你有一个射击动画, 一个空闲站立,一个走动循环,你需要在走和站两个动作间持续的淡入淡出(在玩家走动速度的基础上) 但当玩家射击时我们只想展示射击动画因而射击动画此时的优先度最高。
为了达到这一目的最简单的方法是在射击时简单的保持walk和idle动画接下来需要确定shoot animation在一个比idle和walk更高的层。这意味着shoot animation将首先收到混合权重,walk 和idle只有在shoot animation不使用100%混合权重的情况下接收权重,所以当CrossFading the shoot animation in, 权重将从0开始很短时间内到达100%,在开始阶段walk和idle层将依然可以收到混合权重,但当shoot animation完全切入时, 他们就收不到权重了,这才是我们需要的。
function Start () {
// Set all animations to loop 设置所有动画为循环
animation.wrapMode = WrapMode.Loop;
// except shooting 除了射击(不循环)
animation["shoot"].wrapMode = WrapMode.Once;
//放置idle 和 walk 进低一级别的 layers (默认 layer 总是 0)
// This will do two things这将作两件事情
// - 当 calling CrossFade时,由于shoot 和 idle/walk 在不同的layers 中
// 它们将不会影响互相之间的重放.
// - 由于 shoot 在高一级的 layer, 当faded in 时shoot动画将替换
// idle/walk 动画 .
animation["shoot"].layer = 1;
// Stop animations that are already playing停止已经播放的动画
//(万一 user 忘记的话,自动disable播放)
animation.Stop();
}
function Update () {
// Based on the key that is pressed,基于按下的键
// play the walk animation or the idle animation播放走,站动画
if (Mathf.Abs(Input.GetAxis("Vertical")) > 0.1)
animation.CrossFade("walk");
else
animation.CrossFade("idle");
// Shoot射击
if (Input.GetButtonDown ("Fire1"))
animation.CrossFade("shoot");
}
默认情况下animation.Play()和animation.CrossFade()将停止或淡出在同一层里面的动画。这是我们在绝大多数情况下需要的,在我们shoot、idle、run 范例中, 播放idle和run将不会影响到 shoot动画,反之亦然。
动画混合Animation Mixing
动画混合可以让你缩减你必须为游戏制作的动画片断数量,方法是制作只对身体某个部分起作用的动画,这意味着这些动画可以和其他动画合并起来一起使用。
如果你想给一个动画添加 animation mixing transform to an animation by calling AddMixingTransform() on the given AnimationState.
混合范例Mixing Example
例如你可能有一个挥手(hand-waving)动画。你可能需要让一个空闲站立(idle)角色或者一个走动(walking)角色来挥手。如果没有动画混合你可能需要制作两个挥手hand-waving动画: 一个给idle, 一个给walking。可是, 如果你将挥手(hand-waving)动画作为一个mixing transform 添加到shoulder transform,挥手动画将只控制肩膀,身体余下部位不受其影响,下半身会继续播放idle或者 walk动画,因而你只需要一个挥手(hand-waving)动画。
/// Adds a mixing transform using a Transform variable
var shoulder : Transform;
animation["wave_hand"].AddMixingTransform(shoulder);
Another example using a path.
function Start () {
// Adds a mixing transform using a path instead
var mixTransform : Transform = transform.Find("root/upper_body/left_shoulder");
animation["wave_h和"].AddMixingTransform(mixTransform);
}
附加动画和动画混合可以让你缩减为游戏制作的动画片断的数量,并且对面部动画(facial animations)来说非常重要。
如果创建一个在跑和转身时身体可以自动倾斜的角色:
你已经制作好了一个walk和run循环, 现在你还要制作一个走动左倾( walk-lean-left), 走动右倾(walk-lean-right), 跑左倾(run-lean-left), 跑右倾(run-lean-right)动画。
这意味着你需要多做4个动画片断,制作这么多数量的动画会累死人的,而附加动画(Additive animations)和混合(Mixing)可以大大减少这些工作量。
附加动画范例 Additive Animation Example
附加动画允许你在顶层覆盖其他所有可能播放的动画的效果。当你制作一个附加动画时, Unity将计算动画片断里的第一帧 (first frame)和当前帧(current frame)的差异. 然后它将在所有其他播放的动画之上应用这个差异。
现在你只需要制作一个左倾( lean-left) 和右倾( lean-right)动画. Unity将为此倾斜动画新建一个层并置于walk, idle或run循环的层级之上。
代码如下:
private var leanLeft : AnimationState;
private var leanRight : AnimationState;
function Start () {
leanLeft = animation["leanLeft"];
leanRight = animation["leanRight"];
// Put the leaning animation in a separate layer
// So that other calls to CrossFade won't affect it.
leanLeft.layer = 10;
leanRight.layer = 10;
// Set the lean animation to be additive 混合模式为附加
leanLeft.blendMode = AnimationBlendMode.Additive;
leanRight.blendMode = AnimationBlendMode.Additive;
// Set the lean animation ClampForever
// With ClampForever animations will not stop
// automatically when reaching the end of the clip
leanLeft.wrapMode = WrapMode.ClampForever;
leanRight.wrapMode = WrapMode.ClampForever;
// Enable the animation 和 fade it in completely
// We don't use animation.Play here because we manually adjust the time
// in the Update function.
// Instead we just enable the animation 和 set it to full weight
leanRight.enabled = true;
leanLeft.enabled = true;
leanRight.weight = 1.0;
leanLeft.weight = 1.0;
// For testing just play "walk" animation 和 loop it
animation["walk"].wrapMode = WrapMode.Loop;
animation.Play("walk");
}
// Every frame just set the normalized time
// based on how much lean we want to apply
function Update () {
var lean = Input.GetAxis("Horizontal");
// normalizedTime is 0 at the first frame 和 1 at the last frame in the clip
leanLeft.normalizedTime = -lean;
leanRight.normalizedTime = lean;
}
提示:当使用附加动画时它会判断你同时也在播放一些其他的使用了附加动画的非附加动画, 否则动画将添加到最后一帧结果的顶部,这通常不是你所需要的。
程序动画角色Procedurally Animating Characters
有时你需要程序化的驱动你的角色骨骼。例如你可能需要你的角色的头注视3D空间的某个点,这个活最好让脚本来干,幸运的是, Unity做这个很容易,在Unity中所有骨骼来驱动蒙皮网格的变换,因而你可以给角色的骨骼写脚本,就和其他GameObject一样。
布娃娃系统Ragdolls也是用同样的方法制作出来的。你可以简单的把刚性物体, 角色关节和胶囊碰撞体连接给不同的骨骼。这样物理系统就可以作用于蒙皮角色。(什么是布娃娃系统,当你在射击类游戏中打死对手时可以注意到当角色快接近地面时,他的四肢开始瘫软在地面上,这个不是动画师调出来的,而是布娃娃系统自动计算出来的。)
动画重放和取样Animation Playback 和 Sampling
这一部分将说明引擎如何在动画重放时取样。动画片断制作时总是有一个特定的速率。举例来说, 你可能在Max 或Maya创建了一个帧速为60 frames每秒(fps)的动画。当导入Unity后, 输入模块将读取帧速, 所以导入的动画帧速还是60fps。
可是, 游戏运行时的速率是不断变化的,有的电脑帧速快有的电脑帧速慢, 即使是同一台电脑前一秒和后一秒因为视角的不同帧速也不一样,基本上当游戏开始运行时我们无法确定一个精确的帧速,这意味着即使我们的动画片断制作时是60fps, 它重放时也许用的是另外一个速率, 例如56.72 fps, 或 83.14 fps. 它可以变成任何一个速率。
Unity对这些变化的速率取样, 不在于其制作时的速率,幸运的是3D电脑图形动画不是由分散的动画组成, 确切地说是由连续的曲线构成的。这些曲线可以让我们在任何时间点取样,而不是适配某一个原始帧的时间点,I这也意味着如果游戏运行速率高于原始制作速率, 动作事实上看起来会更平滑流畅。
对绝大多数应用场合, Unity对变化帧速的采样我们无需对其进行干预。可是, 如果你的某个游戏逻辑所依赖的动画变化或道具结构十分特殊, 那你必须知道这一点。