Как сделать 2D-анимацию в Unity с минимумом кода: кадры и скрипты
Инди-играм может не хватать визуальной привлекательности. Разбираемся, как оживить персонажей и фоны с помощью самой простой анимации.
Что можно анимировать в Unity
Анимация позволяет оживить игру даже с самой примитивной графикой. Например, персонаж для игры из этой статьи выглядит вот так:
Пишет о разработке сайтов, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
В нём нет ничего привлекательного, это набор простых фигур. Посмотрите, как он изменится, если добавить анимацию:
Теперь персонаж стал чуть более живым и даже забавным — на него гораздо приятнее смотреть, хотя внешне он не изменился.
С помощью анимации можно оживить и фоны. Например, сделать:
Анимацию добавляют и в интерфейсы: переливающиеся шкалы здоровья, поблёскивающие кнопки. Даже вращающаяся шестерёнка вместо простой надписи «Загрузка…» украсит проект.
Мини-игра к этой статье и все ассеты выложены в репозитории на GitHub. Если хотите научиться делать игры с нуля, рекомендуем записаться на курс «Профессия разработчик игр на Unity».
Анимация «из коробки» в Unity
В Unity создают анимацию с помощью:
Скрипт изменяет параметры объекта при определённых условиях: обновлении масштабов, добавлении цвета и так далее.
Что-то более продвинутое делается с помощью покадровой анимации — когда для разных состояний объекта (стоит, идёт, в прыжке, атакует) создаётся несколько спрайтов (двумерных изображений), которые сменяют друг друга с определённым интервалом.
И чуть более сложное — скелеты. Они позволяют использовать один спрайт, а потом приводить его в движение, изменяя положение отдельных частей.
Мы рассмотрим только скриптовую и покадровую анимацию в Unity, потому что для скелетной требуются специальные ассеты, и она куда сложнее.
Анимация вращения с помощью скрипта в Unity
Когда вы создаёте персонажа и добавляете ему спрайт, а потом управляете им, герой всегда смотрит в одну и ту же сторону, даже если движется в другую.
Исправить, создав отзеркаленную версию спрайтов, — не лучший вариант, потому что придётся делать лишнюю работу. Правильнее написать скрипт, который вращает объект.
Анимация в Unity для чайников — проблемы и решения
При разработке игры для нашего прошлого джема мы столкнулись с рядом неожиданных проблем касательно анимации персонажей. В качестве постмортема тогда я написала, что именно у нас вызвало затруднения и какие неочевидные параметры Unity нам очень пригодились.
Для этого фестиваля я неспешно делаю в одиночку 2D-платформер по одной из своих завалявшихся идей. И да, здесь опять анимация, но на этот раз не трехмерная, а спрайт-шитовая. Как оказалось, хоть статья была написана давненько, а проблемы все еще актуальны. И теперь я могу дополнить ее еще и пунктом для спрайтовой анимации.
— полезна тем, кто только начинает работать с анимацией в Unity;
— довольно бесполезна для опытных разработчиков, хотя мне было бы приятно получить от них фидбэк;
— совершенно не нужна тем, кто не имеет ничего общего с Unity и Mecanim. Разве что они хотят почитать про Mixamo.
Если кто не в курсе, Mixamo — это облачная служба автоматического риггинга и банк персонажей и анимаций, а Fuse — это приложение для создания гуманоидных моделей (редактор типа как в Sims), которые потом можно анимировать через Mixamo. К сожалению, в 2015 году все это купила Adobe, которая полностью забила на дальнейшее развитие этих продуктов и прикрутила Fuse к своему Creative Cloud. Что примечательно, все это сейчас совершенно бесплатно — бери не хочу, только зарегайся в Adobe и поставь себе кучу их ненужных сервисов.
Я покажу две кнопочки на Mixamo, которые нам помогли ускорить закачку всего этого добра. Первая из них — в форме черепушки — снимает скин с предпросмотра, позволяя значительно повысить производительность сервиса и предотвратить его падения.
Вторая полезная фишка — в окошечке загрузки, это возможность не загружать скин (модельку), а скачать только анимацию, уменьшив вес загрузки раз в 10. Таким образом, вам нужно только одну анимацию скачать с моделью (with skin, не забудьте пометить как-нибудь этот fbx, чтобы в юнити проще было его найти), а все остальные можно оставить без нее.
Хорошо, модельки и анимации у нас есть, только при импорте в Unity все текстуры куда-то пропали. Что делать? Про магию импорта моделек из Mixamo хорошо рассказывается здесь: https://www.youtube.com/watch?v=xOeodlLTx8g
А на скриншоте ниже показаны две волшебные кнопки, которые и достают текстуры и материалы из моделек (здесь и далее версия Unity 2017.4.0f1).
И не удивляйтесь, если у вас получаются вот такие ресницы:
А дальше мы переходим к самому интересному…
Корень всех проблем, как обычно, заключался в том, что мы успели совершенно забыть даже то немногое, что знали про Mecanim ранее, и начинали практически с нуля. В общем, вот вам список проблем, над которыми нам пришлось помучаться.
Проблема 1: как зациклить анимацию (например, ходьба или idle). Или же наоборот, не зацикливать. Мы помнили, что где-то этот флажок был, но проискали его целый вечер. Вот он:
Проблема 2: как прервать анимацию другой анимацией. Например, как запустить анимацию выстрела в момент нажатия кнопки игроком, не дожидаясь окончания 4-х-минутного айдла «я-держу-ружье». А по умолчанию Unity делает именно это. Когда вы создаете переход, ему автоматически ставится флажок Has exit time — это значит, что переход между анимациями начнется тогда, когда первая анимация подойдет к концу. Если нужен переход в момент триггера, просто снимите этот флажок.
Проблема 3: как задать нужный момент в анимации. Например: вот у вас есть анимация «бью-мечом». Она состоит из замаха, собственно момента поражения врага и возвращения меча на место. Любая анимация какой-либо атаки строится по этому принципу. Как определить момент, в который нужно сделать проверку попадания и вычесть хиты? А точнее, как это сделать лучше и правильней? Потому что для этого есть как минимум три способа:
Проблема 4: состояние смерти. Trigger vs bool. Триггер, если кто опять не в курсе, это такой bool-параметр анимации, который сам выключается где-то там после запуска этой анимации. Очень удобно использовать для запуска тех же анимаций атаки. Когда именно он выключается и что там потом происходит — этого никто не знает. И в этом кроется огромная проблема. У нас персонажи после проигрывания анимации смерти по триггеру снова возвращались в idle-состояние. И происходило это даже тогда, когда уже было полностью исключено как в коде, так и в аниматоре (см. скриншот). Так и не выяснив причину, мы решили поменять тип параметра на bool. И столкнулись с еще одной неприятной проблемой: персонажи зависали в начале анимации смерти и дальше не продвигались. Происходило это от того, что анимация смерти все время переходила сама в себя. Как оказалось, это лечится простым флажком:
Проблема 5 (для классических спрайтовых анимаций):
И вот в очередной раз принимаемся за анимацию главного персонажа, вроде бы все на месте, но откуда-то возникают задержки (заметите на гифке?)
Оказалось, что проблема в длительности переходов. По умолчанию юнити делает плавные переходы между анимациями, для костевых и параметрических анимаций это подойдет, но для спрайт-шитов это ни разу не нужно. После зануления длительностей переходов сразу стало видно, что не так в машине анимаций, и я смогла ее довести до приемлемого состояния. (На скриншоте в этот раз Unity 2019.2)
Как обрабатывать анимацию, в которую зашито движение (root motion). Благо, большинство анимаций Mixamo имеют флажок In place, который отключает рут-моушн. Но что делать, если вам хочется использовать в игре подобную анимацию, а возможности убрать у нее перемещение нет? Как отключить его или хотя бы скормить это перемещение navmesh-агенту — мы так и не придумали. Поэтому неписи в нашей игре прыгают от врагов, а потом оказываются в начальной точке, и все это выглядит так, будто они телепортируются вокруг зомби (в целом довольно сносно:)) Если кто сможет предложить иное решение, помимо «не использовать анимации с вшитым движением» — welcome, очень жду. И да, на флажок enable root motion в компоненте аниматора мы пробовали нажимать:)
Скриптинг анимации (Legacy)
Система анимации в Unity позволяет создавать великолепно анимированных персонажей. Она поддерживает блендинг, микширование, сложение анимаций, синхронизацию цикла ходьбы, анимационные слои, контроль всех аспектов проигрывания (время, скорость, веса блендинга), скиннинг мешей с 1, 2 или 4 костями на вершину, а также основанные на физике rag-dolls (тряпичные куклы) и процедурную анимацию. Для получения лучших результатов, рекомендуется почитать о практических подходах и принципах создания персонажей с оптимальной производительностью в Unity на странице Моделирование оптимизированных персонажей.
Вы можете попробовать примеры показывающие готовую анимацию персонажей. Чуть позже, когда вы освоите базовые вещи, можете взглянуть на описание класса Animation.
Смешивание анимации
В современных играх, смешивание анимации является необходимой функцией для обеспечения персонажа плавными анимациями. Аниматоры создают отдельные анимации, например, цикл ходьбы, цикл бега, анимация в состоянии покоя или стрельбы. В любой момент во время игры, должна быть возможность перехода с анимации спокойствия в цикл ходьбы и обратно. Естественно, вы хотите чтобы этот переход был плавным и без внезапных рывков в движении.
В этом случае смешивание анимации становится полезным. В Unity можно иметь любое количество анимаций, которые проигрываются на одном и том же персонаже. Все анимации смешиваются или складываются вместе для создания окончательной анимации.
Наш первый скрипт для анимации персонажа довольно простой; нам нужен способ для определения скорости движения персонажа, а после этого делать переход между анимациями ходьбы и покоя. Для этого простого теста мы будем использовать стандартные оси ввода:-
Чтобы использовать этот скрипт в вашем проекте:-
После нажатия на кнопку Play, персонаж начнет шагать на месте, пока вы будете удерживать нажатой кнопку со стрелкой вверх, и вернется в позу ожидания, если вы отпустите ее.
Слои анимации
Слои это невероятно полезная концепция, позволяющая классифицировать анимацию и приоритезировать веса.
Веса смешивания всегда нормализуются перед применением
Предположим, что у вас есть цикл ходьбы и цикл бега, имеющие веса, равные 1 (100%). Когда Unity генерирует окончательную анимацию, веса нормализуются, то есть вклад цикла ходьбы составит 50% анимации, и вклад цикла бега также составит 50%.
Типично, вам захочется указать, какая анимация получает больший вес, когда проигрывается две анимации. Конечно, можно вручную убедиться, что веса суммируются в 100%, но легче просто использовать для этого слои.
Пример использования слоев
Самый простой способ добиться этого, это просто продолжать проигрывание анимаций ходьбы и покой во время стрельбы. Чтобы это сделать, нам надо удостовериться, что анимация стрельбы находится в верхнем слое над анимациями покоя и ходьбы, что означает, что анимация стрельбы будет получать веса смешивания в первую очередь. Анимации ходьбы и покой будут получать веса только если анимация стрельбы не использует все 100% веса. Так, когда начинается переход в анимацию стрельбы, вес начнется с нуля, и за короткое время станет 100%. Вначале, слой ходьбы и покоя все еще будет получать веса, но когда полностью закончится переход в анимацию стрельбы – они не получат веса вообще. И это в точности то, что нам нужно!
По умолчанию, animation.Play() и animation.CrossFade() остановят или плавно уберут анимации находящиеся в одном том же слое. Это то что требуется в большинстве случаев. В нашем примере стрельбы, покоя и бега, проигрывание покоя и бега не будет влиять на анимацию стрельбы, и наоборот (если требуется, то это поведение можно изменить опциональным параметром функции animation.CrossFade).
Смешивание анимаций
Смешивание анимаций позволяет урезать количество анимаций, который нужно создать для вашей игры. Это достигается тем, что некоторые анимации влияют только на часть тела. Это значит, что такие анимации могут быть использованы совместно с другими анимациями в разнообразных комбинациях.
Добавление трансформации смешивания анимаций производится вызовом метода AddMixingTransform() имеющегося AnimationState.
Пример смешивания
Другой пример с использованием путей.
Аддитивные (additive) анимации
Аддитивные анимации и технология смешивания анимаций позволяют уменьшить общее количество анимаций, которые вам надо создать для вашей игры, и эти техники также важны для создания лицевой анимации.
Предположим, вам захотелось создать персонажа, который наклоняется в стороны во время поворотов, когда он ходит или бегает. Это приводит к 4 комбинациям (идти-наклоняться-влево, идти-наклоняться-вправо, бежать-наклоняться-влево, бежать-наклоняться-вправо), для каждой из которых нужна анимация. Создание отдельной анимации на каждую комбинацию, очевидно, ведет к множеству дополнительной работы, даже в таком простом случае. Но количество комбинаций увеличивается с каждым добавляемым действием. К счастью, аддитивные анимации и смешивание позволяет избежать необходимости создания отдельных анимаций для комбинаций простых движений.
Пример аддитивной анимации
Аддитивные анимации позволяют накладывать эффекты одной анимации поверх любых других запущенных анимаций. Когда генерируются аддитивные анимации, Unity вычислит разницу между первым и текущим кадрами анимационного клипа. Затем разница применится поверх всех остальных проигрываемых анимаций.
Ссылаясь на предыдущий пример, вы могли бы создать анимации для наклонов вправо и влево, и Unity смог бы наложить их на цикл ходьбы, покоя или бега. Это может быть достигнуто с помощью кода следующим образом:-
Совет: При использовании аддитивных анимаций, очень важно также проигрывать какие-то другие не-аддитивные анимации на каждой трансформации, которая также используется в аддитивной анимации. В противном случае, анимации будут добавляться поверх результата последнего кадра. Это точно не то, что вам нужно.
Процедурная анимация персонажей
Иногда вам понадобится анимировать кости вашего персонажа процедурно. Например, вы хотите чтобы голова вашего персонажа смотрела в определенную точку в 3D пространстве. Это действие лучше всего реализовать с помощью скрипта, который отслеживает целевую точку. К счастью, Unity делает это очень простой задачей, так как кости всего лишь являются трансформациями, которые управляют кожей (skinned mesh). Вы можете управлять костями персонажа из скрипта так же, как трансформациями GameObject.
Тряпичные куклы (Ragdolls) создаются таким же способом. Вам просто требуется добавить компоненты Rigidbody, Character Joint и Capsule Collider к различным костям. Это позволит вам создать анимацию вашего персонажа основанную на физике.
Воспроизведение и cэмплирование анимации
Этот раздел рассказывает о том как в Unity происходит сэмплирование анимации во время ее воспроизведения движком.
Анимационные клипы обычно являются фиксированными по частоте кадров. Например, вы можете создать анимацию в 3ds Max или Maya с частотой 60 кадров в секунду (FPS). При импорте анимации в Unity, частота кадров будет прочитана импортером, так что данные импортируемой анимации также будут 60 кадров в секунду (FPS).
Игры обычно работают с переменной частотой кадров. Частота кадров на вашем компьютере может быть выше, чем на других, и также может меняться из-за сложности отрисовки объектов в поле зрения камеры. В основном, это означает, что мы не можем сделать никаких предположений, с какой частотой кадров будет работать игра. Это значит, что даже если анимация создавалась для 60 кадров в секунду, она может быть воспроизведена на другой частоте кадров – например, 56.72, или 83.14 кадров в секунду, или любой другой.
В результате, Unity должен сэмплировать анимацию с различной частотой кадров, и не может гарантировать частоту кадров, для которой анимация создавалась. К счастью, анимации для 3D графики состоят не из дискретных кадров, а из непрерывных кривых. Так что, если игра выдает большую частоту кадров, чем частота кадров анимации, анимация будет выглядеть плавнее в игре, чем она выглядела в анимационном ПО.
Создание игры на ваших глазах — часть 7: 2D-анимации в Unity («как во флэше»)
В этой статье поговорим о 2D анимациях в Unity. Я расскажу о своем опыте работы с родными анимациями в юнити, о том, насколько тайм-лайны похожи на флэшевские, об управлении анимациями, event’ах, вложенности, и о том, как художник справляется с анимированием.
Для начала, немного теории.
В Unity есть две сущности:
1. Анимация (то, что отображается в окно «Animation»)
2. Mechanim дерево анимаций (то, что отображается в окне «Animator»).
Ниже я немного расскажу, что это такое и как нам может приходиться (или не пригодиться).
Animation
Итак, анимация. По сути — это таймлайн с ключевыми кадрами. Здесь вы можете двигать, поворачивать, масштабировать ваши объекты. Естественно, можно рисовать кривые и пользоваться разными изингами. И даже управлять любыми (в т.ч. самописными) их свойствами. То есть вполне можно написать компонент с float паблик-значением «яркость» и эту самую «яркость» анимировать наравне с x, y, z штатными средствами. Спрайты поддерживают покадровую анимацию.
Кстати, несмотря на то, что у каждой анимации есть FPS (поле «sample»), сами анимации к FPS не привязаны. Они привязаны ко времени. Т.е. если вы делаете анимацию с 5 FPS, где у вас объект двигается из точки А в точку Б с помощью задания двух ключевых кадров в начале и в конце, то в игре этот объект не будет двигаться ступеньками с 5 FPS. Анимация рассчитывается каждый кадр игры, а FPS внутри анимации сделан лишь для вашего удобства, чтобы вам не частить кадры.
Animator
Это — большая и сложная система, которая непосредственно управляет анимациями. То есть анимация — это просто файл (ресурс) с настройками ключевых кадров и сама по себе ничего не умеет. Вот именно компонент «Animator» — это то, что умеет играть эти анимации.
В самом же просто случае, ваш аниматор будет выглядеть так:
то есть содержать одну-единственную анимацию и никаких связей/переходов.
Начинаем шаманить.
Пока все понятно. Но давайте подумаем, как сделать что-то чуть более сложное?
Мой конкретный случай — у нас есть сугроб снега, в котором сидит заяц. Сугроб сам по себе шевелится:
Далее, мы хотим сделать такую анимацию:
1. сугроб, шевелясь, двигается влево
2. из сугроба выглядывает заяц (анимация пульсации останавливается):
3. сугроб двигается вправо
В принципе, ничего сложного. Анимируем пульсацию сугроба внутри объекта, внешним аниматором двигаем его влево, потом скрываем, вместо него показываем покадровую анимацию выглядывающего зайца, потом обратно. И все это на одном таймлайне (кроме «внутренней» анимации сугроба).
Но такой вариант мне не нравится его жесткостью. В первую очередь я считаю неправильным, что в таком варианте покадровая анимация вылезающего кролика оказывается на том же таймлайне, что и движение сугроба. Это значит, что если мы захотим сделать вариацию этой анимации, где сугроб будет двигаться по другой траектории, мы должны будем заново анимировать вылезание зайца. А если мы потом это самое вылезание захотим поправить, то нам придется делать это во всех анимациях, где оно используется.
Хотелось бы большей гибкости.
Есть другой вариант. Мы анимируем выглядывание зайца в отдельном объекте (так же, как мы сделали это с шевелением сугроба), а в основном тайм-лайне просто включаем этот объект (active) в нужный момент и анимация начинается.
Это уже гораздо лучше, но все равно не идеально. Ведь в таком случае мы должны в нашем основном таймлайне знать, какой длины анимация этого вылезания. Чтобы включить и отключить ее в нужный момент. А что если мы опять-таки поменяем эту анимацию и заяц будет дольше смотреть по сторонам? Да и вообще, в каких-то более сложных случаях нам будет еще сложнее уместить все в один таймлайн.
Идеально было бы иметь возможность поставить основной таймлайн на паузу, начать проигрывание вложенной анимации и снять его с паузы уже по окончании этой вложенной анимации (или по какому-то событию в ней).
То есть сделать так:
1. движение влево
2. прячем пульсирующий сугроб, показываем анимацию вылезания кролика, встаем на паузу
3. прячем анимацию вылезание кролика, показываем шевелящийся сугроб, двигаемся вправо
Что нам для этого понадобится? Unity позволяет добавлять на анимацию вызов кастомных юзер event’ов. Это именно то, что нам нужно! Осталось только правильно все написать.
Напишем метод для постановки на паузу. Кстати, в юнити нет такой прямой возможности. Для того, чтобы поставить на паузу анимацию, обычно применяется немного грязный хак с выставлением ее скорости в 0. Он в целом работает, правда, странности есть (об этом — в последней части статьи).
Где _animator — это переменная, в которой мы закешировали компонент » Animator «:
Если вы обратили внимание на скрин выше, над ключевым кадром, который я пометил цифрой «2» стоит небольшая вертикальная черта. Именно за ней скрывается вызов события (метода) «Pause»:
Стоит отметить, что в такие события можно даже передавать параметр. Поддерживаются string, float и объект из библиотеки (не со сцены).
Ок, на паузу мы поставили. Теперь задача — снять с паузы. Очевидно, это должна делать вложенная анимация. То есть доиграла анимация вылезающего кролика до конца, и прокинула наверх события «пошли дальше».
Этот метод ищет среди родителей компонент » GJAnim » и снимает его с паузы. Соответственно, ставим это событие на окончание анимации нашего кролика:
Profit!
Кроме того, все делается в пределах юнитевского UI и достаточно прозрачно для любого аниматора. Наш художник через час попадания ему в руки этого инструмента, уже во всю анимировал.
О багах Unity и сумасшествии.
Однако, не все так гладко. В какой-то момент, создав анимацию, мы увидели, что она ведет себя неправильно.
У нас была родительская (главная) анимация, которая показывала один объект (скрывала все остальные), вставала на паузу, в это время в этом объекте начинала проигрываться своя (вложенная) анимация, которая снимала родителя с паузы по окончании. Далее — показывался следующий объект и т.п.
Так вот, мы заметили, что кадры иногда проскакивают.
Долго-долго дебажили, много писали в лог… и вот что выяснили:
По видимому, в юнити есть какой-то стэк кадров/событий анимаций. И когда компьютер (редактор unity) подтормаживает, он может положить в этот стек сразу два кадра, чтобы в следующую итерацию выполнить их оба.
Это влечет за собой чуть ли не полностью неисправимый фейл. Мы ловили ситуацию, когда аниматор выполнял все действия с кадром и вставал на паузу (это ок), а потом в этот же кадр выполнял еще и следующий кадр. То есть за один кадр рассчитывал сразу два кадра анимации. И то, что в 1-м кадре было событие, ставящее скорость анимации в 0, не мешало ему рассчитать еще и следующий кадр, который, по видимому, уже лежал в стеке.
И если в анимации с кроликом этого бы никто не заметил (кролик бы вылез на пиксель не на том месте), то когда вы каждый кадр что-то прячете и показываете, тут может быть фейл.
На данный момент проблема выглядит неисправимой. Как мы справились? Поставили FPS таких анимаций в 20. Видимо, на таком FPS’е случая, когда юнити хочет просчитать два кадра за одну итерацию — не случается.
Но все равно, ситуация не очень. Получается, что при каких-то фризах на компьютере (или на очень тормозных), все равно игрок сможет словить сбой анимаций.





