реклама
разместить

Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед

Всем привет. На связи Fair Pixel. В этот раз мы расскажем про анимации в проекте Erra: Exordium. О том как они повлияли на геймплей и на каком велосипеде мы едем.

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

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

С чего мы начали...
...и к чему пришли.

Вслед за героем претерпели видимых изменений противники, как в плане движений, так и в плане видимой атаки. Но о противниках мы расскажем в другом выпуске нашего дневника.

Это лишний раз показывает важность предпродакшна и четкого видения геймплея.

А теперь про велосипед “Аниматор”

Без купюр и возможно с позором. В главных ролях: Unity Animator, программисты Fair Pixel.

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

Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед

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

Количество анимаций росло. Усложнялись состояния. Увеличивалось количество переходов. Это привело нас к тому, что кадры анимаций стали анимациями в аниматоре. И тут ты наверное крутишь палец у виска?!

Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед

То ли из-за отсутствия опыта, то ли из-за страха перед увиденным, мы решили, что для нас стандартный Unity Animator слишком шикарен.

Во-первых, мы начали тратить на это много времени. Настройка, отладка - это ещё полбеды. Расширение анимации, логики, вызовы кода, более сложные штуки - вот это была боль.

Во-вторых, мы решили отделить руки с огнестрельным оружием от тела, чтобы не рисовать всего персонажа целиком с оружием на каждый вид состояния. Представь себе, что персонаж с пистолетом может целится стоя, сидя, в движении. И оружие при этом не крутится как объект. Отдельное положение оружие - это конкретный кадр. Типа трупиксель! А потом ещё доставать оружие, прятать, перезаряжать и опять стоя, сидя, в движении… Прогрессия!

Поэтому было решено написать свой велосипед. Который был бы проще в настройках и решал бы вот всё выше написанное. В итоге информация про анимацию хранится в контейнере (благодаря ScriptableObject) и описана тремя полями: идентификатор, межкадровый интервал и набор спрайтов.

Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед

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

Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед

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

Дневник разработки Erra: Exordium #2: Покадровая анимация, код и велосипед

AnimationManager находится в каждом персонаже. Он хранит и управляет слоями анимации. LayerController содержит в себе перечень всех анимаций слоя. Анимацией управляет AnimationController. Таким образом, данные анимаций из ScriptableObject превращаются в AnimationController, в котором присутствует набор команд и событий.

Приведем пример на псевдокоде.

В классе CharacterExample показана инициализация аниматора. AnimationManager получает доступ к компоненту объекта (SpriteRenderer) и данные анимации. StateManager сегодня мы обсуждать не будем, кратко скажем, что это машина состояний для разных живых объектов в игре.

public class CharacterExample : IActor { private AnimationManager _animations; private SpriteRenderer _sr; private StateManager _states; public AnimationManager Animations { get { return _animations; } } public StateManager State { get { return _states; } } public CharacterExample(Animation[] animations) { _animations = new AnimationManager(_sr, animations); _states = new StateManager(); _states.Add(States.IDLE, new IdleState(this)); _states.Add(States.JUMP, new JumpState(this)); } public void Update() { _animations.Update(); _states.Update(); } }

Класс IdleState демонстрирует простой вызов смены анимации при старте состояния Idle.

public class IdleState : State { private CharacterExample _character; public IdleState(CharacterExample character) : base(character) { _type = States.IDLE; } public void Start() { _character.Animations.ChangeAnimation(AnimationsLayers.BASE, Animations.IDLE); } public void Update() { if (Input.Jump.Down) { _character.State.Set(States.JUMP); } else if (Input.Horizontal != 0f) { _character.State.Set(States.WALK); } } }

Класс JumpState демонстрирует варианты вызовов и события анимаций. При запуске состояния, аниматор переключится на анимацию JUMP. Затем произойдет переключение на анимацию JUMP_MOTION, когда сработает условие в событии FrameHandler. По завершению той или иной анимации, сработает переход в состояние IDLE. Ещё раз повторим, что это псеквдокод… Простая демонстрация некоторых возможностей.

public class JumpState : State { private CharacterExample _character; private AnimationController _animationJump; private AnimationController _animationJumpMotion; public JumpState(CharacterExample character) : base(character) { _type = States.JUMP; _animationJump = _character.Animations.Get(AnimationsLayers.BASE, Animations.JUMP); _animationJump.FrameHandler += AnimationJump_FrameHandler; _animationJump.EndHandler += AnimationJump_EndHandler; _animationJumpMotion = _character.Animations.Get(AnimationsLayers.BASE, Animations.JUMP_MOTION); _animationJumpMotion.EndHandler += AnimationJump_EndHandler; } public void Start() { _character.Animations.ChangeAnimation(Characters.AnimationsLayers.BASE, Characters.Animations.JUMP); } private void AnimationJump_FrameHandler(AnimationController controller) { if (controller.CurrentFrame > 2) { _character.Animations.ChangeAnimation(AnimationsLayers.BASE, Animations.JUMP_MOTION); } } private void AnimationJump_EndHandler(AnimationController controller) { _character.State.Set(States.IDLE); } }

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

И вот мы "победили", казалось бы, такую небольшую, но такую важную вещь. Зато теперь живем в мире и согласии с кодом и анимациями.
Нам будет очень интересно выслушать ваши примеры решения собственных велосипедов. А может у вас есть советы как можно было решить нашу проблему.
Будем рады услышать каждого!

6969
реклама
разместить
8 комментариев

Это да, аниматор юнити не самый приятный товарищ. 

3

не только нам так показалось?

1

анимации прикольные)  желаю удачи) 

2

Спасибо)

1
Джеймс Кэмерон: «„Аватар 3“ будет немного длиннее второго фильма»

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

Джеймс Кэмерон: «„Аватар 3“ будет немного длиннее второго фильма»
106106
7575
1616
55
44
33
11
Дед, иди уже на пенсию
реклама
разместить
В 2020-м Кодзима назвал Луку Маринелли «вылитым Солидом Снейком» — в Death Stranding 2 тот сыграл персонажа с банданой

Геймдизайнер привлекает к работе всех, на кого обращает внимание.

В 2020-м Кодзима назвал Луку Маринелли «вылитым Солидом Снейком» — в Death Stranding 2 тот сыграл персонажа с банданой
6969
88
33
33
22
11
Кодзима уронил свежеприготовленный онигири
Обзор боевика «Рейс навылет» — это «Быстрее пули», из которого убрали лишних актёров, болтовню, пафос, а заодно урезали бюджет и здравый смысл

Почему все молчат о комедийном боевике Fight or Flight про толпу киллеров в самолёте с Джошем Хартнеттом в роли слегка чокнутого наёмника? Придётся немного о нём рассказать.

247247
2525
1313
88
22
22
22
11
11
В Быстрее пули не было лишних актёров.
В российском Steam возобновились продажи серий Company of Heroes и Warhammer 40,000: Dawn of War от Relic

Возвращение игр шло постепенно со второй половины февраля.

В российском Steam возобновились продажи серий Company of Heroes и Warhammer 40,000: Dawn of War от Relic
9696
1616
55
33
22
11
11
11
Думают мы все забыли и побежим с распахнутыми, кошельками? Ну уж нет, у меня все в черном списочке уже, кто такой финт провернул и ушел из православного стима
Атомик Харт аутентичный, ведь все знают, что в СССР воздушные шары делали из чугуния
182182
7777
2424
44
22
11
Indiana Jones and the Great Circle получила возрастной рейтинг ESRB для PS5

Релиз уже скоро.

Indiana Jones and the Great Circle получила возрастной рейтинг ESRB для PS5
2727
44
22
22
11
Спасибо Майкам, что выпускают игры на плойку Теперь сонибоям есть во что играть!
[]