Version: 2021.1
Animation
Legacy Unity Analytics (SDK Workflow)

Animation Scripting (Legacy)

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.

Making an animated character involves two things; moving it through the world and animating it 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.

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.

Animation Blending

In today’s games, animation blending is an essential feature to ensure that characters have smooth animations. Animators create separate animations, for example, a walk cycle, run cycle, idle animation or shoot animation. At any point in time during your game you need to be able to transition from the idle animation into the walk cycle and vice versa. Naturally, you want the transition to be smooth and avoid sudden jerks in the motion.

В этом случае смешивание анимации становится полезным. В Unity можно иметь любое количество анимаций, которые проигрываются на одном и том же персонаже. Все анимации смешиваются или складываются вместе для создания окончательной анимации.

Сначала сделаем плавный переход между анимациями спокойствия и ходьбы. Чтобы облегчить работу написания скрипта, сперва нужно изменить Wrap Mode анимации на Loop. Затем нужно отключить Play Automatically для уверенности, что никто, кроме нашего скрипта, не проигрывает анимацию.

Наш первый скрипт для анимации персонажа довольно простой; нам нужен способ для определения скорости движения персонажа, а после этого делать переход между анимациями ходьбы и покоя. Для этого простого теста мы будем использовать стандартные оси ввода:-

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

Чтобы использовать этот скрипт в вашем проекте:-

  1. Создайте Javascript файл используя Assets->Create Other->Javascript.
  2. Скопируйте и вставьте код в него.
  3. Перетащите скрипт на персонажа (он должен быть привязан к GameObject, который имеет анимацию).

После нажатия на кнопку Play, персонаж начнет шагать на месте, пока вы будете удерживать нажатой кнопку со стрелкой вверх, и вернется в позу ожидания, если вы отпустите ее.

Animation Layers

Слои это невероятно полезная концепция, позволяющая классифицировать анимацию и приоритезировать веса.

Система анимации Unity может смешивать несколько анимаций, в соответствие с вашими пожеланиями. Вы можете назначить веса вручную, или просто использовать animation.CrossFade(), который будет анимировать вес автоматически.

Веса смешивания всегда нормализуются перед применением

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

However, you will generally want to prioritize which animation receives most weight when there are two animations playing. It is certainly possible to ensure that the weight sums up to 100% manually, but it is easier just to use layers for this purpose.

Layering Example

As an example, you might have a shoot animation, an idle and a walk cycle. The walk and idle animations would be blended based on the player’s speed but when the player shoots, you would want to show only 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. To do this, we need to make sure that the shoot animation is in a higher layer than the idle and walk animations, which means the shoot animation will receive blend weights first. The walk and idle animations will receive weights only if the shoot animation doesn’t use all 100% of the blend weighting. 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!

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");
}

По умолчанию, animation.Play() и animation.CrossFade() остановят или плавно уберут анимации находящиеся в одном том же слое. Это то что требуется в большинстве случаев. В нашем примере стрельбы, покоя и бега, проигрывание покоя и бега не будет влиять на анимацию стрельбы, и наоборот (если требуется, то это поведение можно изменить опциональным параметром функции animation.CrossFade).

Смешивание анимаций

Смешивание анимаций позволяет урезать количество анимаций, который нужно создать для вашей игры. Это достигается тем, что некоторые анимации влияют только на часть тела. Это значит, что такие анимации могут быть использованы совместно с другими анимациями в разнообразных комбинациях.

Добавление трансформации смешивания анимаций производится вызовом метода AddMixingTransform() имеющегося AnimationState.

Пример смешивания

В качестве примера смешивания можно привести что-то вроде анимации махания рукой. Вы можете захотеть сделать махание рукой либо когда персонаж находится в покое, либо когда он идет. Без смешивания анимаций, вам бы пришлось сделать отдельные анимации махания рукой для состояния покоя и ходьбы. Но если вы добавите трансформацию плечей как трансформацию смешивания в анимацию махания рукой, то она будет иметь полный контроль только от плечевого сустава до руки. Так как остальное тело не будет подвержено влиянию анимации махания рукой ,оно продолжит играть анимацию покоя или ходьбы. Как следствие, понадобится только одна анимация, чтобы сделать махание рукой, тогда как остальное тело будет использовать анимацию покоя или ходьбы.

/// 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.

Suppose you want to create a character that leans to the sides as it turns while walking and running. This leads to four combinations (walk-lean-left, walk-lean-right, run-lean-left, run-lean-right), each of which needs an animation. Creating a separate animation for each combination clearly leads to a lot of extra work even in this simple case but the number of combinations increases dramatically with each additional action. Fortunately additive animation and mixing avoids the need to produce separate animations for combinations of simple movements.

Additive Animation Example

Additive animations allow you to overlay the effects of one animation on top of any others that may be playing. When generating 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.

Referring to the previous example, you could make animations to lean right and left and Unity would be able to superimpose these on the walk, idle or run cycle. This could be achieved with code like the following:-

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;
}

Совет: При использовании аддитивных анимаций, очень важно также проигрывать какие-то другие не-аддитивные анимации на каждой трансформации, которая также используется в аддитивной анимации. В противном случае, анимации будут добавляться поверх результата последнего кадра. Это точно не то, что вам нужно.

Процедурная анимация персонажей

Иногда вам понадобится анимировать кости вашего персонажа процедурно. Например, вы хотите чтобы голова вашего персонажа смотрела в определенную точку в 3D пространстве. Это действие лучше всего реализовать с помощью скрипта, который отслеживает целевую точку. К счастью, Unity делает это очень простой задачей, так как кости всего лишь являются трансформациями, которые управляют кожей (skinned mesh). Вы можете управлять костями персонажа из скрипта так же, как трансформациями GameObject.

Одна из важных вещей, которую необходимо знать, это то, что анимационная система обновляет трансформации после функции Update(), и до функции LateUpdate(). Поэтому, если вам нужно вызвать функцию LookAt(), следует делать это в LateUpdate(), чтобы удостовериться, что вы на самом деле переопределяете анимацию.

Тряпичные куклы (Ragdolls) создаются таким же способом. Вам просто требуется добавить компоненты Rigidbody, Character Joint и Capsule Collider к различным костям. Это позволит вам создать анимацию вашего персонажа основанную на физике.

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 Autodesk® 3ds Max® or Autodesk® 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.

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 framerate, such as 56.72 fps, or 83.14 fps, or practically any other value.

As a result, Unity must sample an animation at variable framerates, and cannot guarantee the framerate for which it was originally designed. Fortunately, animations for 3D computer graphics do not consist of discrete frames, but rather of continuous curves. These curves can be sampled at any point in time, not just at those points in time that correspond to frames in the original animation. In fact, 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 than it did in the animation software.

For most practical purposes, you can ignore the fact that Unity samples animations at variable framerates. 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 that the re-sampling takes place behind the scenes. 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.

Также обратите внимание, что как следствие плавающей частоты кадров, анимация, которая проигрывается с использованием WrapMode.Once, не может быть сэмплированна в точное время последнего кадра. В одном кадре игры, анимация может быть сэмплированна только перед концом анимации, и на следующем кадре, время могло уже перейти длину анимации, после чего анимация отключается, и не больше не сэмплируется. Если для вас очень важно, чтобы последний кадр анимации сэмплировался точно, следует использовать режим WrapMode.ClampForever, который продолжит сэмплировать последний кадр бесконечно, пока вы не остановите анимацию.

Animation
Legacy Unity Analytics (SDK Workflow)