Animation Scripting 动画脚本

优质
小牛编辑
132浏览
2023-12-01

Unity's Animation System allows you to create beautifully animated skinned characters. The Animation System supports animation blending, mixing, additive animations, walk cycle time synchronization, animation layers, control over all aspects of the animation playback (time, speed, blend-weights), mesh skinning with 1, 2 or 4 bones per vertex as well as supporting physically based rag-dolls and procedural animation. To obtain the best results, it is recommended that you read about the best practices and techniques for creating a rigged character with optimal performance in Unity on the Modeling Optimized Characters page.

Unity的动画系统可以创建漂亮的角色蒙皮动画。动画系统支持动画融合、混合、叠加动画,行走循环的时间同步,动画层,控制动画的各个方面(时间、速度、融合权重),带有每顶点1、2或4骨骼的蒙皮网格,以及支持基于物理的布娃娃系统和程序动画。为了获得最佳效果,建议您阅读有关的最佳做法,创建最佳性能的绑定的角色在建模优化的角色页面。

Making an animated character involves two things; moving them through the world and animating them accordingly. If you want to learn more about moving characters around, take a look at the Character Controller page. This page focuses on the animation. The actual animating of characters is done through Unity's scripting interface.

制作一个动画角色涉及两件事情,从世界空间移动和做相应的动画。如果想了解角色移动相关的更多内容,参见角色控制器页面,这页着重动画。实际上角色动画完成是通过Unity的脚本接口。

You can download example demos showing pre-setup animated characters. Once you have learned the basics on this page you can also see the animation script interface.

可以从示例演示页面下载预先设置好的动画角色。一旦你学会了此页面上的基础知识,您还可以查看动画脚本接口

If you like, quickly jump to one of the topics covered on this page:

如果你喜欢,快速跳转到此页面上所涵盖的主题:

Animation Blending 动画融合

In today's games, animation blending is an essential feature to ensure that characters have smooth animations. Animators create separate animations, e.g. a walk cycle, run cycle, idle animation or shoot animation. At any point in time in your game you need to be able to transition from the idle animation into the walk cycle and vice versa. Of course you don't want any sudden jumps in the motion, you want the animation to smoothly transition.

如今的游戏,Animation Blending是确保角色平滑动画的一项重要功能。动画师创建的分段动画,如:行走循环、跑步循环、 空闲动画或射击动画。在游戏的任何时间点都有可能从空闲转换到走动,反之亦然。当然你不希望两个不同的动作之间突然跳转,想要动画平滑过渡。

This is where animation blending comes in. In Unity you can have an arbitrary amount of animations playing on the same character. All animations are blended or added together to generate the final animation.

这就是动画融合的用武之地。在Unity同一个角色可以拥有任意数量的动画。所有动画被融合或添加在一起,来产生最终动画。

Our first step will be to make a character smoothly blend between the idle and walk animations. In order to make our job simpler when scripting, we will first set the Wrap Mode of the animation to Loop. Then we will turn off Play Automatically to make sure our script is the only one playing animations.

我们第一步是将制作一个角色在空闲动画和行走动画之间平滑融合。为了使写脚本时更加简单,我们首先设置循环模式为Loop,然后关闭自动播放,以确保仅通过脚本播放动画。

Our first script for animating the character is quite simple; we only need some way to detect how fast our character is moving, and then fade between walk and idle animation. For this simple test we use the pre-setup input axes.

我们第一个动画角色的脚本是非常简单的;我们只需要一些方法来检测角色是如何快速移动,然后在行走和空闲动画直接过渡。对于这个简单的测试,我们使用预置的输入轴。

function Update () {
   if (Input.GetAxis("Vertical") > 0.2)
       animation.CrossFade ("walk");
   else
      animation.CrossFade ("idle");
} 

To get this script running:

来让这个脚本运行起来:

  1. Create a javascript using Assets->Create Other->Javascript.
    从菜单创建脚本Assets->Create Other->Javascript
  2. Copy & Paste the code into it
    复制并粘贴代码进来
  3. Drag the script onto the character (It needs to be the same GameObject as the animation)
    拖动脚本到角色上(需要作为动画的同一游戏物体)

When you hit the Play button, the character will start walking in place when you hold the up arrow key and return to the idle pose when you release it.

当点击播放按钮,角色将在按下上箭头键时开始走动,释放按键时,返回空闲姿势。

Animation Layers 动画层

Layers are an incredibly useful concept that allow you to group animations and prioritize weighting.

层是非常有用的概念,允许群组动画和优先级别。

In Unity's animation system, you can blend between as many animation clips as you want. You can assign blend weights manually or simply use animation.CrossFade(), which will animate the weight automatically.

在Unity动画系统,可以在许多动画剪辑之间做期望的融合。可以指定手工融合权重或简单实用animation.CrossFade(),来自动动画处理权重。

Blend weights are always normalized before being applied 融合权重总是在应用前被规格化

Let's say you have a walk cycle and a run cycle, both have a weight of 1 (100%). When Unity generates the final animation it will normalize the weights, which means walk will contribute 50% to the animation, the run cycle will also contribute 50%.

比方说有一个行走循环和跑步循环,两者的权重都是1(100%)。当Unity生成最终动画会规格化权重,这意味着行走将占50%,跑步占50%权重。

This is all very nice, but often you want to prioritize which animation receives most weight when there are two animations playing. Surely you could just make sure that the weight sums up to 100% manually, but it is a lot easier to use layers for this purpose.

这很不错,但往往考虑优先级,当两个动画播放时,哪个动画接收更多的权重。当然要确保手动调节的权重之和为100%,但是,使用层就容易的多。

Layering Example 使用层示例

As an example, you might have a shoot animation, an idle and a walk cycle. You will want to continously fade between the walk and idle animation based on the player's speed. But when the player shoots you want to only show the shoot animation. Thus the shoot animation essentially has a higher priority.

作为一个例子,有一个射击动画,一个空闲和行走循环动画,想要在行走和空闲动画之间连续过渡,基于玩家的速度。但当玩家射击时,仅显示射击动画,因此,射击动画基本上有更高优先级。

The easiest way to do this is to simply keep playing the walk and idle animations while shooting. Then we need to make sure that the shoot animation is in a higher layer than idle and walk. This means the shoot animation will receive blend weights first. The walk and idle animation will receive weights only if the shoot animation doesn't use all of the 100% blend weights. So when CrossFading the shoot animation in, the weight will start out at zero and over a short period become 100%. In the beginning the walk and idle layer will still receive blend weights but when the shoot animation is completely faded in, they will receive no weights at all. This is exactly what we need!

要做到这点最简单的方法是在射击时简单的保持行走和空闲动画,然后我们需要确保射击动画比空闲和行走动画在更高的层。这意思是射击动画将首先接收融合权重。行走和空闲动画仅当射击动画不使用融合权重100%时,接收权重。因此,交叉淡入淡出射击动画时,权重将从0开始很短时间内变为100%,在开始时步行和空闲层仍然会收到融合权重,但当射击动画完全切入时,它们不再接收权重,这正是我们所需要的!

function Start () {
   // Set all animations to loop
   animation.wrapMode = WrapMode.Loop;
   // except shooting
   animation["shoot"].wrapMode = WrapMode.Once;

   // Put idle and walk into lower layers (The default layer is always 0)
   // This will do two things
   // - Since shoot and idle/walk are in different layers they will not affect
   //   each other's playback when calling CrossFade.
   // - Since shoot is in a higher layer, the animation will replace idle/walk
   //   animations when faded in.
   animation["shoot"].layer = 1;

   // Stop animations that are already playing
   //(In case user forgot to disable play automatically)
   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");
} 

By default the animation.Play() and animation.CrossFade() will stop or fade out animations that are in the same layer. This is exactly what we want in most cases. In our shoot, idle, run example, playing idle and run will not affect the shoot animation and vice versa (you can change this behavior with an optional parameter to animation.CrossFade if you like).

默认animation.Play() 和 animation.CrossFade()将停止或淡出在同一层里面的动画。这是我们在绝大多数情况下需要的,在射击、空闲、跑步示例中,播放空闲和跑步不会影响射击动画,反之亦然。

Animation Mixing 动画混合

Animation mixing allow you to cut down on the number of animations you have to create for your game by having some animations apply to part of the body only. This means such animations can be used together with other animations in various combinations.

动画混合,让你可以削减为游戏创建的动画数量,让一些动画应用给只是身体的一部分。这意味着这样动画可以和其他动画以各种组合一起使用。

You add an animation mixing transform to an animation by calling AddMixingTransform() on the given AnimationState.

在给定的动画状态,通过调用AddMixingTransform(),叠加动画混合变换到动画。

Mixing Example 使用混合示例

For example you might have a hand-waving animation. You might want to play the hand waving animation on a idle character or on a walking character. Without animation mixing you would have to create two hand waving animations: One for idle, and one for walking. However, if you add the shoulder transform as a mixing transform to the hand waving animation, the hand waving animation will have full control only from the shoulder and out. The rest of the body will not be affected by it, and will continue playing the idle or walk animation. Thus you only need one hand waving animation.

例如,有个一个挥手动画,想要在空闲角色或正行走的角色播放挥手动画。没有动画混合,您必须创建两个手挥舞着动画:一个用于空闲,一个用于行走。不过,如果你添加肩膀变换作为混合变换,来做挥手动画,那么挥手动画将从肩膀位置外受完全控制,身体的其余部分将不会受到它的影响,将继续播放空闲或行走动画。因此,只需要一个挥手动画。

/// 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_hand"].AddMixingTransform(mixTransform);
}

Additive Animations 叠加动画

Additive Animations and Animation mixing allow you to cut down on the number of animations you have to create for your game, and are important for creating facial animations.

叠加动画和动画混合,让你削减为游戏创建的动画数量,对于创建面部动画是非常重要的。

Let's say you want to create a character that leans to the sides when running and turning.

比如说,想要创建一个角色当跑步和转弯时,身体向一侧倾斜。

You already made a walk and run cycle, now you could make individual walk-lean-left, walk-lean-right, run-lean-left, run-lean-right animations.

已经制作好一个行走和跑步循环,现在分别制作行走左倾、行走右倾、跑步左倾、跑步右倾动画。

But that means you just quadrupled the amount of animation work! Creating a huge amount of animations is not feasible. Additive animations and Mixing to the rescue!

但是,这意味着制作动画的工作翻了两倍!创建这么巨量的动画是不可行的。要使用叠加动画和混合动画来解决问题。

叠加型动画通过将两个动画彼此相减进行计算,并将其存储为两个动画的差值。实际上叠加型动画本身并没有什么用途,因为它需要添加到另一个动画上才能正常工作。使用叠加型动画实际目的是想找到可以应用到多个动画上的叠加姿势。这可以降低每个状态需要的动画数量。比如一个武器弹药装载动画可以制作成为叠加动画。现在可以把它添加到空闲动画、走步的动画集(前、后、左和右)上面,同样也可以添加到跑步的动画集上。所以不必制作向前走动过程中加载弹药的动画或者其他任何组合动画,只需要创建一个装载弹药的叠加动画,然后将其添加到向前走动、向后跑动等动画的上面即可。

叠加型动画存在的问题是,把两个动画叠加到一起并不能总是产生您想要的效果。当构建时,叠加型动画是两个动画间的不同部分。您所获得的是个叠加型动画,它可以在那个特定动画的上面工作的很好。但是如果您将叠加型动画和另一个动画结合使用,结果可能不像您所期望的那样。

您可能会看到枪支戳到面部上、角色看上去不自然等等。所以创建及使用叠加型动画不是一个容易的过程。一般,您将会想在一个非常接近于基础姿势的动画上使用叠加动画,您也将会想使用IK骨骼来修复在四肢中的错误。一个常见的问题是使用叠加型动画来影响一个角色的臀部和腿部,但最终的结果并没把脚部放置到您期望放置的位置上。使用脚部放置可以解决这个问题。

引用至UDK

Additive Animation Example 叠加动画示例

Additive animations allow you to overlay the effects of animation on top of any others that may be playing. When making additive animations, Unity will calculate the difference between the first frame in the animation clip and the current frame. Then it will apply this difference on top of all other playing animations.

叠加动画让你在任意其他动画上面叠加动画效果,当制作叠加动画,Unity将计算在动画剪辑的第一帧和当前帧之间的差异,然后将在所有其他播放的动画上面应用这个差异。

Now you only have to make a lean-left and lean-right animation. Unity will then layer this animation on top of the walk, idle or run cycle.

现在仅需要制作一个左倾和一个右倾动画。Unity将层化这个动画到行走、空闲和跑步循环的顶部。

Here is the code to make that happen:

这里是来实现这一目标的代码:

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 and 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 and 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 and 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 and 1 at the last frame in the clip
   leanLeft.normalizedTime = -lean;
   leanRight.normalizedTime = lean;
} 

Tip 技巧:

When using Additive animations it is critical that you are also playing some other non-additive animation on every transform that is also used in the additive animation, otherwise the animations will add on top of the last frame's result. This is most certainly not what you want.

当使用叠加动画时,在每个也使用叠加动画的变换上也在播放一些其他非叠加动画,这是至关重要的。否则,动画将添加在最后一帧的结果上面,这当然不是你想要的。

Procedurally Animating Characters 程序动画角色

Sometimes you want to animate the bones of your character procedurally. For example you might want the head of your character to look at a specific point in 3D space. This is best done with a script. Fortunately, Unity makes this very easy. In Unity all bones are just Transforms which drive the skinned mesh. Thus you can script bones of a character just like any other GameObject.

有时,想要程序动画角色骨骼。例如,可能需要角色的头注视3D空间的某个点,这个最好让脚本来做,幸运的是,Unity做这个很容易,所有的骨骼都只是变换,来驱动蒙皮网格。因此,脚本控制骨骼就像其他游戏物体一样。

One important thing to know is that the animation system updates the Transforms after the Update() function and before the LateUpdate() function is called. Thus if you want to do a LookAt() function you should do that in LateUpdate() to make sure that you are really overriding the animation.

一件很重要的事情要知道的是动画系统更新变换是在Update()函数之后并LateUpdate()函数被调用之前。因此想要调用LookAt()函数,应该在LateUpdate()中以确保已经真的重写动画。

Ragdolls are created in the same way. You simply have to attach Rigidbodies, Character Joints and Capsule Colliders to the different bones. This will then physically animate your skinned character.

创建布娃娃系统用同样的方法。简单的附加刚体、角色关节和胶囊碰撞器给不同的骨骼,这样物理系统就会作用于蒙皮的角色。

Animation Playback and Sampling 动画播放和取样

This section explains how animations in Unity are sampled when they are played back by the engine.

这节将说明引擎如何在动画播放时取样。

AnimationClips are typically authored at a fixed frame rate. For example, you may create your animation in 3ds Max or Maya at a frame rate of 60 frames per second (fps). When importing the animation in Unity, this frame rate will be read by the importer, so the data of the imported animation is also sampled at 60 fps.

动画剪辑通常以一个固定的帧速率制作。例如,可以在3ds Max或Maya中以每秒60帧(fps)的帧速率创建动画。当导入动画到Unity时,这个帧速率将由导入器读取,因此,导入的动画数据被取样也是60fps。

However, games typically run at a variable frame rate. The frame rate may be higher on some computers than on others, and it may also vary from one second to the next based on the complexity of the view the camera is looking at at any given moment. Basically this means that we can make no assumptions about the exact frame rate the game is running at. What this means is that even if an animation is authored at 60 fps, it may be played back at a different frame rate, such as 56.72 fps, or 83.14 fps. It can be anything.

然后,游戏运行通常运行在一个变化帧速率,有的电脑帧速快,有的电脑帧速慢,即使是同一台电脑,前一秒和后一秒因为视角的不同帧速也会不一样。基本上当游戏开始运行时我们无法确定一个精确的帧速率。这意味着即使动画剪辑制作时是60fps,那它播放时也许是另外一个速率,例如56.72 fps 或 83.14 fps,它可能是任何一个。

Unity samples animation at this variable frame rate, not at the frame rate they were authored with. Luckily, animations for 3d computer graphics do not consist of discrete frames, but rather of continuous curves. These curves are well suited for being sampled at any point in time; not just at those points in time that corresponds to one of the original frames. It also means that if the game runs at a higher frame rate than the animation was authored with, the animation will actually look smoother and more fluid in the game.

Unity在这变化的帧速率取样动画,不在于制作时的帧速率,幸运的是,3D电脑图形动画不是由分散的动画组成,而是连续的曲线。这些曲线可以让我们在任何时间点取样,而不只是对应某一个原始帧的时间点。这也意味着如果游戏运行的帧速率高于动画原始制作时的帧速率,在游戏中,动画剪辑实际上将看起来更加平滑和更流畅。

For most practical purposes, the fact that Unity samples animations at this variable frame rate is not something you need to be concerned with. However, if you have gameplay logic that relies on animations that animate transforms or properties into very specific configurations, then you need to be aware of it. For example, if you have an animation that rotates an object from 0 to 180 degrees over 30 frames, and you want to know from your code when it has reached half way there, you should not do it by having a conditional statement in your code that checks if the current rotation is 90 degrees. Because Unity samples the animation according to the variable frame rate of the game, it may sample it when the rotation is just below 90 degrees, and the next time right after it reached 90 degrees. If you need to be notified when a specific point in an animation is reached, you should use an AnimationEvent instead.

对于大多数的实际应用,事实上Unity对变化帧速的采样不是我们需要关切的,然而,如果你的某个游戏逻辑依赖于动画变换或属性的动画在一个非常特殊的配置,那必须知道这一点。例如,如果有一个动画是把一个物体30帧内从0旋转到180度,想从代码中知道当它已达到了一半,你不能写一段条件语句来检查现在旋转值是不是90度。因为 Unity 依照游戏的变化速率来对动画取样,它可能在旋转快到90度时进行取样,或刚好过90度的时候取样。当在一个动画指定一个点到达时,需要通知, 应该使用AnimationEvent来代替。

Note also that as a consequence of the variable frame rate sampling, an animation that is played back using WrapMode.Once may not be sampled at the exact time of the last frame. In one frame of the game the animation may be sampled just before the end of the animation, and in the next frame the time can have exceeded the length of the animation, so it is disabled and not sampled further. If you absolutely need the last frame of the animation to be sampled exactly, you have to use WrapMode.ClampForever. In that case the animation will keep sampling the last frame infinitely until you stop the animation yourself.

还要注意的变化帧速率采样的结果,一个使用WrapMode.Once模式播放的动画的采样不一定是精确的最后一帧。在游戏中很有可能是刚好结束前的某一帧,在下一帧时间可能超过动画的长度,所以它被禁用,而不再采样。如果需要动画的最后一帧取样精确,你可以使用WrapMode.ClampForever。 如在这种情况下,动画在这种情况下,动画将保持无限取样最后一帧,直到你停止动画。

页面最后更新: 2011-01-28