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

Июнь 27, 2008
|

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

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

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

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

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

<code class="javascript">

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&frasl;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();

  };

};

// Осталась самая малость&nbsp;&mdash; создать пустой клип под шлейф и применить к нему прототип.

// var mc = this.createEmptyMovieClip("MagicMc", 0);

// mc.Magic(20, 8, 0xFF8080, 0xCC80CC);

  </code>

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

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

Tags:

Автор записи: Михаил Востриков

  • Vitaliy

    круть

Twitter Facebook Flickr