Как создать спрайтовую анимацию sonic guide

Обновлено: 14.05.2024

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

За последние два месяца я создал целых 9 новых действий игрока (такие забавные вещи как блокировка щитом, уворачивание в прыжке и оружие), 17 новых носимых предметов, 3 набора брони (пластинчатый, шёлковый и кожаный) и 6 видов причёсок. Также я завершил создавать всю автоматизацию и инструменты, поэтому всё уже используется в игре. В статье я расскажу, как этого добился!

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

№53. AnimatedSprite. Анимированный спрайт.

ВНИМАНИЕ !
Если вы делаете платформер, не используйте показанный тут код. Он написан для примера работы с анимированным спрайтом.
В платформерах, для управления персонажем обычно используется нода KinematicBody2D со всеми вытекающими из этого изменениями.
Почитать про неё можно вот тут .
----------------------

В этой статье разберемся, что такое AnimatedSprite .
Создадим персонажа, подключим к нему простенькое управление, и свяжем это всё вместе.

Персонаж будет бегать влево и вправо, стоять на месте, и атаковать.

Добавляем в сцену AnimatedSprite , заходим с инспектор и жмем New SpriteFrames

Откроется меню работы с анимированными спрайтами.

Добавить в редактор можно как покадровую анимацию, так и спрайт таблицу ( spritesheet — одна картинка на которой собраны в виде таблицы все кадры анимации).

У меня анимация покадровая. Так что просто дропаем из ресурсов кадры в окно редактора.
Начнем с анимации «idle» (стоим на месте).

По умолчанию первой анимации присвоено имя default. Поменяем его на «idle»

У нас всего два кадра. Скорость проигрывания регулируется через значения Speed .
Ну а Loop — это зацикливать анимацию или нет.
В инспекторе ставим галочку возле Playing и сможем наблюдать анимацию во вьюпорте.

Создание скелетной анимации в Spriter


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

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

В качестве примера мы возьмем готового персонажа по имени Foxy, это будет главный герой нашей игры. Для создания проекта в редакторе Spriter необходимо поделить образ персонажа на элементы и сохранить их в отдельные директории. Элементы — это части тела Foxy, которые мы будем прикреплять к «скелету».


Открываем Spriter, создаем новый проект и выбираем корневую директорию со спрайтами. В панели Palette мы видим наши директории, перетаскиваем в рабочую область и восстанавливаем образ Foxy из отдельных элементов. В панели Z-order выстраиваем нужное положение спрайтов по z-координате.


Теперь, если мы выделим спрайт правой кнопкой мыши, редактор нам покажет какие ещё спрайты находятся в директории, таким образом можно быстро менять спрайты во время работы с анимацией. Теперь построим «скелет» нашему персонажу.

В панели Palette выбираем Create bone, с нажатой кнопкой Alt отмечаем кости — выстраиваем скелет. Пара правил по его построению. Если кость создается при выделенной кости, выделенная кость становится родителем созданной. Если на рабочем поле нет выделенных костей, и вы создали кость, в данном случае она будет независимой от скелета. Иерархию скелета можно смотреть в панели Hierarchy.


В панели Hierarchy, вместо сгенерированных названий костей bone_xxx, желательно переименовать их и выбрать более понятные имена — например, head-bone или leg-left-bone. Это поможет легко найти интересующий элемент в коде сгенерированных исходников XML/JSON.


После того, как был сформирован скелет по форме персонажа, необходимо связать каждую кость с лежащим под ней спрайтом. Для этого выделяем кость, нажимаем и удерживаем «B» + клик по спрайту. У нас получился вот такой скелет, в панеле иерархии наглядно демонстрируется последовательность прикрепленных спрайтов.

Мы подошли к анимации. Тем, кто делал анимацию в Adobe Flash, будет знаком привычный Timeline. А для тех, кто в первый раз видит редактор по анимации, не пугайтесь, это стандартный инструмент и работа в нём проста. Переносите бегунок по таймлайну и изменяйте позиции костей. Spriter сохраняет позиции персонажа на ключевых кадрах и просчитывает движение между ними.

На этом этапе виден очевидный плюс — процесс создания анимации занимает в разы меньше времени, чем покадровая отрисовка. В панели Animations создаем дополнительные анимации для нашей игры. Второй плюс скелетной анимации — при увеличении количества анимации увеличивается только количество строк в XML/JSON, а не количество кадров в спрайтовой анимации.


В Spriter есть еще одна очень полезная для игроделов опция — Character Maps. Эта опция позволяет создавать образы на основе готового скелета и набора анимации. В рамках формы и размера вашего персонажа вы можете нарисовать многочисленное количество аксессуаров и новой одежды, или полностью заменить персонажа с идентичным скелетом. И все это в рамках одного проекта!


Пришло время проверить в бою наши наработки. Подготовим наш проект для экспорта в Construct 2 (об этом иструменте для создания HTML5-игр мы писали в предыдущих статьях). Для этого изменим параметры сохранения проекта. Включим в него все подпункты Additional Data for Authoring Tools. У нас есть два файла scml и scon, они абсолютно аналогичны синтаксису XML и JSON.


Следующий шаг — установка плагина на Construct 2 для поддержки скелетной анимации. На форуме Сonstruct 2 можно всегда скачать последнюю версию.

Распакованный плагин представляет из себя набор из трех js-сценариев, перемещаем их в установочную директорию Теперь Construct 2 готов обрабатывать файлы Spriter.

Перетаскиваем файлы scml на рабочее поле в Construct 2 и прописываем события в Event Sheet. Читайте об особенностях написания логики и событий в Construct 2 в наших предыдущих статьях.

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


Для сравнения мы подготовили демонстрационную сцену с двумя типами анимации.


Модульная система спрайтов

I. Познай свои границы

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

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

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

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

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

II. Смена палитры ради светлого будущего

С умом используя цвета в дизайне спрайтов, можно отрисовать один спрайт и создать множество различных вариаций при помощи смены палитры. Можно менять не только цвета, но и создавать различные включаемые и отключаемые элементы (например, заменой цветов на прозрачность).

Каждый набор брони имеет 3 вариации, а смешивая верхние и нижние части, можно получить множество комбинаций. Я планирую реализовать систему, в которой можно собрать один набор брони для внешнего вида персонажа, а другой — для его характеристик (как в Terraria).

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

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

Я не буду объяснять всё в подробностях, а вместо этого расскажу о способах реализации этой техники в Unity, и об их плюсах и минусах.

1. Текстура поиска для каждой палитры

Это наилучшая стратегия для создания вариаций врагов, фонов и всего того, где множество спрайтов иеет одинаковую палитру/материал. Различные материалы нельзя сгруппировать в батчи, даже если они используют одинаковый спрайт/атлас. Работа с текстурами довольно мучительна, но палитры можно изменять в реальном времени, заменяя материалы, с помощью SpriteRenderer.sharedMaterial.SetTexture или MaterialPropertyBlock, если вам нужны разные палитры для каждого экземпляра материала. Вот пример фрагментной функции шейдера:

2. Массив цветов

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


Я представил свои палитры как тип ScriptableObject и использовал для их редактирования инструмент MonoBehaviour. Проработав долгое время над редактированием палитр в процессе создания анимаций в Aseprite, я понял, какие инструменты мне требуются и писал эти скрипты соответствующим образом. Если вы хотите написать собственный инструмент для редактирования палитр, то вот какие функции я обязательно рекомендую реализовать:

— Обновление палитр на различных материалах при редактировании цветов для отображения изменений в реальном времени.

— Присваивание названий и изменение порядка цветов в палитре (используйте поле для хранения индекса цвета, а не его порядка в массиве).

— Выбор и редактирование нескольких цветов за раз. (Совет: поля Color в Unity можно копипастить: просто нажмите на один цвет, скопируйте, нажмите на другой цвет, вставьте — теперь они одинаковы!)

— Применение цвета оверлея ко всей палитре

— Запись палитры в текстуру

3. Единая текстура поиска для всех палитр

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

Во-первых, нужно будет упаковать все палитры в одну большую текстуру. Затем вы используете цвет, заданный в компоненте SpriteRenderer (AKA цвет вершины) для определения строки, которую надо считать из текстуры палитры в шейдер. То есть палитра этого спрайта управляется через SpriteRenderer.color. Цвет вершины — это единственное свойство SpriteRenderer, которое можно менять без нарушения батчинга (при условии, что все материалы одинаковы).

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

Чудеса замены палитр и слоёв спрайтов. Так много комбинаций.

III. Автоматизируйте всё и применяйте подходящие инструменты

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

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

Затем я написал импортёр для Unity. Aseprite выводит удобный файл JSON с данными кадров, поэтому можно создавать ассеты анимаций программно. Обработка Aseprite JSON и запись этого типа данных оказались довольно нудными, поэтому я привожу их здесь. Вы можете с лёгкостью загрузить их в Unity с помощью JsonUtility.FromJson<AespriteData>, только не забудьте запустить Aseprite с опцией --format 'json-array'.


На стороне Unity серьёзные проблемы у меня возникли в двух местах: в загрузке/нарезке спрайтшита и в построении клипа анимации. Мне бы очень помог понятный пример, поэтому вот фрагмент кода из моего импортёра, чтобы вы не так мучились:


Если вы этого ещё не делали, то поверьте — начать создавать собственные инструменты очень легко. Самый простой трюк заключается в размещении в сцене GameObject с привязанным к нему MonoBehaviour, которое имеет атрибут [ExecuteInEditMode]. Добавьте кнопку, и вы готовы к бою! Помните, что ваши личные инструменты не обязаны выглядеть хорошо, они могут быть чисто утилитарными.


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

Как я перерос возможности Mecanim: жалоба

Со временем прототип системы модульных спрайтов, который я создал с помощью Mecanim, стал самой большой проблемой при апгрейде Unity, потому что API постоянно сильно менялся и был плохо задокументирован. В случае простого конечного автомата было бы разумно иметь возможность запрашивать состояния каждого клипа или менять клипы во время выполнения. Но нет! Из соображений производительности Unity запекает клипы в их состояния и заставляет нас использовать для их смены неуклюжую систему переопределений.

Сам по себе Mecanim не такая уж плохая система, но мне кажется, что ему не удаётся реализовать свою основную заявленную особенность — простоту. Идея разработчиков заключалась в том, чтобы заменить то, что казалось сложными и мучительным (скриптинг) чем-то простым (визуальным конечным автоматом). Однако:

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

— Простым случаям использования мешают обобщённые требования системы. Чтобы проиграть одну-две анимации, вам нужно создавать новый контроллер и назначать состояния/переходы. Разумеется, присутствует и излишняя трата ресурсов.

— Забавно, что в результате вам всё равно приходится писать код, ведь чтобы конечный автомат делал что-то интересное, нужен скрипт, вызывающий Animator.SetBool и подобные ему методы.

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

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

Рассказ о том, как разработчики Firewatch попали в ад визуального скриптинга. Самое забавное в том, что когда докладчик показывает наиболее простые примеры, они всё равно выглядят безумно. Зрители в буквальном смысле стонут на 12:41. Добавьте огромные затраты на обслуживание, и вы поймёте, почему я сильно не люблю эту систему.

Многие из этих проблем даже не вина разработчиков Mecanim, а просто естественный результат несовместимых идей: нельзя создать общую и в то же время простую систему, а описывать логику при помощи изображений сложнее, чем просто словами/символами (кто-нибудь помнит флоучарты UML?). Я вспомнил фрагмент из доклада Зака Макклендона на Practice NYC 2018, и, если найдётся время, рекомендую вам посмотреть видео целиком!

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

Если вы уже немного программист и делаете игры со спрайтами, то вам возможно стоит подумать дважды. Когда я начинал, то меня был уверен, что никогда не смогу написать что-то связанное с движком лучше, чем разработчики Unity.

Опрос

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

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

Краткое описание

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

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

Я раскопал этот древний GIF моего первого месяца работы с Unity. На самом деле этот модульный спрайт оказался одним из первых моих экспериментов в разработке игр!

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

В то время я создавал всю свою анимацию в Photoshop и не заморачивался автоматизацией процесса, поэтому он был очень скучным. Потом я подумал: «Так, основная идея сработала, позже я добавлю новые анимации и снаряжение». Оказалось, что «потом» — это несколько лет спустя.

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

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

Спойлер: всё получилось замечательно. Ниже я раскрою свои ***секреты***

Спрайтовая анимация на CSS 3


Спрайтовая анимация — одна из тех вещей, которые при всей своей примитивности успешно работают и применяются в компьютерной графике и играх уже больше четверти века. Даже в трехмерных играх есть спрайты — например, билборды взрывов. Во многих браузерных и флеш-играх применяют именно спрайтовую анимацию, так как она очень проста и не требует высокой производительности — просто переключай кадры и все! А с появлением анимации в CSS 3 стало возможным использовать спрайты на своих страницах без яваскриптов.

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

Все, что нам надо с ним сделать, это поставить фоном в div и менять у него со временем background-position. Вроде бы все просто:
  1. .sprite
  2. position : absolute ;
  3. left : 50% ; /* положение спрайта */
  4. top : 33% ;
  5. width : 32px ; /* его размер */
  6. height : 32px ;
  7. margin : -16px 0 0 -16px ; /* просто чтобы он был по центру :-) */
  8. background : url ( sprite.jpg ) no-repeat 0 0 ; /* фон */
  9. -webkit-animation-name: sprite ; /* название анимации */
  10. -webkit-animation-duration: .3s; /* интервал в 300 миллисекунд */
  11. -webkit-animation-iteration-count: infinite ; /* повторять бесконечно */
  12. -webkit-animation-timing-function: linear ; /* использовать линейную функцию */
  13. >
  14. /* анимация sprite */
  15. @-webkit-keyframes sprite
  16. /* перемещаем фон спрайта три раза, на четвертый возвращаем обратно в 0 */
  17. 0%
  18. background-position : -0px 0 ;
  19. >
  20. 33%
  21. background-position : -32px 0 ;
  22. >
  23. 66%
  24. background-position : -64px 0 ;
  25. >
  26. 100%
  27. background-position : -0px 0 ;
  28. >
  29. >


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

  1. @-webkit-keyframes sprite
  2. 0%
  3. background-position : -0px 0 ;
  4. >
  5. 33.332%
  6. background-position : -0px 0 ;
  7. >
  8. 33.334%
  9. background-position : -32px 0 ;
  10. >
  11. 66.665%
  12. background-position : -32px 0 ;
  13. >
  14. 66.667%
  15. background-position : -64px 0 ;
  16. >
  17. 99.999%
  18. background-position : -64px 0 ;
  19. >
  20. 100%
  21. background-position : -0px 0 ;
  22. >
  23. >


Код получился довольно объемный, но результат нас почти устраивает, если бы не одно «но»: при маленьких интервалах можно наблюдать рывки.
Для полностью правильной анимации надо прибегнуть к другой хитрой возможности CSS 3: увеличению размера. Для этого установим ширину и высоту кадра в 1 пиксель и при помощи свойства transform увеличим спрайт до наших 32 пикселей. Так как transform влияет не только на размер самого элемента, но и на его фон, его размер установим в 3 пикселя по ширине и 1 по высоте, и в самой анимации сдвигать будем так же по 1 пикселю. Вместе с увеличением оно как раз и будет равно 32 пикселям.

  1. .sprite
  2. position : absolute ;
  3. left : 50% ;
  4. top : 33% ;
  5. width : 1px ; /* размеры элемента ставим в 1 пиксель */
  6. height : 1px ;
  7. margin : -16px 0 0 -16px ; /* на отступы увеличение через transform не влияет */
  8. background : url ( sprite.jpg ) no-repeat 0 0 ;
  9. background - size : 3px 1px ; /* размер фона также уменьшаем */
  10. -webkit-animation-name: sprite ;
  11. -webkit-animation-duration: .3s;
  12. -webkit-animation-iteration-count: infinite ;
  13. -webkit-animation-timing-function: linear ;
  14. -webkit-transform: scaleX( 32 ) scaleY( 32 ); /* увеличим размер элемента */
  15. -webkit-transform-origin: top left ;
  16. >
  17. @-webkit-keyframes sprite
  18. /* сдвигаем фон по 1 пикселю */
  19. 0.000%
  20. background-position : -0px 0 ;
  21. >
  22. 25.000%
  23. background-position : -1px 0 ;
  24. >
  25. 50.000%
  26. background-position : -2px 0 ;
  27. >
  28. /* честно говоря, сам не понял, зачем надо сдвигать */
  29. /* еще на один пиксель (ведь это должен быть */
  30. /* пустой кадр). но иначе оно не покажет все */
  31. /* кадры анимации. */
  32. 75.000%
  33. background-position : -3px 0 ;
  34. >
  35. 100.000%
  36. background-position : -0px 0 ;
  37. >
  38. >


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

  1. function generateKeyframes( $count , $sprite_width )
  2. $result = '' ;
  3. $count ++;
  4. for ( $i = 0 ; $i <= $count ; $i ++)
  5. $result .= sprintf( "\t%.3f%%\n\t\n" , $i * 100 . 0 / $count , ( $i % $count ) * $sprite_width );
  6. >
  7. return $result ;
  8. >


Увы, этот способ пока что малоприменим на практике, так как работает только в Webkit-браузерах типа Chrome или Safari. В Firefox анимация так и останется плавной, в Opera показывается только первый кадр, а в IE оно вообще не работает. Так что анимация с использованием Javascript и Flash будет держать свои позиции еще довольно долго.

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