Михаил Востриков

Магический прототип

Хочу рассказать про симпатичный эффект, который реализуется достаточно просто, а выглядит на пять с плюсом, особенно в связке с надлежащим окружением.

Подобные эффекты обычно реализуют с помошью битмапа и комбинации фильтров на нем. Это не сложно, требуется немного фантазии и «игры» с параметрами фильтров. Но настоящие герои всегда идут в обход.

Сразу объясню почему. Во-первых, подобный прототип будет использоваться в реальном проекте, где вся сцена динамически изменяется перед эффектом, и за ним будет находится много объектов. Во-вторых, требуется высокая скорость работы всей флешки. При размере сцены 1280×700 пикселей, метод с применением битмапов не имеет никаких шансов получить приемлемый FPS на среднем компьютере.

Итак, задача: нужно получить светящийся шлейф от движущегося объекта, если быть точнее — от источника света. Источник может перемещаться по всей сцене, может менять свой размер, поэтому толщина шлейфа тоже должна меняться. Причем желательно, чтобы эффект мог работать как в девятой версии флеш-плеера, так и в более старых, минимально в шестой (с частичной потерей функциональности). AS3 использовать не будем — к этому есть свои предпосылки.

Приступим к реализации. Сам шлейф будем рисовать программно, запоминая координаты движения объекта и используя функцию curveTo() для сглаживания траектории движения. Свечение, пожалуй, лучше сделать через GlowFilter() со средним качеством, так как симуляция свечения через многократное рисование шейпов будет медленнее. Будем писать прототип, так как он — истинное оружие пролетариата, как сказал Иван Дембицкий.

MovieClip.prototype.Magic = function (len, w, fColor, gColor, ptr) {
// на входе:
// len - количество сегметов шлейфа
// w - начальная толщина шлейфа в пикселах
// fColor - цвет шлейфа
// gColor - цвет свечения вокруг шлейфа (Glow)
// ptr - муви-клип, за которым будет строиться магический шлеф. Если не задан, то за мышью.
 w *= .5;
 // запомним старые координаты начала шлейфа, они нам пригодятся для расчета
 // вектора движения и перпендикуляра к нему
 var oldX = this._xmouse;
 var oldY = this._ymouse;
 // используем координаты мышки, а если задан муви-клип, то его координаты
 if (ptr == undefined) {
 oldX = ptr._x;
 oldY = ptr._y;
 }
 // примерним два фильтра к клипу, в который будет рисовать шлейф.
 // BlurFilter немного размоет края шлейфа, чтобы придать реалистичности
 this.filters = new Array(
 new flash.filters.BlurFilter(2, 2, 1),
 new flash.filters.GlowFilter (gColor, 1, 25, 25, 3, 2)
 );
 // в массиве crd будем хранить текущую и все предыдущии координаты шлейфа,
 // причем их кол-во не будет превышать длинный шлейфа len
 var crd = new Array();
 // на событие перерисовки кадра "повесим" функцию рассчета необходимых переменных и само рисование
 this.onEnterFrame = function() {
 // создадим пустой объект, в нем будем хранить координаты
 // и дополнительные данные каждого узла шлейфа
 var o = new Object();
 // используем координаты мышки, а если задан муви-клип, то его координаты
 if (ptr == undefined) {
 o.x = this._xmouse;
 o.y = this._ymouse;
 } else {
 o.x = ptr._x;
 o.y = ptr._y;
 }
 // рассчитаем угол, равный перпендикуляру к вектору движения, сохраним его в объекте
 o.a = Math.atan2(o.y-oldY, o.x-oldX)+Math.PI*.5;
 oldX = o.x;
 oldY = o.y;
 // поместим объект в массив и обрежем массив до максимальной длины шлейфа
 crd.push(o);
 if (crd.length>len) {
 crd.splice(0, 1);
 }
 // поместим длину массива координат во временную переменную, она немного ускорит
 // цикл перебора массива и будет использована несколько раз
 var l = crd.length;
 // рассчитаем координаты перпендикуляров в каждом узле (с конца шлейфа к его началу),
 // причем линейно будем увеличивать толщину шлейфа до максимальной (заданной через переменную w)
 for (var i = 0; i < l; i++) {
 // возьмем объект из массива
 var o = crd[i];
 // k = коэффициент толщины шлейфа
 var k = w*i⁄l;
 // косинус и синус вектора движения нам нужен для рассчета координат перпендикуляров
 var cs = k*Math.cos(o.a);
 var ss = k*Math.sin(o.a);
 // собственно сам рассчет, x0 и y0 - правая сторона шлейфа, x1 и y1 - левая
 o.x0 = o.x+cs;
 o.y0 = o.y+ss;
 o.x1 = o.x-cs;
 o.y1 = o.y-ss;
 // немного уменьшим координату, дабы получить эффект "огня" -
 // плавного смещения всего шлейфа вверх, это добавит динамики нашему эффекту
 o.y *= .997;
 }
 // начнем рисование, используя цвет заливки, заданный на входе прототипа
 this.clear();
 this.beginFill(fColor);
 // начнем рисование с правой стороны шлейфа (набор коодинат x0 и y0), передвинем текущую позицию
 // рисования в самый хвост
 this.moveTo(crd[0].x, crd[0].y);
 // переберем массив координат с первого до предпоследнего, соединим точки с помошью сплайна
 // (кривой Безье)
 // будем использовать "классический" метод рисования кривых, состоящих из нескольких сегментов
 for (var i = 1; i<(l-1); i++) {
 this.curveTo(crd[i].x0, crd[i].y0,
 (crd[i].x0+crd[i+1].x0)*.5,
 (crd[i].y0+crd[i+1].y0)*.5);
 }
 // завершим рисование правой стороны, проведя линию до начальной координаты шлейфа
 this.lineTo(crd[l-1].x, crd[l-1].y);
 // левую половину шлейфа нарисуем аналогично, только используя второй набор рассчитаных координат
 // x1 и y1
 // чтобы рисуемая форма была корректно залита флешем, начальная и конечная координаты у обоих
 // половин должны совпадать!
 this.moveTo(crd[0].x, crd[0].y);
 for (var i = 1; i<(l-1); i++) {
 this.curveTo(crd[i].x1, crd[i].y1,
 (crd[i].x1+crd[i+1].x1)*.5,
 (crd[i].y1+crd[i+1].y1)*.5);
 }
 this.lineTo(crd[l-1].x, crd[l-1].y);
 // завершим рисование, форма шлейфа готова
 this.endFill();
 };
};
// Осталась самая малость — создать пустой клип под шлейф и применить к нему прототип.
// var mc = this.createEmptyMovieClip("MagicMc", 0);
// mc.Magic(20, 8, 0xFF8080, 0xCC80CC);
 

Работающий пример и архив с исходными файлами смотрите ниже.

You need to upgrade your Flash Player

Исходник
(файл .fla, 80 Кб)

Зимин Дмитрий
27 июня
Мишко - молодца!
А что при круговых движениях угловатости? Для упрощения?
Вадим
28 июня
Патресающе! Очень понравилось. И верх самого сайта справа, тоже просто шедевр.
Вадим
30 июня
Оболдеть! Эффектно выглядит.
Михаил Востриков
30 июня
Спасибо, я старался :)
По поводу угловатостей - просто движения мышки не слишком плавные. Когда эффект работает в "автомате", то все плавненько.
sta1ex
10 августа
Приветствую! Эффект потрясный! Можно я сюда пришлю человека, который ведет блог с флеш уроками? Хотелось бы чтобы молодые флешеры понимали как надо филигранно мыслить и творить:)
Илья
20 августа
Ребят, спасибо за статьи. Пожалуйста, пишите еще, не прекращайте ;-)
sas
27 августа
какие предпосылки привели к отказу от AS3?
Михаил Востриков
5 сентября
По поводу отказа от AS3. Абсолютное большиство заказчиков стараются занизить версию плеера, часто настаивают на 6-ке, хотя на 8-ку соглашаются после небольшой лекции. А вот на 9-ку склонить заказчика очень сложно, это реально проблема. Соответственно приходиться быть в рамках. Например после выхода сайта "BotaniQ" (сделанного полностью на флеше), уже спустя неделю пришлось все переделывать под 6-ой плеер по собственной инициативе, так как статистика показала, что 40-50% посетителей не видят сайт (что удивительно в общем то), а для рекламной акции это было абсолютно неприемлемо.
Михаил Востриков
5 сентября
В любом случае главное понять принцип создания эффекта, а переписать на AS3 - дело 15 минут...
foreground
4 января
Просто круть!!!!!!!! Попробую использовать как-нибудь на своём сайте
Flashist
28 Февраля
Потрясающе, мне очень понравилось, блин.
Будете ли вы против, если я опубликую ваш урок на своём сайте в разделе уроки ( http://flashist.ru/lessons ) со ссылкой на источник? Если будете против, то можно ли мне сделать будет просто описание вашего урока в разделе новостей?

Если вам понравится мой сайт, я могу предоставить вам права на создание статей в нём, если вам это будет интересно. Дело в том, что сейчас на сайте есть возможность деления пользователей по различным ролям и возможностям деятельности, поэтому, возможно, вам будет интересно вести свою рубрику на сайте.
Михаил Востриков
3 марта
Публикуйте на здоровье, но не забудьте указать автора и ссылку на этот пост :)
Flashist
4 марта
Спасибо за разрешение. Если вам будет интересно, то можете взглянуть на то, что получилось тут: http://flashist.ru/lessons/magiya-ot-vostrikova-mikhaila
Flasher
8 июня
СУПЕР!!!!!!!!!!! РЕСПЕКТ!!!!!! и уважуха))))))))))))))
Иван Зверев
15 июня
Вопрос по работе этой анимации.
У меня есть кнопка, когда на неё наводится мышка, вокруг неё должны летать два шарика. Это реализовано при помощи addEventListener и stop & play, то есть шарики двигаются не из actionscript, а по заданным траекториям. Я хотел приделать к ним этот шлейф, но после применения Magic они стоят на месте и двигаться не хотят.
Чую, что что-то неверное в самом подходе, а что?
Павел
28 июля
это невероятно... но я столкнулся с проблемой... как сделать так, чтоб шлейф не был всегда на переднем плане... прост мы с другом делаем игру, и иногда меч, от которого идет шлейф, заходит за персонажа... а шлейф на переднем плане(((
Михаил Востриков
7 августа
Иван, применять прототип нужно не к шарикам, а к пустому мувиклипу, в который будет рисоваться шлейф. А сам шарик нужно передать через параметр ptr, относительно него будет строиться шлейф.

Павел, прототип не меняет глубину объекта. То есть если мувиклип, в который генерится шлейф, нужно расположить на требуемой глубине или можно использовать swapDepths (в AS1-2) для задания нужной глубины динамически.