Статьи

Скопируйте активный эффект камуфляжа с помощью Flash

Активный камуфляж — это научно-фантастическая концепция, обычно видимая в форме костюма, которая позволяет владельцу стать почти невидимым. Это можно увидеть в таких фильмах, как Predator и Die Another Day, а также в таких играх, как Halo и Crysis.

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


Давайте посмотрим на конечный результат, к которому мы будем стремиться:


Отображение смещения — это карта текстуры, которая используется для модуляции силы смещения. Смещение означает буквальное перемещение пикселей поверхности не на своем месте. В большинстве 3D-приложений пиксели смещены вдоль поверхности нормали. В Adobe Flash смещение происходит в двухмерном пространстве вдоль координат X и Y изображения.

Фильтры смещения во Flash обычно анимируются путем динамического изменения их интенсивности (параметры scaleX и scaleY), изменения положения растрового изображения смещения (параметр mapPoint) или путем манипулирования цветовыми каналами. В этом руководстве будут объяснены эти методы, но мы также изучим другой, который использует последовательность растровых изображений для рисования новой карты смещения в каждом кадре.

Пример смещения по поверхности нормали.

Пример отображения смещения во вспышке по осям X и Y.


Откройте новый документ во Flash и установите размер 550×368, чтобы он соответствовал нашему фоновому изображению. Установите частоту кадров 48 кадров в секунду. Фильтр смещения на самом деле будет работать со скоростью 12 кадров в секунду, но в случае, если вам понадобится дополнительная анимация, он будет выглядеть более плавным при скорости 48 кадров в секунду.

Нажмите «Файл»> «Параметры публикации»> «Параметры ActionScript 3.0» и отключите «автоматическое объявление экземпляров сцены».


Импортируйте rainforest.jpg на сцену.

Нажмите Ctrl + K, чтобы выровнять и соответствовать размеру холста. Это будет наше фоновое изображение.

Теперь, когда изображение выбрано, нажмите F8, чтобы преобразовать его в символ. Выберите «Movie Clip» в меню типа.

Если окно свойств закрыто, нажмите Ctrl + F3, чтобы открыть его. Мы собираемся назвать клип, который был только что создан. В поле имени экземпляра введите «bkgd_mc».


Теперь нажмите Ctrl + F8, чтобы создать новый видеоклип. Мы пока не называем это. Сначала мы собираемся импортировать последовательность растровых изображений внутри этого фрагмента ролика. Перейдите в Файл> Импорт> Импортировать в рабочую область. Выберите первое изображение последовательности, которое называется «pred0001.jpg». Flash спросит, хотите ли вы импортировать все изображения в этой последовательности. Нажмите да.


Вы заметите, что каждое растровое изображение размещается на ключевом кадре вдоль временной шкалы фрагмента ролика. Начиная с кадра 1, выберите изображение и нажмите F8, чтобы преобразовать его в видеоклип. Делайте это на каждом кадре до конца последовательности. Делайте это по порядку, от первого до последнего и убедитесь, что вы не пропустите ни одного кадра, иначе это испортит анимацию.

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


Выберите фрагмент ролика в кадре 1. Щелкните правой кнопкой мыши и распределите по слоям. Опять же, сделайте это для всех видеоклипов на всех кадрах. Когда вы закончите, вы больше не сможете видеть анимацию, только изображение на верхнем слое.


Нажмите Ctrl + L, чтобы открыть библиотеку, и перетащите «Символ 2» на сцену. На вкладке свойств назовите этот экземпляр «displ_mc». Этот мувиклип будет использован в нашем фильтре смещения.


Мы собираемся написать код для нашего фильтра карты смещения внутри файла класса документа . Создайте новый файл Actionscript и назовите его «pred_as3». Теперь вставьте этот код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package {
import flash.display.MovieClip;
import flash.display.BitmapData;
import flash.display.IBitmapDrawable;
import flash.display.BitmapDataChannel;
import flash.filters.DisplacementMapFilter;
import flash.filters.DisplacementMapFilterMode;
import flash.geom.Point;
import flash.events.Event;
 
 
    public class pred_as3 extends MovieClip {
                                                }
}

Вернитесь к документу Flash и назовите класс pred_as3.

Как вы можете видеть, мы уже импортировали все классы, которые нам понадобятся в этом уроке, теперь давайте продолжим писать класс документа. Добавьте этот код к нему:

1
2
3
4
5
6
7
private var clipcont= new Array();// all the animated frames will be stored in this array
private var count:Number;
private var timer:uint = 0;//sets the speed of the animation
 
 
public var displ_mc:MovieClip;
public var bkgd_mc:MovieClip;

Мы указываем несколько переменных, которые будут использоваться позже. Они должны быть объявлены перед конструктором класса, если они должны использоваться более чем одной функцией в классе документа.


Прямо под последней строкой мы начинаем записывать параметры и конструктор фильтра карты смещения.

01
02
03
04
05
06
07
08
09
10
11
12
private var strength1:int = 120;
private var mapBitmap:BitmapData = new BitmapData(320,240);// the size of the displacement map in pixels
private var mapPoint:Point = new Point(0,0);// the position of the displacement bitmap
private var componentX = BitmapDataChannel.GREEN;// which color channel is used;
private var componentY = BitmapDataChannel.GREEN;
private var spe:int = 1;//changes the strenght of the displacement filter
 
//all variables are then applied to a new filter
private var filter:DisplacementMapFilter = new DisplacementMapFilter(mapBitmap, mapPoint, componentX, componentY, scaleX, scaleY);
 
 
private var filterList = new Array();// a filter list array.

Итак, мы установили параметры для силы, размера, положения и канала RBG. Давайте подробнее рассмотрим каждый из этих параметров.


Как упоминалось ранее, смещение во вспышке возможно только вдоль осей X и Y. Параметры, которые устанавливают силу смещения для X и Y, представляют собой scaleX и scaleY соответственно. В этом уроке мы будем использовать одинаковую силу на осях X и Y, поэтому мы используем одинаковую переменную сила1 для обоих параметров. Ниже приведен пример смещения по горизонтальной оси с масштабом Y, установленным на ноль (левое изображение), и вертикальной осью, с масштабом X, установленным на ноль (справа).


Обратите внимание, как размер установлен на 320×240. Мы уже знаем размер растровых изображений в анимации, и конструктор должен иметь тот же размер, что и они. Если значение в конструкторе больше, чем у растровых изображений, в областях, где это не должно происходить, будет смещение. Серый цвет # 808080 вокруг головы персонажа является нейтральным, с другой стороны, любая область, которая пуста, или прозрачное растровое изображение фактически сместят фоновое изображение.

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


Фильтр смещения использует только один из 3 каналов RGB на растровом изображении для каждой оси. При использовании цветного растрового изображения в качестве карты смещения каждый канал будет давать очень разные результаты, как показано в примере ниже. В этом уроке мы используем изображение в оттенках серого, поэтому канал не имеет значения. ComponentX и componentY установлены на зеленый, но тот же эффект будет получен при использовании красного или синего каналов.

Различные результаты получены с использованием зеленого канала, красного канала или синего канала.


Параметр mapPoint устанавливает положение карты смещения. Положение относится к объекту, к которому он применяется, а не к сцене. Если установить положение (0,0), карта смещения появится в верхнем левом углу нашего фонового изображения, которая не всегда будет совпадать с верхним левым углом сцены, как показано ниже.

Параметр mapPoint относится к объекту, а не к сцене.


Теперь давайте применим фильтр смещения к нашему фоновому изображению «displ_mc». Фильтр смещения помещается в массив фильтров, и мы делаем это внутри конструктора класса. Мы также добавляем на сцену два наших главных клипа с помощью метода addchild. В AS3 конструктор класса является первой функцией, которая должна быть выполнена в классе документа, и она вызывается автоматически, поэтому лучше, чтобы любые функции или методы, которые необходимо запускать при загрузке, вызывались из конструктора класса.

01
02
03
04
05
06
07
08
09
10
public function pred_as3() {
     
addChild(displ_mc);
addChild(bkgd_mc);
filterList.push(filter);// add the displacement map filter to the array.
bkgd_mc.filters = filterList;
storeClips();
     
     
}

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


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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private function storeClips():void //stores the animation in an array
{
    count = displ_mc.numChildren;//the total number of movieclips inside displ_mc
     
for (var i:int = 0; i < displ_mc.numChildren; i++)//finds all movieclips inside displ_mc
    {
     
 
         
         
    clipcont.push(displ_mc.getChildAt(i));// frames are pushed inside the clipcont array
         
         
         
     
        }
}

Эта функция использует цикл for для сканирования всех видеоклипов внутри displ_mc. Мы хотим, чтобы в этом уроке кадры анимации были преобразованы в видеоклипы ранее. Помните, когда я сказал, чтобы преобразовать их кадр за кадром? Мы сделали это, чтобы кадры могли быть отсортированы правильно, а затем доступ к ним осуществлялся с помощью метода getChildAt (). Поскольку мы не назвали ни одного из этих экземпляров, Flash сортирует их внутри по порядку создания. Если растровые изображения были случайным образом преобразованы в видеоклипы, анимация никогда не будет воспроизводиться правильно. Таким образом, кадры теперь можно помещать в массив clipcont, один за другим.

Код до сих пор должен выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package {
    import flash.display.MovieClip;
    import flash.display.BitmapData;
    import flash.display.IBitmapDrawable;
    import flash.display.BitmapDataChannel;
    import flash.filters.DisplacementMapFilter;
    import flash.filters.DisplacementMapFilterMode;
    import flash.geom.Point;
    import flash.events.Event;
 
 
    public class pred_as3 extends MovieClip {
         
private var clipcont= new Array();// all the animated frames are stored in this array
 
private var count:Number;
private var timer:uint = 0;
public var displ_mc:MovieClip;
public var bkgd_mc:MovieClip;
 
 
private var strength1:int = 120;
private var mapBitmap:BitmapData = new BitmapData(320,240);// the size of the displacement map in pixels
private var mapPoint:Point = new Point(0,0);// the position of the displacement bitmap
private var componentX = BitmapDataChannel.GREEN;// which color channel is used;
private var componentY = BitmapDataChannel.GREEN;
private var spe:int = 1;
 
//all variables are then applied to a new filter
private var filter:DisplacementMapFilter = new DisplacementMapFilter(mapBitmap, mapPoint, componentX, componentY, scaleX, scaleY);
 
 
private var filterList = new Array();// a filter list array.
 
 
 
//CLASS CONSTRUCTOR
public function pred_as3() {
     
addChild(displ_mc);
addChild(bkgd_mc);
storeClips();
filterList.push(filter);// add the displacement filter to the array.
bkgd_mc.filters = filterList;
     
     
}
 
 
private function storeClips():void //stores the animation in an array
{
    count = displ_mc.numChildren;//the total number of movieclips inside displ_mc
     
for (var i:int = 0; i < displ_mc.numChildren; i++)//finds all movieclips inside displ_mc
    {
             
    clipcont.push(displ_mc.getChildAt(i));// frames are pushed inside the clipcont array
             
        }
}
 
 
 
}
}

Теперь, когда у нас есть готовая анимация, давайте поместим ее в фильтр смещения. Мы собираемся получить доступ к массиву clipcont с помощью цикла «время освобождения», используя класс Event.ENTER_FRAME . Каждые 4 кадра осуществляется доступ к новому растровому изображению в массиве, который затем применяется к фильтру с помощью метода draw (). После отрисовки последнего кадра в clipcont цикл начинается заново и рисуется первый кадр в clipcont. Это бесконечный цикл.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  private function animate(e:Event) {
   
    filter.scaleX = strength1;
    filter.scaleY = strength1;
     
     
if(timer > 3) // a new frame is drawn every 4 frames
{
    if(count <= 0)
    {
        count = clipcont.length;// setting an infinite loop
         
    }
count—;
timer = 0;
 
 
 
}
 
    if (clipcont[count])
    {
        filter.mapBitmap.draw(clipcont[count]);// a new frame of animation is drawn
    }
    bkgd_mc.filters = filterList;//updates the filter
     
 }

Скопируйте приведенные выше строки в ваш файл Actionscript. Теперь давайте запустим это, добавив прослушиватель событий в конструктор класса.

01
02
03
04
05
06
07
08
09
10
public function pred_as3() {
     
addChild(displ_mc);
addChild(bkgd_mc);
filterList.push(filter);// add the displacement map filter to the array.
bkgd_mc.filters = filterList;
storeClips();
addEventListener(Event.ENTER_FRAME, animate);
     
}

Обновите конструктор класса с addEventListener метода addEventListener . Теперь функция animate была добавлена ​​на сцену и вызывается в каждом кадре. Проверьте эффект, нажав Ctrl + Enter. Вы должны увидеть анимированное лицо в левом верхнем углу фильма.


У нас есть анимационный цикл в углу фильма. Сделаем так, чтобы карта смещения следовала за мышью, таким образом вы сможете увидеть, как эффект активного камуфляжа выглядит на разных участках фона. Вставьте эту строку внутри функции анимации:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  private function animate(e:Event) {
   
    filter.scaleY = strength1;
    filter.scaleX = strength1;
     
    timer++;
 
if(timer > 3) // a new frame is drawn every 4 frames
{
    if(count <= 0)
    {
        count = clipcont.length;// setting an infinite loop
    }
count—;
timer = 0;
 
}
 
    if (clipcont[count])
    {
        filter.mapBitmap.draw(clipcont[count]);// a new frame of animation is drawn
    }
     
 
        filter.mapPoint = new Point(mouseX-160, mouseY-240);
     
    bkgd_mc.filters = filterList;
 }

Таким образом, мы обновляем позицию карты смещения на основе ввода кадра, используя свойства mouseX и mouseY. Нажмите Ctrl + Enter, чтобы проверить это. Голова должна теперь следовать за мышью.


На последнем шаге этого урока мы немного поиграем с силой нашего фильтра, увеличивая значение параметров scaleX и scaleY в течение определенного периода времени, а затем уменьшая его до исходного значения. Мы пытаемся добиться этого, чтобы эффект выглядел более динамичным и … видимым. Хотя весь смысл камуфляжа в реальной жизни должен заключаться в том, чтобы сделать вещи менее заметными, мы пытаемся сделать так, чтобы это выглядело круто. Давайте заморозим анимацию, чтобы вы могли понять, о чем я говорю. В функции анимации замените строку

1
filter.mapBitmap.draw(clipcont[count]);

вместо этой строки:

1
filter.mapBitmap.draw(clipcont[20]);

Вместо того, чтобы рисовать анимацию, мы говорим flash, чтобы рисовать один и тот же кадр снова и снова. Нажмите Ctrl + Enter, чтобы проверить это.

Эффект выглядит абсолютно статичным и скучным. Давайте сделаем это немного. Вставьте приведенный ниже код в функцию animate:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  private function animate(e:Event) {
   
        filter.scaleY = strength1;
    filter.scaleX = strength1;
     
    timer++;
 
if(timer > 3) // a new frame is drawn every 4 frames
{
    if(count <= 0)
    {
        count = clipcont.length;// setting an infinite loop
    }
count—;
timer = 0;
 
}
 
    if (clipcont[count])
    {
        filter.mapBitmap.draw(clipcont[20]);// a new frame of animation is drawn
    }
    filter.mapPoint = new Point(mouseX-160, mouseY-240);
     
    if (filter.scaleX > 220 || filter.scaleX < 120) // filter keeps changing it’s intensity, making the effect more dynamic
    {
        spe *= -1;
    }
    strength1 += spe;
     
    bkgd_mc.filters = filterList;
 }

Теперь проверьте это с помощью Ctrl + Enter.

Видите, насколько лучше это выглядит? Хорошо, теперь вы можете восстановить анимацию, исправить строку, которая была изменена:

1
filter.mapBitmap.draw(clipcont[count]);

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


Файл может быть немного тяжелым, если вы используете jpeg quality 100, что я и рекомендую. При более низком качестве эффект теряет часть своего очарования. Если вы хотите фильм меньшего размера, вы можете сжимать изображения еще больше в фотошопе, но убедитесь, что вы правильно используете цветовую схему. Цвет вокруг головы персонажа всегда должен быть # 808080, иначе вы увидите рамку вокруг него.

Вот и все. Первый урок, который я когда-либо написал, было весело делать это, и я надеюсь, что вы получили удовольствие от чтения и хорошо его использовали. Я очень ценю ваши отзывы. Спасибо за прочтение!