You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Трудно представить современную игру без анимаций. Сегодня я расскажу о том как работаю с 2д анимациями на Юнити через фреймворк Actors. Описанный мною подход реализуется легко на любых движках и языках. Ну а проект на юнити можно скачать отсюда.
Проблема
Зачем свое решение писать если в Unity уже есть Mecanim? Я много работаю с 2д анимациями и могу ответственно сказать, что использовать меканим для 2д это как стрелять из пушки по воробьям. Много ассетов,много абстракций, много оверхеда, много гибкости и мало смысла.
Дело вкуса и предпочтений но для того чтобы настроить простейшие связи анимаций нужно
Добавить animator на объект
Сделать animator controller ( особенно жесть если анимация всего 1 или 2 ).
Понять, что лучше для этих целей использовать component animation. Который Legacy. Который в общем-то не дает нам ничего кроме массива анимаций и проигрывания. Да еще и с лишними вызовами методов со стороны движка хрен знает чего и для чего.
Допускается что мы будем использовать и Animator Controller и Component Animation и работать уже с двумя логическими единицами
Нужно создать anim clip и загрузить туда свою 2д анимацию. Кто этим никогда серьезно не занимался не прелставляет какой это гемор. Спасает разве что PowerSprite Animator от Powerhoof.
Настроить animator controller закончив это дело настоящим итальянским болоньезе,с кучей второстепенных скриптов и невнятной логики.
Goto 1. и так пока весь проект незавалится кучей бесмысленных ассетов с animator и anim clip.
БОЛОНЬЕЗ НЯМ
Я не говорю, что mecanim плох, я говорю вот об этом:
Описываем задачу
Чертить тонны схем и из какого места должны вылазить методы не моё , но прежде чем погрузиться в код надо хотя бы примерно представлять что мы будем делать.
Мы создаем систему которая будет работать БЕЗ юнити аниматора и анимклипов.
Нам нужно иметь возможность загружать несколько анимаций для объекта.
Нам нужен функционал позволяющий проигрывать цепочку анимаций. Хотя бы цепочку из двух анимаций.
Было бы неплохо при вызове анимации получить назад время на проигрывание выбранной анимации.
Нам нужно иметь возможность работать с анимациями через скрипты если это потребуется.
Простое api чтобы назначать и сбрасывать анимации у сущностей.
Работает из Actors разумеется.
Что мне нравится в системах анимаций так это то, что их можно сделать какими угодно сложными или простыми.
В конечном итоге у нас должно получиться что-то такое:
sealedclassProcessorGame:Processor,ITick{publicententity_hero;publicProcessorGame(){// находим на сцене GameObject и создаем для него сущность.entity_hero= Entity.CreateFor("Obj Lopatnik");// даем ему два компонента.varcAnimator= entity_hero.Set<ComponentAnimator>();varcRenderer= entity_hero.Set<ComponentRenderer>();// Component Renderer
cRenderer.source = entity_hero.GetMono<SpriteRenderer>();// Component Animator
cAnimator.map = game.locals.animations_pawn_shovel;// анимации для лопатника
cAnimator.guide = scripts.AnimatorGuidePawn.Instance;// animator guide для юнитов
cAnimator.frame =0;}publicvoidTick(floatdelta){if(Input.GetKeyDown(KeyCode.Alpha1)){// начнет проигрывать анимацию бесконечно
entity_hero.animation(states.anim.walk);}if(Input.GetKeyDown(KeyCode.Alpha2)){// начнет собирать. Проиграет анимацию 4 раза
entity_hero.animation(states.anim.grab,4);}if(Input.GetKeyDown(KeyCode.Alpha3)){// проиграет анимацию 1 раз.
entity_hero.animation(states.anim.walk, states.anim.once);}if(Input.GetKeyDown(KeyCode.Space)){// сбросит текущую анимацию и возьмет подходящую анимацию из// animator guide
entity_hero.animationReset();}}}
Кодим!
Я не следую общепринятым нотациям стиля для C#, пишите как вам больше нравится.
sequence
По-русски последовательность. Она и в Африке последовательность. Это аналог animclip
Здесь указываем массив спрайтов, ID следующей анимации если такая есть и даем возможность вытащить спрайт по индексу. Обычно я не люблю пользоваться свойствами в C#, но тут оно красиво позволяет обратиться к спрайту sequence[index]
Итак, у нас есть данные анимации, но нам надо их где-то хранить и обращаться к ним по ключу.
Кажется, словари C# идеальный кандидат на контейнер наших анимаций но...зачем если задачу можно решить проще? Массив с перебором гораздо компактнее и отлично подойдет для нашей несложной 2д игры. 100 анимаций никто ведь добавлять на объект все равно не будет.
publicclasssequences{
sequence[]elements=new sequence[3];int[]keys=newint[3];intlength;publicref sequence this[intindex]{get{for(inti=0;i<length;i++){if(keys[i]==index)returnref elements[i];}
#if UNITY_EDITOR
Debug.LogError($"there is no animation with id {index}");
#endif
returnref elements[0];}}publicvoidAdd(intkey,insequencesequence){if(elements.Length ==length){
Array.Resize(ref elements,length+2);
Array.Resize(ref keys,length+2);}
keys[length]=key;
elements[length++]=sequence;}}
anim
Нам нужны ID для анимаций. Я предпочитаю использовать константы, но это так же легко делается через enum.
Это компонент для екс. В общем-то это то что мы будем вешать на наши сущности.
Здесь нам интересен AnimatorGuide. Именно он позволит тонко работать с анимациями и добавлять к ним "события" и "скрипты".
publicclassComponentAnimator{publicAnimatorGuideguide= AnimatorGuide.Default;// логика анимаций если нужнаpublicsequencesmap=new sequences();// контейнер анимацийpublicintframe;// кадрpublicintanimation_next;// id анимацииpublicinttimes;// сколько раз нужно проиграть анимациюpublicfloatanimation_time;// сколько занимает времени анимацияpublicbooloverriding;// включено ли принудительное проигрывание анимацииpublicboolpause;// стоит ли анимация на паузе}
AnimatorGuide
AnimatorGuide это аналог Mecanim в коде. С помощью animator guide можно легко настроить логику для базовых анимаций. Например все юниты проигрывают idle если скорость = 0 или меняют анимацию на бег если скорость выше 0.
AnimatorGuide по умолчанию не делает ничего. Напишем AnimatorGuidePawn который прикажет всем сущностям у которых он есть стоять на месте. Каждый раз когда мы создаем новую сущность она начнет проигрывать idle. Если мы в коде игры ( например в АИ скриптах ) перезапишем анимацию, то cAnimator.overriding не даст работать методу handle в AnimatorGuide. Так же нам не придется создавать по копии класса AnimatorGuidePawn для каждого юнита. Можно будет просто повесить один и тот же AnimatorGuidePawn через Instance.
Создаем процессор ( или еще их называют системами ) который будет обрабатывать все сущности с нашими компонентами ComponentAnimator и ComponentRenderer. ComponentRenderer просто хранит ссылку на SpriteRenderer.
game.locals.time_between_frames хранит время через которое нам надо обновлять кадры. В целях простоты мы не делаем локального времени для каждой отдельной секвенции, однако добавить это очень просто.
Осталось прикрутить API и сделать загрузку анимаций.
Метод ниже позволяет проиграть анимацию у сущности. Мы выбираем тип анимации, сколько раз она должна проиграться и c какого кадра. Мы так же считаем время нужное на проигрывание анимации и кешируем его в компоненте/возвращаем обратно.
anim.loop и anim.random_frame просто удобные константы чтобы указать что нам нужно сыграть анимацию бесконечно раз или нам нужен случайный кадр. Эти контанты добавляем к структуре anim```
Последний метод который нам понадобится это animationReset Мы просто сбрасываем кадры до нуля и говорим, что больше не перезаписываем анимацию чтобы наш animator guide мог решить сам какую анимацию нужно запустить.
Делаем все из кода. Просто и быстро. Box.Load это обертка Resources.Load в фреймворке с возможностью кеширования. Последняя секвенция интересная. Мы в ней пишем, что после проигрывания анимации Grab нужно проиграть анимацию Walk
У нас на руках простое, легкорасширяемое решение по анимациям. Зарисуем же чтонибудь на экране. В самом начале поста я показывал как у нас это будет выглядить в скриптах игры.
Интереснее всего этот отыгрыш. После четырёх отыгрышей anim.grab отыграется anim.walk так как мы ее указали в настройках анимации для этого юнита.
if(Input.GetKeyDown(KeyCode.Alpha2)){// начнет собирать. Проиграет анимацию 4 раза
ntity_hero.animation(states.anim.grab,4);}
Проект на unity можно скачать здесь.
Это не самая совершенная система, но ее не сложно дополнить нужными фишками. Но хочется особенно подчеркнуть : keep it simple, code fast.
А как справляетесь с анимациями вы? Делитесь мыслями и идеями в комментариях, будет интересно почитать :)
The text was updated successfully, but these errors were encountered:
Непосредственно для спрайтовой анимации писал свои контроллеры (потом под эту же систему завернул рантайм спайна и юнити анимации). Контроллеры от юнити очень долго инициализировались, а мне нужно было иметь довольно много объектов, которые переодически нужно было отключать. Там где обойтись было нельзя, то тоже использовал legacy компонент.
А для анимаций в целом, писал свою систему похожую на actions в cocos2d. Чем удобно, что в них можно завернуть все что угодно, что происходит на протяжении некоторого времени/асинхронно. Так у меня там были твины, спрайтовые и спайновые анимации, паузы, ожидания инпута и http реквесты и т.д.
Кроме того, для этой системы легко пишутся action для последовательного исполнения, для параллельного с ожиданием последнего и т.д. В итоге получалась довольно мощная система, которая позволяла довольно просто выстраивать сложные последовательности, комбинировать работу аниматоров и процедурные анимации, что особенно важно для различных казуальных игр.
Например: что-то вылетает из одного места, летит в другое, там анимированно ждет, пока отыграет анимация персонажа, затем трансформируется во что-то другое с анимацией, потом улетает в ui где взрывается, параллельно с анимацией перса, причем промежуточные точки обусловлены состоянием игрового поля, а используются партиклы, твины, спайн анимации, спрайтовые анимации, просто паузы с ожиданием, а где-то даже ожидание инпута от пользователя или ответа от сервера.
Трудно представить современную игру без анимаций. Сегодня я расскажу о том как работаю с 2д анимациями на Юнити через фреймворк Actors. Описанный мною подход реализуется легко на любых движках и языках. Ну а проект на юнити можно скачать отсюда.
Проблема
Зачем свое решение писать если в Unity уже есть Mecanim? Я много работаю с 2д анимациями и могу ответственно сказать, что использовать меканим для 2д это как стрелять из пушки по воробьям. Много ассетов,много абстракций, много оверхеда, много гибкости и мало смысла.
Дело вкуса и предпочтений но для того чтобы настроить простейшие связи анимаций нужно
Допускается что мы будем использовать и Animator Controller и Component Animation и работать уже с двумя логическими единицами
БОЛОНЬЕЗ НЯМ
Я не говорю, что mecanim плох, я говорю вот об этом:
Описываем задачу
Чертить тонны схем и из какого места должны вылазить методы не моё , но прежде чем погрузиться в код надо хотя бы примерно представлять что мы будем делать.
Что мне нравится в системах анимаций так это то, что их можно сделать какими угодно сложными или простыми.
В конечном итоге у нас должно получиться что-то такое:
Кодим!
sequence
По-русски последовательность. Она и в Африке последовательность. Это аналог
animclip
Здесь указываем массив спрайтов, ID следующей анимации если такая есть и даем возможность вытащить спрайт по индексу. Обычно я не люблю пользоваться свойствами в C#, но тут оно красиво позволяет обратиться к спрайту
sequence[index]
sequences
Итак, у нас есть данные анимации, но нам надо их где-то хранить и обращаться к ним по ключу.
Кажется, словари C# идеальный кандидат на контейнер наших анимаций но...зачем если задачу можно решить проще? Массив с перебором гораздо компактнее и отлично подойдет для нашей несложной 2д игры. 100 анимаций никто ведь добавлять на объект все равно не будет.
anim
Нам нужны ID для анимаций. Я предпочитаю использовать константы, но это так же легко делается через enum.
ComponentAnimator
Это компонент для екс. В общем-то это то что мы будем вешать на наши сущности.
Здесь нам интересен AnimatorGuide. Именно он позволит тонко работать с анимациями и добавлять к ним "события" и "скрипты".
AnimatorGuide
AnimatorGuide это аналог Mecanim в коде. С помощью animator guide можно легко настроить логику для базовых анимаций. Например все юниты проигрывают idle если скорость = 0 или меняют анимацию на бег если скорость выше 0.
AnimatorGuide по умолчанию не делает ничего. Напишем AnimatorGuidePawn который прикажет всем сущностям у которых он есть стоять на месте. Каждый раз когда мы создаем новую сущность она начнет проигрывать idle. Если мы в коде игры ( например в АИ скриптах ) перезапишем анимацию, то cAnimator.overriding не даст работать методу handle в AnimatorGuide. Так же нам не придется создавать по копии класса AnimatorGuidePawn для каждого юнита. Можно будет просто повесить один и тот же AnimatorGuidePawn через Instance.
ProcessorAnimator
Создаем процессор ( или еще их называют системами ) который будет обрабатывать все сущности с нашими компонентами ComponentAnimator и ComponentRenderer. ComponentRenderer просто хранит ссылку на SpriteRenderer.
game.locals.time_between_frames
хранит время через которое нам надо обновлять кадры. В целях простоты мы не делаем локального времени для каждой отдельной секвенции, однако добавить это очень просто.API
Осталось прикрутить API и сделать загрузку анимаций.
Метод ниже позволяет проиграть анимацию у сущности. Мы выбираем тип анимации, сколько раз она должна проиграться и c какого кадра. Мы так же считаем время нужное на проигрывание анимации и кешируем его в компоненте/возвращаем обратно.
anim.loop и anim.random_frame просто удобные константы чтобы указать что нам нужно сыграть анимацию бесконечно раз или нам нужен случайный кадр. Эти контанты добавляем к структуре
anim```Последний метод который нам понадобится это
animationReset
Мы просто сбрасываем кадры до нуля и говорим, что больше не перезаписываем анимацию чтобы наш animator guide мог решить сам какую анимацию нужно запустить.Загрузка анимаций
Делаем все из кода. Просто и быстро.
Box.Load
это обертка Resources.Load в фреймворке с возможностью кеширования. Последняя секвенция интересная. Мы в ней пишем, что после проигрывания анимацииGrab
нужно проиграть анимациюWalk
Результат
У нас на руках простое, легкорасширяемое решение по анимациям. Зарисуем же чтонибудь на экране. В самом начале поста я показывал как у нас это будет выглядить в скриптах игры.
Интереснее всего этот отыгрыш. После четырёх отыгрышей
anim.grab
отыграетсяanim.walk
так как мы ее указали в настройках анимации для этого юнита.Проект на unity можно скачать здесь.
Это не самая совершенная система, но ее не сложно дополнить нужными фишками. Но хочется особенно подчеркнуть : keep it simple, code fast.
А как справляетесь с анимациями вы? Делитесь мыслями и идеями в комментариях, будет интересно почитать :)
The text was updated successfully, but these errors were encountered: