Как сделать скелетную анимацию в юнити

Обновлено: 04.07.2024

Недавно я работал над анимацией респауна и спецэффектом главного героя моей игры “King, Witch and Dragon”. Для этого спецэффекта мне нужна была пара сотен анимированных крыс.

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

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

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

Я подробно опишу создание вот этой анимации:

Для начала я создал в Blender низкополигональную модель крысы.

Rat model

Для разделения модели на разные части (тело, хвост, лапы) я использую UV-координаты. Чтобы всё работало как нужно, модель нужно развернуть особым образом.

Rat UV

Основное движение крысы — это прыжки. Чтобы все вершины анимировались плавно и синхронно, нам нужно разместить всё тело крысы вдоль одной из осей. Для удобства я разместил её вдоль горизонтальной оси U.

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

Хвост крысы занимает левую половину развёртки (координаты U от 0,0 до 0,5). Это будет нашей маской хвоста, которую мы используем для анимации хвоста.

Лапы расположены в нижней половине развёртки (координаты V от 0,0 до 0,4). Это наша маска лап. Кроме того, лапы сжаты по горизонтальной оси U, чтобы предотвратить нежелательную деформацию при движении вперёд-назад. Так как в своём проекте я использую cel-shading без детализованной текстуры, сжатые UV не вызовут проблем.

На основании этой UV-развёртки я создал диффузную текстуру. Теперь мы можем начать работу над шейдером.

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

Сначала я создам этот шейдер в Shader Graph движка Unity, а затем покажу текстовую версию.

Давайте создадим Unlit Graph, в качестве Preview выберем Custom Mesh, а затем выберем модель крысы.


Накладываем текстуру

Создадим новый параметр Texture 3D, это будет наша основная диффузная текстура. Создадим нод Sample Texture 2D, соединим наш новый параметр с его полем texture, а затем соединим нод с полем Color нода Master.


В дальнейшем мы будем работать только с вершинами.

Основное движение

Мы создадим его при помощи синусоиды. Чтобы настроить форму волны, нам нужны три параметра:

  • Jump Amplitude — высота прыжков крысы
  • Jump Frequency — частота прыжков крысы
  • Jump Speed — скорость движения вдоль вертикальной оси


Два основных нода, которые позволят нам создать анимированное движение — это Time и UV. В ноде UV нам нужно будет использовать каждую ось по отдельности, поэтому мы соединим его с нодом Split, который даст нам доступ к каждому из каналов.

Мы можем управлять скоростью прокрутки синусоиды, умножив нод Time на параметр Jump Speed.

Если умножить горизонтальный компонент UV на Jump Frequency, то мы сможем управлять сжатием и растягиванием синусоиды.

Сумма этих произведений даст нам синусоиду нужной формы. По умолчанию синусоида возвращает значения в интервале от -1,0 до 1,0. Если мы умножим их на Jump Amplitude, то получим траекторию прыжка.

Building sive wave

Теперь нам нужно применить результат к позициям вершин, но только к их вертикальной компоненте (локальной оси Y). Мы используем нод Position и соединим его с нодом Split. Затем прибавим значение синусоиды к компоненте Y и соберём всё вместе при помощи нода Combine. Подключим выход этого нода к полю Vertex Position нода Master.

Apply Y-offset

Крыса начала двигаться, но не совсем так, как нам нужно. Она больше походит на дельфина, чем на крысу.

Absolute values of the sine wave

Сверху — обычные значения, снизу — абсолютные.

Давайте добавим этот нод к нашему графу.

Graph absolute node

Теперь анимация стала более скачущей.

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

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

Sine wave vertical offset

Сверху — абсолютное значение, посередине — со смещением, внизу — максимум между синусом и нулём.

Чтобы реализовать это в графе, нам нужен новый параметр Jump Vertical Offset, который позволит нам настраивать величину сдвига синусоиды.

Graph abs sine offset

Теперь крыса может какое-то время оставаться на земле.

Дополнительное раскачивание хвоста

В целом всё и так выглядит неплохо, но хвост всегда болтается рядом с землёй. Давайте добавим ему энергичности.

Мы используем UV-координаты для маскировки хвоста и анимирования его отдельно от тела.

Мы знаем, что хвост расположен в левой половине UV-развёртки. Создадим плавный градиент от 0,0 до 0,5 (или даже до 0,6, чтобы эффект был более плавным) по горизонтальной оси U. В 0,0 у нас будет белый цвет, в 0,6 и далее — чёрный. Чем ярче пиксель градиента, тем больше дополнительного движения прикладывается к вершине. По сути, на кончик хвоста он будет влиять сильнее всего, а ближе к телу эффект будет ослабевать.

Для создания этого градиента мы используем нод Smooth Step.

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

Умножив этот новый параметр на выход нода Smooth Step, мы получим распределение движения вдоль хвоста. Затем мы прибавим его к параметру Jump Amplitude, чтобы получить окончательное движение тела, учитывающее дополнительное раскачивание хвоста.

Graph tail mask

Graph tail mask full

Теперь движение хвоста более заметно (Tail Extra Swing = 0.3).

Движение лап

Для движения лап мы используем примерно такую же структуру, что и для тела. Нам потребуется ещё пара новых параметров:

  • Legs Amplitude — величина перемещения лап относительно их позиций по умолчанию
  • Legs Frequency — частота движения лап

Лапы должны двигаться вперёд и назад (и положительное, и отрицательное смещение), поэтому в этом случае нам не понадобится нод Absolute.

Graph legs sine wave

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

Мы снова используем нод Smooth Step, но на этот раз в качестве входного параметра выберем вертикальную ось UV. Давайте зададим градиент от 0,1 до 0,4.

Почему 0,1, а не 0,0? Чтобы избежать деформации лап. Все вершины ниже уровня 0,1 будут иметь одинаковое смещение.

Нам нужно настроить значение Legs Frequency таким образом, чтобы когда передние лапы идут вперёд, задние двигались назад, и наоборот. В моём случае я задал значение 10.

Graph legs mask

Давайте прибавим результат к локальной позиции вершин по Z. Я временно отключил движение тела, чтобы отдельно посмотреть на анимацию лап.

Graph isolated legs movement

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

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

Чтобы решить эту проблему, создадим новый параметр Legs Phase Offset, позволяющий нам компенсировать эту разность фаз и согласовать анимации.

Чтобы это смещение фаз работало, нужно добавить его в нод Time уже после умножения на Jump Speed (чтобы не изменять скорость), но перед всеми остальными манипуляциями.

Graph phase offset

После настройки значения (я указал -1,0) анимация стала правильной.

Вот как это выглядит при нормальной скорости.

Complete graph

Текстовая версия шейдера

Для тех, кто пока не перешёл на URP/HDRP или просто предпочитает писать шейдеры вручную, вот версия в коде:

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

Такой способ подойдёт для фоновых объектов, не находящихся в основном фокусе внимания игрока. Кроме того, по сравнению с рендерерами мешей со скиннингом (Skinned Mesh Renderers), он обеспечивает более высокую производительность.

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; // Меняем скорость передвижени(я это сделал потому что, у этой моделки нету анимаций движения простым шагом с мечом. а понижать скорость анимации у бега получиться не красиво)
if(Input.GetKey(KeyCode.W)) //Если нажать W
<
player.transform.position += player.transform.forward * speed * Time.deltaTime; //Перемещаем персонажа в перед, с заданой скорость. 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 * 2; //Возвращаем 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)) //При нажатии и на кнопку 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; //Поворачиваем персонаж

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?

[AddComponentMenu("Scripts/Mouse Orbit")] //Добавляем в меню

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;
transform.position = 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;
return Mathf.Clamp (angle, min, max);
>
>

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) //Проверяем на изминениея позиции персонажа повертикали, если да
<
if(movePlayer.speed == movePlayer._speed * 2) //Проверяем скорость Передвижения персонажа,
<
animation.CrossFade ("Run00"); //Если зажата клавиша shift, значит грузим анимацию бега
>
else
<
animation.CrossFade("Walk"); //В противном случаее ходьбу
>
>
else if(Input.GetAxis("Vertical") 0.0f)
<
if(movePlayer.speed == movePlayer._speed * 2)
<
animation.CrossFade ("R_Run00"); //бег в право
>
else
<
animation.CrossFade ("R_Walk"); //Шагание в право
>

0

Ребят, а как сделать чтоб при движении назад он разворачивался на 180 градусов, заранее спасибо.

Игровой форум GFAQ.ru


Разработка компьютерных игр. С чего начать? Это просто! С нуля до разработчика игр: как начать создавать свою игру. Заказать создание игры.


Итак. Всем привет. И сегодня я расскажу, как сделать простое движение персонажа. Сейчас только от третьего лица… Приступим…
Начнём, пожалуй, с создания персоажа. У меня это будет куб. Кто не знает, как создавать кубы или круги, поясняю – “GameObject” => “CreateOther” => “Cube”. Создаём таким же образом камеру и привязываем к кубу (то бишь просто в иерархии перетаскиваем камеру на куб).
Так… Теперь создадим поверхность, по которой персонаж будет ходить. Пусть это будет просто “Plane”. Ах, да… В конце урока будет ссылка с исходником по туториалу для тех, кто не понял.
Итак. Теперь создадим скрипт “Move”. Добавим переменную игрока и переменную скорости.

Доброго времени суток друзья, в этой статье я хочу показать вам, как использовать анимацию в Unity 5 при создании 2d игры.

Итак, начнем. Откроем Unity и создадим новый 2d проект

2dUnityProject

Далее создадим две папки с названием Sprite и Animation

Unity5Folder

Ptica2

Перенесли спрайт и теперь выделим его. Справа появится его свойства. В свойствах нам пока понадобится только одно свойство Sprite Mode. Установим режим Multiple, так как наше изображение имеет несколько элементов, и нажмем кнопку Apply.

SpriteMode

Далее, для того чтобы разделить наш спрайт на несколько элементов перейдем в редактор спрайта Sprite Editor в том же свойстве этого спрайта.

SpriteEditor

Перед нами откроется редактор, в котором мы просто выделяем наши 2 элемента по отдельности.

SpriteEditor1

SpriteEditor2

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

Вырезали элементы и далее просто закрываем редактор, на что он спросит вас сохранить изменения или нет.

Итак, наша птица готова, переходим к анимированию. Перенесем спрайт на сцену и Unity спросит у нас куда сохранить файл .anim, в котором будут хранится все последующие анимации данного объекта. Выберем папку Animation и сохраним туда этот файл.

Anim

WindowAnimation

В окне создания анимации выберем наш файл Ptica.anim и создадим новую анимацию Create a new Clip…

PticaAnim

Когда наша новая анимация будет выбрана, рядом в параметре Sample укажем количество кадров 2 , так как у нас всего 2 элемента Спрайта.

Затем перенесем наши 2 элемента в окно редактора анимации, нажмем кнопку записи и кнопку Play.

PticaAnimWiev

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

Перейдем в папку Animation и откроем файл Ptica.anim

AnimSpeed

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

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


И импортируем в проект:


Открываем в окне Project папку с персонажем, находим его модель, переносим в сцену.


Поскольку это модель из магазина AssetStore, она полностью настроена и готова к введению в игру. Тем не менее, важно убедиться, что всё настроено верно:

  1. Во вкладке Rig должен быть выбран AnimationType: Humanoid



Эти анимации можно свободно скачать, что мы и сделаем сейчас.

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

Я подготовил вот такой сет анимаций. Он включает в себя:

  • Holding Idle – анимация “стоим на месте”
  • Walking – “Идем вперед”
  • Walking Backward – “Идем назад”
  • Walk Right и Walk Left – “Идем влево/вправо”
  • Running, Left Run, Right Run – “Бег вперед, влево, вправо”.


Обязательно выберите все файлы с анимациями и во вкладке Rig переключите их в режим Humanoid:


Пора настроить импорт каждой анимации в проект. Выделяем,например, walking и переходим во вкладку Animation:


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

  1. Присваиваем имя анимации, можно назвать так же, как мы назвали файлы.


3.Устанавливаем галочку LoopTime- этот включенный параметр зацикливает анимацию при воспроизведении.


Аналогичным образом настраиваем остальные анимации. Не забываем нажимать Apply для того чтобы применить все изменения анимационного клипа.


Теперь в сцене выделим персонажа. Давайте посмотрим на компонент Animator, который присутствует на нем. Нам важны два поля:


  1. Avatar – определяет, как именно будут связаны кости модели с костями в анимации. При импорте модели персонажа, движок автоматически создает аватар. Но если что-то идет не по плану, аватар можно сконфигурировать вручную. Для этого нужно выбрать модель персонажа в окне проекта, и во вкладке Rig перейти в Configure.


Если Вы всё делаете, как и я – проблем с аватаром возникнуть не должно.

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

Давайте создадим анимационный контроллер: Правой кнопкой мыши кликаем в окне проекта. Create – Animator Controller.


Получится вот такой файл, я назвал его PersonAnimatorController:


Кликаем по нему дважды, откроется вкладка Animator:


Это и есть окно, в котором мы установим алгоритм работы наших анимаций. Давайте сразу создадим 3 состояния (ПКМ – Create State – Empty):



Выделив одно из состояний (например, idle), в окне Inspector мы можем заметить, что поле Motion сейчас пустое. Сюда необходимо поместить анимационный клип (тот, которому мы настраивали ключи Start-End, устанавливали Loop pose, и т.д.).

Поэтому, не снимая выделение с выделенного элемента, в окне Project находим файл с анимацией Idle, и из него перетаскиваем АНИМАЦИОННЫЙ КЛИП в поле Motion в окне инспектора.


Таким же образом устанавливаем клипы в остальные состояния (walk, run).

Теперь в левой части окна Animator переходим во вкладку Parameters:



Создаем Int – параметр, назовем его “state”. Это переменная, которая будет определять, в каком состоянии сейчас находится аниматор персонажа (какое из наших трёх состояний воспроизводится в данный момент). Этим параметром в будущем мы будем управлять из скрипта.


Теперь необходимо создать переходы между состояниями.

Кликаем на Idle правой кнопкой, жмем на Make Transition, после чего наводим курсор на состояние Walk.



Сразу сделаем переходы между всеми состояниями, и туда, и обратно.


Теперь переходим к настройке каждого перехода. Первый переход:


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


Has Exit Time – снимаем галочку. Этот параметр, при включенном состоянии, сначала ждет, пока текущая анимация отыграет полностью, а уже после – совершает переход в другое состояние. Нам это не нужно, т.к. переходы у нас будут в конкретные моменты, когда наша переменная state меняет своё значение.

В Conditions добавляем условие перехода:


Дословно это выглядит так: “Если state = 0”. Если представить это в виде кода:

То есть, выбранный переход сработает в том случае, когда значение state будет равно “1”.

Обратный переход из Walk в Idle настраиваем так (не забываем во всех переходах отключать Has Exit Time):


По аналогии, нужно сделать переходы:

Idle – Run (state equals 2)

Run – Idle (state equals 0)

Walk – run (state equals 2)

Run – Walk (state equals 1)

Не забудьте выделить персонажа в сцене и переместить созданный анимационный контроллер в поле Controller в компоненте Animator:


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

Вы можете заметить, что персонаж немного смещается и разворачивается. Это происходит, потому что мы не настроили смещение и поворот за счёт анимации. Делается это в окне импорта каждого файла анимации, во вкладке Animation. Обычно я настраиваю анимации так, чтобы они никак не влияли на перемещение или поворот. Установите флажок во всех опциях Bake Into Pose.

А так же Based Upon в Original.



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



Называем его PersonController. Перетяните скрипт на персонажа, чтобы он отобразился в списке компонентов под компонентом Animator:


Открываем скрипт. Пишем код:


Теперь, если запустить игру, персонаж начнет реагировать на нажатия на клавиши.

На этом пока всё. Это базовый механизм настройки персонажа, аниматора, и простой способ воспроизвести анимации. На основе этого принципа можно анимировать многие игровые объекты, не только персонажей. Но для того, чтобы сделать качественное смешивание анимаций и не превратить Анимационный контроллер в мешанину из огромной кучи состояний и переменных, которыми сложно управлять, когда их много – существуют очень удобные инструменты смешивания в Animator (BlendTree), а так же возможность создать машину состояний внутри одного состояния. Но об мы расскажем в отдельной статье.

Читайте также: