Найти в Дзене
Сергей Эланд

Делаем свою игру на Unity и изучаем язык программирования C# (Часть 4)

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

Добавляем анимации движений персонажа

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

Как вариант, аниматор можно найти в меню Window - Animation - Animator, либо как мы делали до этого - найдя на игроке компонент Аниматор, кликнуть по полю Controller и дважды щелкунть по найденному файлу
Как вариант, аниматор можно найти в меню Window - Animation - Animator, либо как мы делали до этого - найдя на игроке компонент Аниматор, кликнуть по полю Controller и дважды щелкунть по найденному файлу

Давайте добавим новый блок для анимации, кликнув правой клавишей в любом свободном месте окна Аниматора и выберем Create State -> Empty.

Блоги можно для наглядности перемещать как угодно - связи между ними (стрелочки) не сломаются.
Блоги можно для наглядности перемещать как угодно - связи между ними (стрелочки) не сломаются.

Выбрав новый блок назовём его Death - он будет отвечать у нас за анимацию проигрыша. Воспользуемся еще одной удобной функцией Unity и кликнем в инспекторе на кружочек рядом с полем Motion. Откроется окно, в котором отобразятся все файлы нужного типа, которые есть в нашем проекте (в данном случае анимации). У нашего бородатого мужичка в комплекте оказалась целая куча анимаций, среди которых есть и "Death" (это как правило стандартные названия для них).

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

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

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

Теперь давайте соединим блоки анимаций между собой, кликнув на блоке и выбрав Make Transition (создать переход).

Кликаем правой клавишей мыши по блоку, выбираем Make Transition и левой клавишей мыши  кликаем по блоку, к которому хотим сделать переход.
Кликаем правой клавишей мыши по блоку, выбираем Make Transition и левой клавишей мыши кликаем по блоку, к которому хотим сделать переход.

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

Параметрами анимации будем управлять из нашего кода
Параметрами анимации будем управлять из нашего кода

Теперь кликнув по стрелкам перехода, давайте выберем то, как эти переходы будут осуществлять. По умолчанию стоит галочка рядом с Has Exit Time и это значит, что переход к следующей анимации произойдёт только после того, как первая анимация будет проиграна до конца. Если мы хотим, чтобы персонаж сразу падал, как только закончится здоровье, то эту галочку надо убрать. Внизу же в блоке Conditions с помощью "плюсика" давайте добавим новое условие и выберем isDeath со значением true.

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

Для обратного перехода сделаем тоже самое, только выберем isDeath со значением false. Давайте в скрипте Player создадим метод, который будет управлять этим анимационным параметром и сделаем ссылку на компонент Animator. Компонент Animator перетащим в инспекторе, как мы уже делали с CharacterController'ом, т.к. он висит на том же объекте Player.

Создаём публичное поле для обращения к компоненту Animator.
Создаём публичное поле для обращения к компоненту Animator.
Перетаскиваем компонент в окошко нашего нового поля Animator
Перетаскиваем компонент в окошко нашего нового поля Animator

Новый метод в скрипте Player назовём Death(), а в скобках у него укажем входной параметр типа bool, чтобы мы могли этот метод использовать и для анимации проигрыша и для воскрешения игрока.

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

У аниматора есть функция SetBool(), которая позволяет задать значение любому булевому анимационному параметру данного аниматора. Эта функция имеет 2 аргумента: первый имеет тип string и должен точно соответствовать названию анимационного параметра. Переменная типа string - это текстовая переменная, а текст в коде обозначается ковычками и выделяется эдаким "персиковым" цветом. Второй аргумент это булевая переменная - значение, которое мы хотим выставить.

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

Давайте теперь еще сделаем ссылку на скрипт Player в нашем скрипте Game. Не забываем сохранять скрипты!

Со временем полей в наших скриптах будет становиться все больше и будет хорошо разбить все это на более мелкие и специализированные скрипты. Но пока терпимо :)
Со временем полей в наших скриптах будет становиться все больше и будет хорошо разбить все это на более мелкие и специализированные скрипты. Но пока терпимо :)

В инспекторе на объекте Game в одноименном компоненте рядом с полем Player нажмём "волшебный" кружочек и Unity покажет нам все объекты, на которых присутствует скрипт Player. В нашем случае он такой один - единственный и неповторимый!

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

Теперь давайте в скрипте Game в методе GameOver() добавим строчку с вызовом анимации смерти через метод Death() в скрипте Player.

Наведя мышку на подчеркнутое слово, мы можем узнать, на что именно ругается компилятор Visual Studio
Наведя мышку на подчеркнутое слово, мы можем узнать, на что именно ругается компилятор Visual Studio

По логике все правильно, но компилятор Visual Studio выдаст ошибку и подчеркнет метод Death() красной волнистой линией. Если навести на подчеркнутое слово мышкой, то компилятор выдаст подсказку, что "метод недоступен из-за его уровня защиты". Это потому-что по умолчанию наш метод Death() имеет приватный доступ и его нельзя вызывать извне. Давайте сделаем его публичным.

Чтобы метод можно было вызвать извне, нам нужно сделать его публичным добавив идентификатор доступа "public".
Чтобы метод можно было вызвать извне, нам нужно сделать его публичным добавив идентификатор доступа "public".

Теперь в скрипте Game ошибка пропала и мы можем протестировать в Unity то, что у нас получилось. С ошибками в скриптах в режим Play зайти не получится. Жмём Play, ускоряем процесс с помощью уменьшения hungerTime в инспекторе и видим, что наш персонаж красиво упал на траву.

-15

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

Если в скрипте game переменная isAlive имеет значение true, то передвигать персонажа. В противном случае ничего не делать.
Если в скрипте game переменная isAlive имеет значение true, то передвигать персонажа. В противном случае ничего не делать.

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

Анимации ходьбы у сего джентелмена не оказалось - только бег. Хорошо, пусть пока бегает - спорт полезен для здоровья :)
Анимации ходьбы у сего джентелмена не оказалось - только бег. Хорошо, пусть пока бегает - спорт полезен для здоровья :)

Создадим новый анимационный параметр. На этот раз нам нужен тип float и назовём параметр Velocity (скорость). Будем переключать анимацию в зависимости от скорости движения нашего персонажа. А как нам узнать скорость? Для этого мы можем обратиться к компоненту CharacterController и его методу velocity. Вот только этот метод даст нам вектор, а не числовое значение. Но у него есть удобная функция magnitude, которая даст нам абсолютное значение скорости.

В методе Update() мы будем каждый кадр передавать значение скорости движения персонажа в аниматор с помощью метода SetFloat().
В методе Update() мы будем каждый кадр передавать значение скорости движения персонажа в аниматор с помощью метода SetFloat().

Чтобы теперь настроить переключение анимации с покоя на ходьбу выставим в аниматоре, в настройках переходов, значения в блоках условий Conditions, таким образом, чтобы при скорости меньше (less), чем 0,1 аниматор переходил в анимацию покоя, а при скорости больше (greater) чем 0,1 в анимацию бега.

Для перехода с бега на состояние покоя выберем на анимационный параметр Velocity, условие less (меньше) и значение например 0,1
Для перехода с бега на состояние покоя выберем на анимационный параметр Velocity, условие less (меньше) и значение например 0,1
Скорость анимации можно менять в её настройках
Скорость анимации можно менять в её настройках

Работает! Вот только персонаж явно перебирает ногами быстрее, чем движется. Мы можем легко решить эту проблемы выставив в инспекторе в скрипте Player значение скорость движения персонажа (speed) равным 4. Теперь все выглядит естественно.

Но как долго мы сможем выживать, если будем передвигать все время бегом? Боюсь силы наши закончатся очень быстро. Надо научится и нормально ходить. Для этого давайте вновь отправимся в Unity Asset Store и поищем в категории 3D - Animation, какие-нибудь бесплатные анимации. Например вот такие вполне подойдут:

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

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

-22

Нам подойдёт анимация BasicMotions@Walk01 - Forward. Но название длинное и неудобное. Чтобы вытащить ее из этого FBX-файла и переименовать, например в просто Walk, развернём FBX-файл с помощью стрелочки, найдём нужную анимацию, выберем её и нажмём CTRL+D.

FBX-файлы могут содержать в себе 3D-модели и анимации
FBX-файлы могут содержать в себе 3D-модели и анимации

Теперь у нас есть дупликат этой анимации, который мы можем редактировать, а исходные FBX-файлы при желании можно даже удалить.

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

Поменяем теперь в нашем аниматоре анимацию RunForwards на Walk и наш персонаж начнет вполне неплохо ходить со значением скорости 2.

Пока никаких опасностей нет, можно вполне поэкономить силы и спокойно погулять по лесу.
Пока никаких опасностей нет, можно вполне поэкономить силы и спокойно погулять по лесу.

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

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

Первый урок:

Второй урок:

Третий урок: