Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед
Всем привет. На связи Fair Pixel. В этот раз мы расскажем про анимации в проекте Erra: Exordium. О том как они повлияли на геймплей и на каком велосипеде мы едем.
Поначалу мы хотели видеть игру неспешной, в угоду эксплорингу. Первый год разработки мы шли по этому пути, попутно создавая внутри игры различные ситуации. Это позволяло понять какие геймплейные ситуации мы хотим дать игроку.
Но с каждым новым механом ускорялся темп геймплея. Приходилось вносить изменения в анимации главного героя. Менялся персонаж. А его визуальное поведение начинало влиять на буквально всё.
Вслед за героем претерпели видимых изменений противники, как в плане движений, так и в плане видимой атаки. Но о противниках мы расскажем в другом выпуске нашего дневника.
Это лишний раз показывает важность предпродакшна и четкого видения геймплея.
А теперь про велосипед “Аниматор”
Без купюр и возможно с позором. В главных ролях: Unity Animator, программисты Fair Pixel.
За кулисами эволюции персонажа, а именно анимациями, есть небольшая история технической части проекта. Так как каждая наша анимация состоит из полностью прорисованных кадров, а в анимациях нет вращений объектов, мы решили, что управлять этим будет проще паренной репы. Ведь есть Animator и всякие там связи, переходы, вызовы методов класса. Вы и сами знаете, как это всё работает.
Первый Pipeline был следующим: художник давал кадры программисту или сам собирал их в анимацию в Unity (просто набор последовательных кадров, иногда с разным межкадровым интервалом), а программист добавлял анимацию в аниматор и настраивал связи.
Количество анимаций росло. Усложнялись состояния. Увеличивалось количество переходов. Это привело нас к тому, что кадры анимаций стали анимациями в аниматоре. И тут ты наверное крутишь палец у виска?!
То ли из-за отсутствия опыта, то ли из-за страха перед увиденным, мы решили, что для нас стандартный Unity Animator слишком шикарен.
Во-первых, мы начали тратить на это много времени. Настройка, отладка - это ещё полбеды. Расширение анимации, логики, вызовы кода, более сложные штуки - вот это была боль.
Во-вторых, мы решили отделить руки с огнестрельным оружием от тела, чтобы не рисовать всего персонажа целиком с оружием на каждый вид состояния. Представь себе, что персонаж с пистолетом может целится стоя, сидя, в движении. И оружие при этом не крутится как объект. Отдельное положение оружие - это конкретный кадр. Типа трупиксель! А потом ещё доставать оружие, прятать, перезаряжать и опять стоя, сидя, в движении… Прогрессия!
Поэтому было решено написать свой велосипед. Который был бы проще в настройках и решал бы вот всё выше написанное. В итоге информация про анимацию хранится в контейнере (благодаря ScriptableObject) и описана тремя полями: идентификатор, межкадровый интервал и набор спрайтов.
Вначале был один класс управляющий анимацией. Его задачей было взять информацию про анимацию и просто переключать спрайты во времени.
С появлением новых требований, менялась архитектура. На данный момент часть архитектуры выглядит так:
AnimationManager находится в каждом персонаже. Он хранит и управляет слоями анимации. LayerController содержит в себе перечень всех анимаций слоя. Анимацией управляет AnimationController. Таким образом, данные анимаций из ScriptableObject превращаются в AnimationController, в котором присутствует набор команд и событий.
Приведем пример на псевдокоде.
В классе CharacterExample показана инициализация аниматора. AnimationManager получает доступ к компоненту объекта (SpriteRenderer) и данные анимации. StateManager сегодня мы обсуждать не будем, кратко скажем, что это машина состояний для разных живых объектов в игре.
Класс IdleState демонстрирует простой вызов смены анимации при старте состояния Idle.
Класс JumpState демонстрирует варианты вызовов и события анимаций. При запуске состояния, аниматор переключится на анимацию JUMP. Затем произойдет переключение на анимацию JUMP_MOTION, когда сработает условие в событии FrameHandler. По завершению той или иной анимации, сработает переход в состояние IDLE. Ещё раз повторим, что это псеквдокод… Простая демонстрация некоторых возможностей.
Таким образом, нам удалось получить полный контроль над анимациями, их отображением и корректировкой геймплея, чтобы не происходило “разрывов” и прочих неприятных вещей.
И вот мы "победили", казалось бы, такую небольшую, но такую важную вещь. Зато теперь живем в мире и согласии с кодом и анимациями.
Нам будет очень интересно выслушать ваши примеры решения собственных велосипедов. А может у вас есть советы как можно было решить нашу проблему.
Будем рады услышать каждого!