[Из песочницы] Анимация в Spine, советы и рекомендации, псевдо 3D эффект
После просмотра официального гайда возникает много вопросов, поговорим про то, какие подводные камни ожидают нас при работе с этой программой на каждом этапе рабочего процесса (на примере Spine — Unity), как можно оптимизировать свою работу, а так же рассмотрим некоторые популярные фишки типа 3D эффекта. В статье будет много тяжелых гифок.
Сразу оговорюсь, для создания программной анимации в играх есть и другие решения, Dragon Bones, Spriter, Creature, Marionette studio, плагин Puppet 2D и наверняка найдутся другие, просто я как аниматор работаю в Spine.
Схема работы
Когда у нас на руках есть информация и понимание какие персонажи/анимации должны быть созданы для игры, отрисован необходимый материал — можно приступать.
Общая схема работы такова:
Первым делом необходимо подготовить рабочий материал из графических редакторов. Затем ассеты (assets, текстуры) импортируются в Spine и анимируются. На выходе мы получаем Json — файл в котором записаны все ключевые кадры трансформации костей, слотов и проч. Данный файл импортируется в движок, создается специальная skeleton data, которая добавляется на сцене в Spine game object, где наша анимация визуализируется по средствам mesh renderer, запуском можно управлять с помощью кода или же стандартным unity animator.
Непосредственно рабочий процесс в спайне выглядит следующим образом:
- Импорт текстур
- Риггинг (настройка скелета)
- Скиннинг (настройка меша и привязка его к костям)
- Анимация
- Экспорт Json и проверка
В качестве примера попробуем сделать анимацию вот такого персонажа:
Импорт текстур
На этапе подготовки текстур нам важно понимать сколько необходимо проекций персонажа, какая часть работы будет сделана покадрово, какая программно, как грамотно разрезать персонажа и подготовить ассеты.
Типичные ошибки:
А также недостаточное число проекций если персонаж меняет ракурс.
Часто у персонажей есть парные элементы с идентичными изображениями — руки, ноги, глаза, и проч. В таком случае целесообразно сохранить только одну текстуру, и использовать ее два, или более, раз.
В данном случае нам будет достаточно одной проекции, и ассеты художник отрисовал хорошо. Можно приступать к импорту в спайн.
Процесс импорта текстур можно существенно ускорить используя скрипт layers to PNG, который сохраняет каждый слой из фотошопа в отдельное png изображение, при этом записывает Json файл в котором содержится информация о расположении текстур, импортируя его в спайн мы получаем готового собранного персонажа.
2) Импортируем json в спайн file-import data
3) Указываем путь к текстурам
подробнее
Однако у такого способа есть ряд недостатков — скрипт сохраняет изображения без сжатия, что непрактично. Просто пересохранив слои через file-generate-image assets мы уменьшим вес изображения приблизительно в 10 раз.
Далее, необходимо не забывать разворачивать ваши в спрайты в горизонтальное или вертикальное положение, и стирать все лишнее, т.к. скрипт сохранит изображения прямо в том положении в котором они находятся в фотошопе.
Если для проекта необходима покадровая анимация (напр. огонь, спецэффекты, и проч.) — материалы необходимо отрисовать заранее. Далее с помощью adobe after effects и скрипта ae_to_spine мы можем в пару кликов перенести последовательность кадров в Spine.
Рекомендую заранее продумать концепцию наименования, материалы часто приходят в формате «слой 1 (копия) 5», это не слишком практично, для каждой текстуры в проекте необходимо сделать уникальное название, что бы не вызвать проблемы при запаковке атласов. Вполне пригоден вариант формата CharacterVyasya_Skin_a_Hand_R_1.
Тут во многом нам может помочь инструмент find and replace который позволяет быстро переименовывать, указывать путь для большого количества объектов.
Риггинг
Настраивая скелет для персонажа нужно уже думать о том, что, скорее всего, в процессе работы над игрой анимацию нужно будет корректировать, подправлять, поэтому желательно сделать его максимально гибким, что бы процесс правок был максимально простым и быстрым.
Тут может помочь введение дополнительных костей-контролов для разбиения движения по осям X и Y (т.к. в спайне остутствует функция separate dimentions).
Тут же маленький типс, если вдруг чувствуется нагромождение из за излишнего количества костей — попробуйте покрутить настройку bone scale:
Смотрите примеры готовых ригов из образцов поставляемых вместе со спайном, или в сети.
Hwadock Jang
Charles Duchesne
Dao Le Trong
Spine progress
Walk test
Так же полезные материалы можно искать на behanсe, форуме, соответствующих сообществах в социальных сетях.
Скиннинг
Меш — Один из ключевых инструментов в спайне, накладывая на его текстуру мы получаем возможность ее деформировать, искажать, создавать иллюзию 3D.
При создании меша помните: чем меньше вертексов — тем проще контролировать анимацию.
Т.к. деформация меша потребляет ресурсы CPU, при создании сетки стоит руководствоваться принципом необходимого минимума, наглядно это можно рассмотреть на примере рига кончика хвоста динозавра:
Не забывайте про возможность дублирования целой иерархии меша вместе с костями и весами. Это поможет значительно сэкономить время при риге повторяющихся объектов.
Производительность
В конечном итоге у нас возникает вопрос —, а сколько вертексов можно использовать?
Разработчики спайна конкретно не отвечают на этот вопрос, и отсылают к вкладке Spine Metrics, где мы можем отследить суммарное количество элементов в сцене, но при этом говорят о необходимости проведения кастомных тестов производительности.
Важно помнить что трансформация меша, таймлайны, констрейны грузят CPU, а статичная геометрия — GPU. В общем, я бы советовал в случае работы над ключевыми персонажами не сильно заморачиваться по поводу точек, плюс минус десяток не сыграет никакой роли, однако в случае объекта из окружения, который будет повторяться сотню раз, каждый сэкономленный вертекс пойдет на пользу.
И так, когда наш персонаж настроен можно переходить к анимации:
Анимация
Сама по себе анимация это очень большая тема и явно не для обсуждения в рамках одной статьи.
Тут я ограничусь банальными советами в ключе:
- Не забывайте про художественную часть, старайтесь сделать анимацию максимально выразительной
- Следуйте диснеевским принципам анимации
- Импортируйте материал в локацию, часто только после этого становятся видны косяки.
3D-эффект
Это то за что так любят спайн — возможность деформировать изображение с помощью меша тем самым создавая трехмерный эффект.
Для себя я отметил несколько способов создания псевдо 3Д:
- Анимация непосредственно вертексов в меше
- Много костей в ключевых точках меша, анимируются кости
- Кости ставятся выборочно, а далее тщательно настраиваются веса в меше
- Комбинированный способ, все сведено под глобальные контролы
Наглядно принцип построения трехмерного эффекта можно рассмотреть на геометрических примитивах:
Анимируя кости создаем трехмерный эффект:
Можно сделать анимацией вертексов в меше, но как мы видим по сравнению с кубом точек значительно больше:
Аналогично кубу, кости в ключевых точках меша. По затраченному времени этот способ не лучше предыдущего:
Было бы гораздо более гибко свести риг под один глобальный контрол с помощью констрейнов, двигая одним управлять всеми костями:
То же самое, можно сделать без лишней массы костей,
Для этого каждому вертексу тщательно подбирается значение в весах. Такой способ можно считать оптимальным для округлых поверхностей.
Из примеров видно что анимация непосредственно точек в меше это один из самых негибких вариантов, который в случае необходимости будет трудозатратно переделывать. К этому стоит добавить что в некоторых других рантаймах, помимо юнити, эта фича не поддерживается. Поэтому рекомендую по возможности не трогать меш и управлять им с помощью костей.
По аналогии можно сделать деформацию любой геометрической формы, тем самым добавив вашей анимации выразительности.
И так, мы просидели несколько часов над ключевыми кадрами, мешами, и сделали анимацию персонажа:
Тестирование
Далее можно отправлять нашу анимацию в движок. Однако часто возникают ситуации когда внешний вид анимации в спайне и в рантайме существенно отличается, а т.к. каждое взаимодействие аниматор-программист занимает определенное время, лучше заранее провести тест анимации в рантайме, в ходе которого выявятся основные косяки, и только потом отдать работу будучи уверенным что все хорошо.
Если ваш аниматор не дружит с движком, можно использовать Skeleton Viewer
Какие проблемы возникают наиболее часто:
- Вы забыли положить некоторые ассеты, или наоборот в экспорт попало лишнего (рабочие файлы, референсы, и проч) — тут все очевидно — перед тем как отдать материал тщательно проверяйте что должно быть в проекте, а что нет
- несовпадение на стыках анимации — можно попробовать выставить в юнити длительность перехода, тем самым сгладив стык. Или поправить ключи если такое возможно
- текстуры отображаются некорректно
- после отработки анимация выглядит не так как раньше
Некорректное отображение текстур
Чаще всего такое происходит из за неправильной композиции текстуры — меш должен четко соответствовать размеру спрайта.
После отработки анимация выглядит не так как раньше
Происходит это потому что в рантайме анимация просчитывается не из дефолтного положения, а как бы из текущего. Т.е. если мы один раз включим текстуру и не выключим — она останется в таком положении во всех анимациях. Тоже самое и с другими ключами. Это скорее не баг, а фича, но к сожалению аниматор не видит реальной картины пока не импортирует свой материал в рантайм. Решить данный вопрос можно несколькими способами:
1) Проставить ключи на всех свойствах всех объектов в начале и в конце каждой анимации. На первый взгляд слегка муторно, но таким образом вы полностью обезопасите себя от нежданных сюрпризов. Использование фильтров и горячих клавиш ускорит процесс.
Красным выделена иконка фильтра объектов по костям и кнопка для быстрого разворачивания иерархии:
Горячие клавиши по умолчанию:
Key Active: L
Key Selected: ctrl + L
Key Dopesheet: ctrl + shift + L
Key Translation, Key Rotation, Key Scale, Key Shear, Key Color — нужно настраивать самому
2) Использовать скрипт setToSetupPose, который как бы и заставляет проигрываться анимацию из дефолтного состояния. Но такой метод имеет существенный недостаток — Setup Pose выставляется мгновенно, а рендер меняет картинку на соответствующую только со следующего кадра. Таким образом мы имеем проскакивание лишнего кадра между анимациями, выглядит это неприятно. Соответствующее issue уже стоит на доске у разработчиков.
JSON
Мы можем экспортировать из спайна Json, провести с ним какие-либо манипуляции, и импортировать обратно, получив определенный результат. Это очень мощный инструмент т.к. мы получаем доступ ко всем компонентам проекта, в свою очередь нам это может очень помочь в определенных ситуациях:
- При необходимости смержить несколько проектов
- При необходимости скопировать иерархию костей вместе с анимацией
- При необходимости откатиться на более старую версию
- Любое другое применение, которое придет вам на ум
Мержинг проектов
Часто возникает необходимость держать анимации в одной сцене, например как тут:
Для этого есть как минимум два способа: руками и скриптом.Руками
Копируем куски кода из одного json в другой, в соответствующие категории: слоты к слотам, кости к костям, анимации к анимациям, и т.д. Что бы не возникло конфликтов, важно соблюсти корректность наименования объектов, что бы названия из одного json не совпадали с названиями из другого. Проще всего этого добиться заранее в спайне присвоив всем объектам в именах свой уникальный индекс типа _skel1_ и _skel2_. Этот способ слегка затратный по времени, но меня еще не подводил.
{
"skeleton": { "hash": "ZMTMZiuTD2M2gnBhJR0JLPQWOws", "spine": "3.4.02", "width": 0, "height": 0, "images": "" },
"bones": [
{ "name": "root_skel1_" },
{ "name": "bone1_skel1_", "parent": "root_skel1_", "length": 26.95, "rotation": 360, "x": 11.09, "y": -9.65, "color": "00ff00ff" },
{ "name": "bone2_skel1_", "parent": "bone1_skel1_", "length": 26.95, "x": 26.27, "color": "00ff00ff" },
{ "name": "bone3_skel1_", "parent": "bone2_skel1_", "length": 26.95, "x": 26.57, "color": "00ff00ff" },
{ "name": "bone4_skel1_", "parent": "bone3_skel1_", "length": 26.95, "x": 25.97, "color": "00ff00ff" }
],
"animations": {
"animation": {}
}
}
{
"skeleton": { "hash": "osF6oBu7PH6sMNfjN7pm2EwQ8fY", "spine": "3.4.02", "width": 0, "height": 0, "images": "" },
"bones": [
{ "name": "root_skel2_" },
{ "name": "bone1_skel2_", "parent": "root_skel2_", "x": 19.25, "y": 26.63, "color": "fff100ff" },
{ "name": "bone2_skel2_", "parent": "bone1_skel2_", "x": 27.14, "color": "fff100ff" },
{ "name": "bone3_skel2_", "parent": "bone2_skel2_", "x": 25.57, "color": "fff100ff" },
{ "name": "bone4_skel2_", "parent": "bone3_skel2_", "x": 27.14, "color": "fff100ff" }
],
"animations": {
"animation": {}
}
}
{
"skeleton": { "hash": "ZMTMZiuTD2M2gnBhJR0JLPQWOws", "spine": "3.4.02", "width": 0, "height": 0, "images": "" },
"bones": [
{ "name": "root" },
{ "name": "root_skel1_", "parent": "root" },
{ "name": "bone1_skel1_", "parent": "root_skel1_", "length": 26.95, "rotation": 360, "x": 11.09, "y": -9.65, "color": "00ff00ff" },
{ "name": "bone2_skel1_", "parent": "bone1_skel1_", "length": 26.95, "x": 26.27, "color": "00ff00ff" },
{ "name": "bone3_skel1_", "parent": "bone2_skel1_", "length": 26.95, "x": 26.57, "color": "00ff00ff" },
{ "name": "bone4_skel1_", "parent": "bone3_skel1_", "length": 26.95, "x": 25.97, "color": "00ff00ff" },
{ "name": "root_skel2_", "parent": "root" },
{ "name": "bone1_skel2_", "parent": "root_skel2_", "x": 19.25, "y": 26.63, "color": "fff100ff" },
{ "name": "bone2_skel2_", "parent": "bone1_skel2_", "x": 27.14, "color": "fff100ff" },
{ "name": "bone3_skel2_", "parent": "bone2_skel2_", "x": 25.57, "color": "fff100ff" },
{ "name": "bone4_skel2_", "parent": "bone3_skel2_", "x": 27.14, "color": "fff100ff" }
],
"animations": {
"animation": {}
}
}
В результате имеем все в одном проекте:
Проблемы могут возникнуть в проектах с скинами, по возможности старайтесь не менять иерархию костей.
Более сложный пример с лягушкой:
(Осторожно, длинные файлы)
Жук
Лягушка
Жук+Лягушка
Skeleton Merger Tool
Добрые люди написали специальную тулзу для мержинга. Ознакомиться и скачать можно по ссылке. Использовать ее достаточно удобно, включен автоматический ренейминг объектов, однако я не раз сталкивался с ошибками.
Дублирование объектов вместе с анимацией
В спайне отсутствует вложенность композиций (аналог прекомпоз в After Effects или символ во Flash), однако, как показывалось выше, есть возможность продублировать объекты, с сохранением всех зависимостей, но ключи анимации в таком случае не копируются. Не сложно догадаться что, для того что бы не делать анимацию заново, можно переназначить ее на дубликат костей, для этого надо всего лишь перебить в Json имена. Опять же, это проще сделать если у каждой кости будет свой уникальный индекс.
Экспортируем Json и открываем его текстовым редактором. Код анимации walk_1 копируем, находим там все участки _skel_1_:
"animations": {
"walk_1": {
"bones": {
"US_2_skel_1_": {
"rotate": [
{
"time": 0,
"angle": -42.21,
"curve": [ 0.25, 0, 0.75, 1 ]
},
{
"time": 0.4,
"angle": -0.92,
"curve": [ 0.25, 0, 0.75, 1 ]
},
{ "time": 0.8, "angle": -42.21 }
]
},
....
заменяем их на _skel_2_
....
"US_2_skel_2_": {
"rotate": [
{
"time": 0,
"angle": -42.21,
"curve": [ 0.25, 0, 0.75, 1 ]
},
{
"time": 0.4,
"angle": -0.92,
"curve": [ 0.25, 0, 0.75, 1 ]
},
{ "time": 0.8, "angle": -42.21 }
},
...
Вставляем обратно. Таким образом мы переназначили анимацию на другие кости. Сохраняем json, и импортируем его в спайн путем file — import data. Теперь у нас два бегающих муравья.
Откатиться до старой версии
Проекты сохраненные в более новой версии будут не совместимы со старыми. Иногда это может стать проблемой, и для ее решения есть несколько способов: Руками перебить версию в json
"skeleton": { "hash": "", "spine": "3.4.02", "width": 0, "height": 0, "images": "" },
Поможет если между версиями небольшой промежуток, и нет принципиальных различий в алгоритмах работы анимации. JsonRollback tool
Ознакомиться с принципом работы и скачать можно по ссылке.
Резюме
- Четко обозначайте какие задачи в чем вы делаете
- Используйте скрипты для ускорения работы
- Делайте гибкий риг и не перебарщивайте с мешем
- Перед отправкой тестируйте материал в рантайме
- Не стесняйтесь залезть в json если это вам сэкономит время
Спасибо за внимание, наверняка если вы дочитали статью до этого момента у вас возникли вопросы или замечания, с удовольствием прочитаю все в комментариях.
P.S. Статья писалась аниматором по большей части для аниматоров, если вы программист и у вас «рукалицо», можете высказаться в комментариях.