Version: 5.3 (switch to 5.4b)
Animation (анимация)
Legacy GUI

Скриптинг анимации (Legacy)

Система анимации в Unity позволяет создавать великолепно анимированных персонажей. Она поддерживает блендинг, микширование, сложение анимаций, синхронизацию цикла ходьбы, анимационные слои, контроль всех аспектов проигрывания (время, скорость, веса блендинга), скиннинг мешей с 1, 2 или 4 костями на вершину, а также основанные на физике rag-dolls (тряпичные куклы) и процедурную анимацию. Для получения лучших результатов, рекомендуется почитать о практических подходах и принципах создания персонажей с оптимальной производительностью в Unity на странице Моделирование оптимизированных персонажей.

Создание анимированного персонажа включает в себя две вещи - перемещение в пространстве сцены и соответствующая анимация. Чтобы узнать больше о перемещении персонажа, прочитайте Character Controller page. На этой странице разбираются вопросы анимации. Фактически, вся анимация может быть сделана во встроенном редакторе Unity.

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

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

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

В этом случае смешивание анимации становится полезным. В 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, персонаж начнет шагать на месте, пока вы будете удерживать нажатой кнопку со стрелкой вверх, и вернется в позу ожидания, если вы отпустите ее.

Слои анимации

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

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

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

Предположим, что у вас есть цикл ходьбы и цикл бега, имеющие веса, равные 1 (100%). Когда Unity генерирует окончательную анимацию, веса нормализуются, то есть вклад цикла ходьбы составит 50% анимации, и вклад цикла бега также составит 50%.

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

Пример использования слоев

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

Самый простой способ добиться этого, это просто продолжать проигрывание анимаций ходьбы и покой во время стрельбы. Чтобы это сделать, нам надо удостовериться, что анимация стрельбы находится в верхнем слое над анимациями покоя и ходьбы, что означает, что анимация стрельбы будет получать веса смешивания в первую очередь. Анимации ходьбы и покой будут получать веса только если анимация стрельбы не использует все 100% веса. Так, когда начинается переход в анимацию стрельбы, вес начнется с нуля, и за короткое время станет 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");
}

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

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

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

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

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

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

/// Adds a mixing transform using a Transform variable
var shoulder : Transform;
animation["wave_hand"].AddMixingTransform(shoulder);

Другой пример с использованием путей.

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) анимации

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

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

Пример аддитивной анимации

Аддитивные анимации позволяют накладывать эффекты одной анимации поверх любых других запущенных анимаций. Когда генерируются аддитивные анимации, Unity вычислит разницу между первым и текущим кадрами анимационного клипа. Затем разница применится поверх всех остальных проигрываемых анимаций.

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

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 к различным костям. Это позволит вам создать анимацию вашего персонажа основанную на физике.

Воспроизведение и cэмплирование анимации

Этот раздел рассказывает о том как в Unity происходит сэмплирование анимации во время ее воспроизведения движком.

Анимационные клипы обычно являются фиксированными по частоте кадров. Например, вы можете создать анимацию в 3ds Max или Maya с частотой 60 кадров в секунду (FPS). При импорте анимации в Unity, частота кадров будет прочитана импортером, так что данные импортируемой анимации также будут 60 кадров в секунду (FPS).

Игры обычно работают с переменной частотой кадров. Частота кадров на вашем компьютере может быть выше, чем на других, и также может меняться из-за сложности отрисовки объектов в поле зрения камеры. В основном, это означает, что мы не можем сделать никаких предположений, с какой частотой кадров будет работать игра. Это значит, что даже если анимация создавалась для 60 кадров в секунду, она может быть воспроизведена на другой частоте кадров – например, 56.72, или 83.14 кадров в секунду, или любой другой.

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

Для большинства практических применений, можно игнорировать тот факт, что Unity сэмплирует анимацию на плавающей частоте кадров. Однако если присутствует игровая логика, которая опирается на анимации, которые анимируют трансформации и свойства в очень конкретные конфигурации - то вам нужно знать, что за кулисами применяется ресэмплинг. Например, если у вас есть анимация, которая поворачивает объект от 0 до 180 градусов за 30 кадров, и вы хотите знать из кода, когда была пройдена половина пути, вам не следует делать это с помощью условного выражения в коде, которое проверяет, что текущее вращение равно 90 градусам. Так как Unity сэмплирует анимацию на плавающей частоте кадров игры, сэмпл может быть взят когда вращение немного меньше 90 градусов, а следующий сэмпл - когда вращение немного больше 90 градусов. Если вы хотите получить нотификацию, когда была достигнута конкретная точка в анимации, следует использовать AnimationEvent (Анимационное событие).

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

Animation (анимация)
Legacy GUI