27 мая 2017

Процедурные анимации - 5

(English version)
В прошлый раз был синус... сегодня продолжаем с чем-то похожим. Круги и окружности! Гипнотические анимашки, простые формулы, сплошной код и математика - в общем всё то, что называется "процедурной анимацией". Готовьте печеньки, чай и уютный диван... будем делать красоту!
Например, вот такую:



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

Непонятная цепь

Начнём вот с такой ерундовины:


Сначала кажется, что движение кружков хаотично, но при этом немного красивое. На самом деле за этим непонятным движением кроется обычная окружность. Дорисую небольшие вспомогательные круговые элементы, и всё встанет на свои места:




При этом поворот каждой окружности записывается одной строкой:

circles[i].rotation = (Math.cos(timeValue - Math.PI) + 1) * 90;

Круто? А-то!
Пробежимся в цикле по всем окружностям и получим итоговое движение!
Подчеркну: одна строчка кода! одна формула! Ну разве не прелесть? Прелес-с-с-ть...

Концентрические окружности

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



Стоит отметить, что здесь есть не только движение эллипсов вниз-вверх, но и слабо заметное появление новых эллипсов снизу, и пропадание "в никуда" сверху. Вдобавок нижние эллипсы сильнее развёрнуты для достижения 3д-эффекта. Присмотритесь!
Итак, базовое расположение эллипсов - это обычный косинус :) Только у каждого эллипса он смещён по фазе вот так:

_y = cos( i / count * Math.PI ) * 90;

При этом значение каждого радиуса тоже описывается простенькой формулой:

_r = sin( i / count * Math.PI );

Чтобы сделать анимацию, сделаем вот такую "добавку": всего лишь один косинус, с параметром "время" внутри:

20 * cos(i / count * Math.PI * 1.5 + time * 2 * Math.PI / 2);

Чтобы упростить запись сделаем замену angle = i / count * Math.PI и получим вот такого монстра:

_y = cos( angle ) * 90 + 20 * cos(angle * 1.5 + time * 2 * Math.PI / 2);
_r = sin( angle );

А пропадание/добавление - это простое изменение размеров самих эллипсов. Изменим функцию для angle (которая напрямую используется в _r) и получим необходимый эффект:

angle = (i + time / 2) / count * Math.PI;

Ну и напоследок я еще решил сделать небольшой 3d-эффект, при котором нижние эллипсы чуточку развёрнуты к зрителю. Сделать это можно так: верхние эллипсы рисуются более вытянутыми, а нижние - сплюснутыми. Такая добавка (perspective) участвует только при отрисовке в качестве параметра height эллипса.
В итоге все формулы для отрисовки нашей анимации выглядят вот так:

angle = (i + time / 2) / count * Math.PI;
perspective = (1 - angle / Math.PI) * b / 3;
_y = Math.cos( angle ) * 90 + 20 * Math.cos(angle * 1.5 + time * 2 * Math.PI / 2);
_r = Math.sin( angle );
graphics.drawEllipse(-_r * a/2,
                           -_r * b/2 + _y,
                            _r * a,
                    _r * (b + perspective ));


Чего-то много математики вышло, сорри...)))

Цветок из кругов

А теперь немного разноцветной красивости.


Здесь у нас куча кругов, которые сначала сходятся в один круг, а потом разлетаются в виде цветка. В итоге центры кругов всё время лежат на окружности, радиус которой постепенно увеличивается. Такие дела... математика выглядит для x и y центра каждого круга вот так:

Radius = r * (Math.cos(time) + 1) / 2;
circles[i].x = Math.cos(i / (count) * Math.PI * 2 + time * 1 ) * Radius;
circles[i].y = Math.sin(i / (count) * Math.PI * 2 + time * 1 ) * Radius;

Где
r - это максимальный расстояние, на которое разлетаются круги,
Radius - это текущее расстояние от центра,
i - номер круга, для которого мы всё и рассчитываем,
count - количество кругов... да-да, в демке можно менять их количество и увидеть, например, вот это:


Как всегда тут еще есть заморочка с небольшими паузами в начале и конце каждого движения. Делаем её как и прежде, с помощью манипулирования временем:

animationTime += 1/stage.frameRate;
time = Math.pow(Math.sin( animationTime * 1), 7) * Math.PI;

И теперь в формулах используем везде time, который жуть какой нелинейный! (седьмая степень как никак)
Магия...

Исходный код

Ну вот и всё, исходник вооот здесь.

Итого

Сегодня был как бы отдых, небольшие процедурные анимации, которые реально радуют глаз.

Помните: при создании даже совсем небольшой анимашки, мы учимся делать что-то новое! Багаж знаний и умений пополняется. А что может быть лучше этого?! Верно - ничто!

Сообщения, схожие по тематике:

0 коммент.:

Отправить комментарий