Скриптинг анимации (Legacy). Воспроизведение и cэмплирование анимации

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

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

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

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

В этом случае смешивание анимации становится полезным. В 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

Пролог.
И снова здравствуйте. Это второй урок по созданию RPG на Unity3D. Изначально я хотел написать его не много по другому но планы у меня изменились и пришлось все предумывать. Сначала задумывалось делать 2 камеры с видо от первого лица и от 3, но в итоге я оставил только от третьего. Сегодня я все скрипты покажу вам полностью сразу и постораюсь объяснить малопонятные вещи в коде дабы экономить свое время и ваше.
В прошлом уроке я забыл написать о «build», то есть непосредственно компиляции проекта.По этому мы сделаем это сейчас. Откроем наш проект. Если открылась пустая сцена нажмем «Ctrl+o» и откроем сцену которую мы делали на прошлом уроке. Нажмем «Ctrl+Shift+b» и в открывшемся окне нажмем «Add Current». Это поставит наше меню в список компилирования и даст ему порядковый номер. При открытии компилированного проекта первой открывается сцена под номером 0. Если нажмем «Build And Run» наш проект скомпилируется и запустится.

План урока.
1) Импорт персонажа.
2) Передвижение персонажа.
3) Настройка камеры
4) Анимация персонажа.
Импорт персонажа.
Сцена.
Создаем «Terrain» с размерами 200х200. Вешаем какую-нибудь текстуру для удобства отображения. Создадим источник света.
Персонаж
Возьмем заранее подготовленного персонажа с анимациями, я взял из «Asset Store»(«Window>>Asset Store» или Ctrl+9) персонажа с названием Blade Girl NPC. Эта моделька стоит 15$ Она у меня уже есть и я буду использовать именно её. Вы можете там же поискать бесплатные(Среди них есть достойные). или же просто в интернете поискать какие нибудь. В эту модель уже впихнута анимация различных действий, атака, бег и тд. и тп. и даже танец Гангнам Стайл))).


После импорта находим находим в окне «Project» префаб персонажа, у меня он называется «Balde_girl_Prefab» и находится в папке «Blade_girl». Перетаскиваем этот префаб на сцену, и видим наш персонаж, только каойто он большой через чур, это не беда. В инспекторе в пункте «Scale» выставим все по единичке, и все хорошо становиться, моделька стала нормальных размеров, можно работать дальше

«Префаб - это один из типов ресурсов, предназначений для многократного использования и хранящийся в Project View. Префаб может быть вставлен в любое количество сцен и многократно в одну сцену. Когда префаб добавляется в сцену, создаётся его экземпляр. Все экземпляры являются ссылками на оригинальный префаб и фактически его клонами. Независимо от того, как много экземпляров в проекте, при изменении префаба изменяются соответственно и все его экземпляры.»

Нажимаем «Add component>>Physics>>Rigidbody» и «Add component>>Physics>>Capsule Collider». В «Capsule Collider» Выставляем размеры нашей капсулы что бы получилось как на картинке, слегка чуть больше модельки самой.


В «Rigidbody» ставим галочку «Use gravity». Раскрываем вкладку «Constraints» и ставим все 3 галочки на «Freeze Rotation». Это нужно для того что бы наш персонаж не проваливался сквозь землю, не падал на землю если вдруг окажется на неровности и тому подобное.
Программируем персонажа на перемещение.
Создадим c# скрипт «movePlayer».

200?"200px":""+(this.scrollHeight+5)+"px");">using UnityEngine;
using System.Collections;

Public class movePlayer: MonoBehaviour {

Private GameObject player; //Переменна объекта персонажа с которым будем работать.

Public static int speed = 6; //Скорость перемещения персонажа. Запись public static обозначает что мы сможем обращаться к этой переменной из любого скрипта
public static int _speed; //постоянная скорость перемещения персонажа
public int rotation = 250; //Скорость пповорота персонажа
public int jump = 3; //Высота прыжка

Public static bool IsDrawWeapon; //Двоичная переменная, которая будет отвечать достануто ли у нас оружие.
public static float x = 0.0f; //угол поворота персонажа по оси x
void Start () {
IsDrawWeapon = false; //По умолчанию оружие у нас спрятано.
_speed = speed; //Задаем постоянное стандартное значение скорости персонажа
player = (GameObject)this.gameObject; //Задаем что наш персонаж это объект на котором расположен скрипт
}

Void Update () {
if(IsDrawWeapon == true) //Если оружие вытащено
{
speed = _speed * 2; // Меняем скорость передвижени(я это сделал потому что, у этой моделки нету анимаций движения простым шагом с мечом. а понижать скорость анимации у бега получиться не красиво)
{
player.transform.position += player.transform.forward * speed * Time.deltaTime; //Перемещаем персонажа в перед, с заданой скорость. Time.deltaTime ставится для плавного перемещения персонажа, если этого не будет он будет двигаться рывками
}
if(Input.GetKey(KeyCode.S))
{
speed = _speed / 2; //При передвижениии назад снижаем скорость перемещения
}
{
speed = _speed * 2; //Возвращаем cтандартное значение
}
{
}
{
}
{
}

If(Input.GetKey (KeyCode.Tab)) //При нажатии и на кнопку Tab
{
IsDrawWeapon = false; //Мы спрячем наше оружие.
}
}
else if(IsDrawWeapon == false) //Если оружие не спрятано. |||||| Сделано разделение на движения в зависимости от того вытащено ли у нас оружие или нет, потому что персонаж будет перемещаться сразной скорость у меня в этих случаях, как я уже написал из за отсутсвия некоторых анимаций у модельки.
{
speed = _speed;//Скорость в стандартное значение
if(Input.GetKey (KeyCode.LeftShift)) //Если зажать левый Shift
{
speed = _speed * 2; //Увеличиваем скорость перемещения(бег)
}
if(Input.GetKeyUp (KeyCode.LeftShift)) //Если отпустить
{
speed = _speed; //Возвращаем стандартное значение
}
if(Input.GetKey(KeyCode.W)) //Если нажать W
{
player.transform.position += player.transform.forward * speed * Time.deltaTime; //Перемещаем персонажа в перед.
}
if(Input.GetKey(KeyCode.S))
{
speed = _speed / 2;
player.transform.position -= player.transform.forward * speed * Time.deltaTime; //Перемещаем назад
}
if(Input.GetKeyUp (KeyCode.S))
{
speed = _speed; //Возвращаем cтандартное значение
}
if(Input.GetKey (KeyCode.A))
{
player.transform.position -= player.transform.right * speed * Time.deltaTime; //перемещаем в лево
}
if(Input.GetKey (KeyCode.D))
{
player.transform.position += player.transform.right * speed * Time.deltaTime; //перемещаем в право
}
if(Input.GetKey (KeyCode.Space))
{
player.transform.position += player.transform.up * jump * Time.deltaTime; //Прыгаем
}
if(Input.GetKey (KeyCode.Tab)) //при нажатии на кнопку таб
{
IsDrawWeapon = true; //Мы вытащим наше оружие
}
}

//Поворачиваем персонажа. Так как наша переменная x глобальна, из скрипта камеры в неё будем записывать длину на сколько сместился указатель мыши и по оси X и относительно этого будет повернут наш персонаж
Quaternion rotate = Quaternion.Euler (0,x,0); //Создаем новую переменную типа Quaternion для задавания угла поворота
player.transform.rotation = rotate; //Поворачиваем персонаж


Скрипт помещаем на персонажа.
Управление камерой.
Создадим еще один c# скрипт, и назовем его «moveCam»

200?"200px":""+(this.scrollHeight+5)+"px");">codeusing UnityEngine;
using System.Collections;

Public class CamMove: MonoBehaviour {
public Transform target; //Объект за которым летаем(Наш персонаж)
public float distance = 3.0f; //На каком ратоянии от него
public float xSpeed = 125.0f; //Чуствительность по Х
public float ySpeed = 50.0f; //Y Чуствительность
public float targetHeight = 2.0f; //Высота относительно объекта
//Минимальный и максимальный угол поворота Y инче камеру разверет, Дальше у нас будет простая функция для инвертации их в обратные числа
public float yMinLimit = -40;
public float yMaxLimit = 80;
//Максимальное удаление и приближение камеры к персонажу, искорость.
public float maxDistance = 10.0f;
public float minDistance = 0.5f;
public float zoomRote = 90.0f;

Private float x = 0.0f; //Угол поворота по Y?
private float y = 0.0f; //Уго поворота по X?

//Добавляем в меню

Public void Start() {
//переворачивам углы
Vector3 angles = transform.eulerAngles;
x = angles.y;
y = angles.x;

If(rigidbody)
rigidbody.freezeRotation = true; //Если камера столкнется с физ.объектомона остановиться
}

Public void LateUpdate() {
if (target) {//Если цель установлена(Персонаж)
//Меняем углы согласно положению мыши
x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
//Меняем дистанция до персонажа.
distance -= (Input.GetAxis ("Mouse ScrollWheel") * Time.deltaTime) * zoomRote * Mathf.Abs(distance);
distance = Mathf.Clamp (distance, minDistance, maxDistance);

Y = ClampAngle(y,yMinLimit, yMaxLimit); //Вызыв самописной функции для ограничения углов поврот
movePlayer.x = x;
//Повернуть камеру согласно поченым данным
Quaternion rotation = Quaternion.Euler(y, x, 0);
transform.rotation = rotation;

//Двигаем камеру и следим за персонажем
Vector3 position = rotation * new Vector3(0.0f, targetHeight+0.5f, -distance) + target.position;

//Следуйщи код нужен что бы камера не проваливалась по ланшафт
RaycastHit hit;
Vector3 trueTargetPosition = target.transform.position - new Vector3(0, -targetHeight,0);
if(Physics.Linecast (trueTargetPosition, transform.position, out hit))
{
float tempDistance = Vector3.Distance (trueTargetPosition, hit.point) - 0.28f;
position = target.position - (rotation * Vector3.forward * tempDistance + new Vector3(0, -targetHeight, 0));
transform.position = position;
}
}

}
//Меняем значения углов
public static float ClampAngle (float angle, float min, float max) {
if(angle < -360)
angle += 360;
if(angle > 360)
angle -= 360;
return Mathf.Clamp (angle, min, max);
}
}


Скрипт кидаем на камеру. Камера должна быть перемещена в префаб персонажа в окне «Hierarchy». А в компоненте скрипта на камере в поле «Target» долже быть помещен наш префаб из окна «Hierarchy».
Анимация
Создадим новый скрипт и назовем его «AnimatePlayer»

200?"200px":""+(this.scrollHeight+5)+"px");">using UnityEngine;
using System.Collections;

Public class AnimatePlayer: MonoBehaviour {

Public void Start ()
{
// Устанавливаем все клипы анимации в режим цикла
animation.wrapMode = WrapMode.Loop;
// Кроме следующих
animation["Attack01"].wrapMode = WrapMode.Once;
animation["jump"].wrapMode = WrapMode.Once;
animation["Skill"].wrapMode = WrapMode.Once;
//У них одиночное выполнение

//Останавливаем выполнение анимаций.
animation.Stop();
}

Public void Update () {
// На основе нажатой клавиши выполняем анимацию
if(movePlayer.IsDrawWeapon == false) //Если оружие не вытащено
{
if (Input.GetAxis("Vertical") > 0.0f) //Проверяем на изминениея позиции персонажа повертикали, если да
{
//Проверяем скорость Передвижения персонажа,
{
animation.CrossFade ("Run00"); //Если зажата клавиша shift, значит грузим анимацию бега
}
else
{
animation.CrossFade("Walk"); //В противном случаее ходьбу
}
}
< 0.0f) //Далее все по анологии
{
if(movePlayer.speed == movePlayer._speed * 2)
{
animation.CrossFade ("B_Run00"); //бег назад
}
else
{
animation.CrossFade ("B_Walk"); //ходьба назад
}
}
else if (Input.GetAxis("Horizontal") > 0.0f)
{
if(movePlayer.speed == movePlayer._speed * 2)
{
animation.CrossFade ("R_Run00"); //бег в право
}
else
{
animation.CrossFade ("R_Walk"); //Шагание в право
}

}
< 0.0f)
{
if(movePlayer.speed == movePlayer._speed * 2)
{
animation.CrossFade ("L_Run00"); //лево
}
else
{
animation.CrossFade ("L_Walk"); //лево
}
}
else if(Input.GetKey (KeyCode.Space)) //если сделан прыжок
{

Animation.Play ("Jump_NoBlade"); //Включаем анимацию прыжка
}
else
{
animation.CrossFade("Idle"); //просто стоим
}
}
else if(movePlayer.IsDrawWeapon == true) //если оружие вытащено
{
if (Input.GetAxis("Vertical") > 0.0f)
{
animation.CrossFade ("Run"); //бег в перед
}
else if(Input.GetAxis("Vertical") < 0.0f)
{
animation.CrossFade ("B_Run"); //назад
}
else if (Input.GetAxis("Horizontal") > 0.0f)
{
animation.CrossFade ("R_Run"); //в право
}
else if(Input.GetAxis("Horizontal") < 0.0f)
{
animation.CrossFade ("L_Run"); //в лево
}
else if(Input.GetKey (KeyCode.Space))
{
animation.Play ("jump"); //Прыжок
}
else
{
animation.CrossFade("AttackStandy"); //просто стоим
}

//Анимация атаки
if (Input.GetMouseButton (0)) //Если нажать маус 1
animation.CrossFade("Attack01"); //Включаем анимацию атаки
if (Input.GetMouseButton (1)) //Если нажать маус 2
animation.CrossFade("Skill"); //Включаем анимацию скила
}
}
}


Скрипт должен быть помещен на персонажа. В окне «Project» создадим папку «Prefab» и перетащим в нее префаб персонажа из окна «Hierarchy» что бы сохранить все на будущее и не повторять все заново.
На сегодня все всем надеюсь моя статья вам будет полезна.
  • Unity
    • Tutorial

    В этой статье поговорим о 2D анимациях в Unity. Я расскажу о своем опыте работы с родными анимациями в юнити, о том, насколько тайм-лайны похожи на флэшевские, об управлении анимациями, event"ах, вложенности, и о том, как художник справляется с анимированием.

    Для начала, немного теории.

    В Unity есть две сущности:

    1. Анимация (то, что отображается в окно «Animation»)
    2. Mechanim дерево анимаций (то, что отображается в окне «Animator»).

    Ниже я немного расскажу, что это такое и как нам может приходиться (или не пригодиться).

    Animation

    Итак, анимация. По сути - это таймлайн с ключевыми кадрами. Здесь вы можете двигать, поворачивать, масштабировать ваши объекты. Естественно, можно рисовать кривые и пользоваться разными изингами. И даже управлять любыми (в т.ч. самописными) их свойствами. То есть вполне можно написать компонент с float паблик-значением «яркость» и эту самую «яркость» анимировать наравне с x, y, z штатными средствами. Спрайты поддерживают покадровую анимацию.

    Кстати, несмотря на то, что у каждой анимации есть FPS (поле «sample»), сами анимации к FPS не привязаны. Они привязаны ко времени. Т.е. если вы делаете анимацию с 5 FPS, где у вас объект двигается из точки А в точку Б с помощью задания двух ключевых кадров в начале и в конце, то в игре этот объект не будет двигаться ступеньками с 5 FPS. Анимация рассчитывается каждый кадр игры, а FPS внутри анимации сделан лишь для вашего удобства, чтобы вам не частить кадры.

    Animator

    Это - большая и сложная система, которая непосредственно управляет анимациями. То есть анимация - это просто файл (ресурс) с настройками ключевых кадров и сама по себе ничего не умеет. Вот именно компонент «Animator» - это то, что умеет играть эти анимации.

    Кроме того, вы можете создавать дерево этих анимаций с морфингом между ними. Т.е. если у вас есть персонаж, анимированный перекладками (когда каждая часть тела - отдельный спрайтик, который вы вращаете/двигаете), то вполне можно сделать анимацию ног отдельно, анимацию рук - отдельно. А потом (с помощью мышки) настроить условие, что от скорости движения вашего объекта, mechanim аниматор будет включать либо анимацю ног «ходьба», либо «бег». А стрелять ваш персонаж будет отдельной анимацией, которая никак не связана со скоростью переставления ног.

    В самом же просто случае, ваш аниматор будет выглядеть так:

    Начинаем шаманить.

    Пока все понятно. Но давайте подумаем, как сделать что-то чуть более сложное?

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

    1. сугроб, шевелясь, двигается влево
    2. из сугроба выглядывает заяц (анимация пульсации останавливается):

    3. сугроб двигается вправо

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

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

    Хотелось бы большей гибкости.

    Есть другой вариант. Мы анимируем выглядывание зайца в отдельном объекте (так же, как мы сделали это с шевелением сугроба), а в основном тайм-лайне просто включаем этот объект (active) в нужный момент и анимация начинается.

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

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

    То есть сделать так:

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

    Что нам для этого понадобится? Unity позволяет добавлять на анимацию вызов кастомных юзер event"ов. Это именно то, что нам нужно! Осталось только правильно все написать.

    Первое, что нам понадобится, это написать простой компонент (в нашем случае он называется GJAnim) и повесить его на тот же объект, на котором висит наш аниматор. Именно методы этого компонента мы сможем вызывать событиями с таймлайна.

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

    Public void Pause() { _animator.speed = 0; } protected void Resume() { _animator.speed = 1; }
    Где _animator - это переменная, в которой мы закешировали компонент " Animator ":

    Animator = GetComponent();

    Если вы обратили внимание на скрин выше, над ключевым кадром, который я пометил цифрой «2» стоит небольшая вертикальная черта. Именно за ней скрывается вызов события (метода) «Pause»:

    Стоит отметить, что в такие события можно даже передавать параметр. Поддерживаются string, float и объект из библиотеки (не со сцены).

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

    Public void ResumeParent() { Transform pr = transform; while (true) { pr = pr.parent; if (pr == null) { Debug.LogWarning("No GJAnim found in parents!"); return; } GJAnim a = pr.gameObject.GetComponent(); if (a != null) { a.Resume(); return; } } }

    Этот метод ищет среди родителей компонент " GJAnim " и снимает его с паузы. Соответственно, ставим это событие на окончание анимации нашего кролика:

    Profit!

    Собственно, все. Мы написали простой компонент, который позволяет управлять вложенными/родительскими анимациями и обладает достаточной гибкостью. Возможно, понадобится еще метод типа ResumeByName(string) , который бы снимал с паузы конкретную анимацию, а не первую родительскую.

    Кроме того, все делается в пределах юнитевского UI и достаточно прозрачно для любого аниматора. Наш художник через час попадания ему в руки этого инструмента, уже во всю анимировал.

    О багах Unity и сумасшествии.

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

    У нас была родительская (главная) анимация, которая показывала один объект (скрывала все остальные), вставала на паузу, в это время в этом объекте начинала проигрываться своя (вложенная) анимация, которая снимала родителя с паузы по окончании. Далее - показывался следующий объект и т.п.

    Так вот, мы заметили, что кадры иногда проскакивают.

    Долго-долго дебажили, много писали в лог… и вот что выяснили:

    По видимому, в юнити есть какой-то стэк кадров/событий анимаций. И когда компьютер (редактор unity) подтормаживает, он может положить в этот стек сразу два кадра, чтобы в следующую итерацию выполнить их оба.

    Это влечет за собой чуть ли не полностью неисправимый фейл. Мы ловили ситуацию, когда аниматор выполнял все действия с кадром и вставал на паузу (это ок), а потом в этот же кадр выполнял еще и следующий кадр. То есть за один кадр рассчитывал сразу два кадра анимации. И то, что в 1-м кадре было событие, ставящее скорость анимации в 0, не мешало ему рассчитать еще и следующий кадр, который, по видимому, уже лежал в стеке.

    И если в анимации с кроликом этого бы никто не заметил (кролик бы вылез на пиксель не на том месте), то когда вы каждый кадр что-то прячете и показываете, тут может быть фейл.

    На данный момент проблема выглядит неисправимой. Как мы справились? Поставили FPS таких анимаций в 20. Видимо, на таком FPS"е случая, когда юнити хочет просчитать два кадра за одну итерацию - не случается.

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

    Что с этим делать - не ясно.

    Допустим, у нас есть несколько объектов на сцене, и мы хотим сделать так, чтобы по щелчку мыши на объекте он совершал некоторое перемещение. Если траектория перемещения прямолинейная, то можно обойтись модификацией положения объекта в его скрипте. Однако, если это что-то сложное и нелинейное, то для этого гораздо правильнее и легче использовать анимацию. Приступим. Создаем новый проект и создаем на сцене пол(Plane):

    и три, например, кубика (добавим источник освещения, чтобы лучше все было видно):

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

    Двойной щелчек по скрипту — и мы в редакторе. Добавим в класс открытую переменную string Name (открытой переменную сделаем для того, чтобы ее значение можно было установить в инспекторе), и добавим метод void OnMouseDown(), в котором выведем в лог информацию о имени кубика, по которому щелкнули мышью:

    Код скрипта:

    Using UnityEngine; using System.Collections; public class CubeBehaviourScript : MonoBehaviour { public string Name; void Start () { } void Update () { } void OnMouseDown() { Debug.Log("Cube " + Name + " clicked!" ); } }

    Нажимаем кнопку Play и щелкаем по кубикам. Внизу окна появится полоска с логом. Щелкнув по этой полоске, окно лога распахнется:

    Все приготовления сделаны, теперь за дело! Создаем объект анимации. Вкладка Project->Create->Animation. Перетаскиваем анимацию на кубики. Выбираем кубик и открываем окно настройки анимации из меню Window->Animation:

    Добавляем ключевые кадры и в них устанавливаем значения для положения кубика. Я установил изменение положения только по оси Y — оно самое наглядное:

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

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

    Лечится это довольно просто. Для каждого кубика мы создадим пустой(Empty) родительский объект, относительно которого у каждого кубика будут координаты (0, 0, 0), а для того, чтобы расположить кубик на сцене, мы будем перемещать родительский объект. Из меню GameObject->Create Empty создаем три пустых объекта и в каждый из них перетаскиваем кубик:

    Затем у каждого кубика устанавливаем координаты (0, 0, 0), а по сцене двигаем родительские объекты. Затем исправляем анимацию. Выделяем кубик. В первом ключевом кадре устанавливаем координаты (0, 0, 0), а затем добавляем еще несколько ключевых кадров и делаем ДЕЙСТВИТЕЛЬНО сложную анимацию:

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

    А теперь самое вкусное, ради чего, собственно, я и потратил время на написание этой статьи. Моей исходной задачей было сделать так, чтобы по щелчку мыши объекты перемещались по хитрой траектории в новое положение, то есть анимация должна проигрываться каждый раз из нового положения. С нашими знаниями о том, что анимация проигрывается в локальной системе координат, сделать это будет проще простого. В конце проигрывания анимации у объекта изменяются его локальные координаты, а координаты родителя остаются теми же. Значит, при очередном проигрывании анимации, нужно родителя сместить на расстояние, равное координатам кубика внутри родителя, а анимация сама в первом ключевом кадре установит у кубика координаты (0, 0, 0). Поскольку теперь проигрыванием анимации мы будем управлять из скрипта, то в инспекторе у каждого кубика в разделе Animation нужно убрать галочку Play Automatically. В первой итерации код скрипта выглядит так:

    Using UnityEngine; using System.Collections; public class CubeBehaviourScript : MonoBehaviour { public string Name; private string animationName = "CubeAnimation" ; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } void OnMouseDown() { if (!animation.IsPlaying(animationName)){ animation.Play(animationName); } Debug.Log("Cube " + Name + " clicked!" ); } }

    Можно нажать кнопку Play и убедиться, что кубики прыгают, если по ним щелкнуть кнопкой мыши.

    Исправим анимацию. Пусть кубики двигаются также по осям X и Z:

    Обратите внимание, мне нужно, чтобы кубик двигался только по оси Z, поэтому в последнем ключевом кадре значения для осей X и Y равны 0, а для оси Z нет. Добавим в скрипт переменную Vector3 delta. Это расстояние, которое прошел кубик за предыдущее проигрывание анимации. Перед каждым проигрыванием анимации мы будем смещать на это расстояние родителя кубика:

    Using UnityEngine; using System.Collections; public class CubeBehaviourScript : MonoBehaviour { public string Name; private string animationName = "CubeAnimation" ; private Vector3 delta; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } void OnMouseDown() { if (!animation.IsPlaying(animationName)){ delta = transform.position - transform.parent.position; transform.parent.Translate(delta); animation.Play(animationName); } Debug.Log("Cube " + Name + " clicked!" ); } }

    Ну, собственно и все 🙂 Теперь наш объект перемещается по замысловатой кривой на заданное расстояние при каждом щелчке мыши.

    English tags: Unity3D single animation object for multiply objects, Unity3D move objects using animation, Unity3D local animation.



    Поделиться: