EN
creator cover aleha_84
aleha_84
Creating cozy pixelart seamless scenes
aleha_84
21
subscriber
Available to everyone
Aug 13 18:18

Статья №2. Создание бесшовной анимации + разбор примера

Далее я расскажу своё видение происходящего отталкиваясь от личного опыта. Подходы используемые мною могут отличаться от общепринятых, но я иду свои путём.
В первую очередь при создании бесшовной\бесконечной анимации надо принять тот факт, что не нужно воспринимать происходящее там действие у которого есть начало и конец. Словно на 1-ом кадре что-то начинается, а на 100-ом заканчивается. Начинаться может на 60-ом, а заканчиваться на 20-ом. Как такое может быть? Логично что любой видео файл или ГИФ имеет свою длительность. Я воспринимаю это как количество кадров, которые меняются с заданной частотой. При создании бесшовной анимации, нужно принять тот факт, что нет второй стенки. Когда мы доходим до лимита кадров в итоговой анимации, следующий индекс просто начинается с нуля.
В коде это решается так (пререндеринг кадров, описанный в предыдущей статье):
for(let f = 0; f < totalFrames; f++){
    let frameIndex = f + startFrameIndex;
    if(frameIndex > (framesCount-1)){
        frameIndex-=framesCount;
    }
    // do your action here
}
(средств для форматирования кода на бусти нету)
Визуально результата можно достигать двумя основными способами.
Первый. В ходе действия возвращать состояние объекта в его стартовое состояние. В этой ситуации количество кадров у самого объекта должно совпадать с количеством кадров итоговой анимации. Если оно будет выше или меньше, то будет дергание кадров. Например, итоговая анимация имеет длину 100 кадров. Размещаем на ней точку, которая в течении этих же 100 кадров изменяет своё состояние туда и обратно (размер, положение, что угодно). 
Второй. Когда мы добиваемся результата за счет множества других объектов. В этой ситуации количество кадров объекта должно быть меньше чем количество кадров в итоговой анимации. При этом нам не обязательно возвращать объект в его исходное состояние в конце. За счет существования похожих "соседей" можно создать визуальное ощущение непрерывности действия, создать поток. Чем меньше количество кадров у объекта, тем на более короткий срок он появляется в сцене и наоборот. Это работает в паре с количеством однотипных объектов. Тут необходимо найти баланс, чтобы не создавать много шума. 
Первый и второй вариант могут быть скомбинированы, для создания большего разнообразия. 
Обязательным условием при использовании более одного объекта в такой анимации, независимо от выбранного способа визуализации (описанных выше), является распределение по шкале времени стартовой точки. Распределение может быть случайным, линейным или боолее комплексным, каждый вариант даёт свой результат. Под понятием случайности я понимаю механизмы построенные на основе джаваскриптовой функции Math.random().
Предыдущий пример кода можно дополнить записью: let startFrameIndex = getRandomInt(0, framesCount-1); что является по сути определением точки старта для анимации очередного объекта на шкале времени. В случае первого варианта визуализации, когда время анимации объекта равно времени анимации объекта, множество непрерывных действий будет выглядеть рарозненно, не поддающееся каким-то правилам. В случае второго варианта, это добавляет дополнительный уровень хаоса, без которого это не будет иметь смысла. 
Далее уже идут только вариации описанных выше подходов. Например наряду с визуальными параметрами можно случайно выбирать длительность анимации объектов, например одни будут появляться и гаснуть быстрее или медленнее. 
Подводя итог, можно выделить следующие ключевые моменты: выбор длительности анимации, выбор стартовой точки на шкале времени, перекидывать очередные кадры в начало, выбор параметров для анимирования.
Разбор примера. 
Переливающаяся цветами радуги волнистая шкала. Может показаться, что она движется по тригонометрической функции, но нет.
Стартовые параметры: колчество кадров - 120; количество объектов - 20; рамки по X координате; рамки по Y координате; размер каждого объекта - 2х2; длительность анимации объекта - 120;
Тут используется вариант, когда длительность анимации объекта равна общей длительности анимации. На старте вычисляется расположение элементов по шкале X используя выделенный диапазон. Определяются набор параметров, которые будут изменяться у каждого объекта: H-компонент HSV представления цвета, положение по координате Y, размер объекта по высоте. Параметры меняются по квадратному уравнению с заданной плавностью, т.е. заранее вычисляется значение параметра в соответствии с нужным кадром и формируется массив значений.
Начальный кадр для каждого объекта определяется по формуле: i*fast.r(framesCount/2/itemsCount); где i - индекс объекта от 0, fast.r - округление, framesCount - общее число кадров в сцене, делим их ещё дополнительно пополам для большей плавности, itemsCount - количество объектов. Таким образом получили линейное распределение по шкале времени.
Далее уже дело техники, у нас есть индекс очередного кадра, его позиция во врмени, надо всего лишь взять значения параметров и отобразить это на канвасе. Многие параметры подбираются на глаз, чтобы это удовлетворяло меня визуально, часто бывает так, что ошибочные настройки совместно с другими дают отличный визуальный эффект. Так же за те пару лет, которые я поддерживаю этот проект, накопилось большое количество вспомогательного кода, позволюящего экономить время и не повторять ранее написанный код. 
Спасибо за внимание. Если у вас есть вопросы, пишите мне пожалуйста в моих соцсетях - в инсте, ВК, твиттере и т.д. я всегда отвечаю.  
Log in, to post comments

Subscription levels

Поддержка \ Support

100₽ per month
Go up