Активный камуфляж - это концепция научной фантастики, его обычно представляют в форме костюма, который позволяет надевшему его стать почти невидимым. Его можно увидеть в фильмах, таких как Predator и Die Another Day, и играх подобных Halo и Crysis.
Этот урок покажет вам, как добиться такого эффекта во Flash, анимируя фильтр displacement, используя последовательность битмапов. Этот эффект не только прикольный, но эта техника редко применяется в уроках online.
1) Карта смещения (Displacement Mapping) во Flash
Карта смещения - это карта текстуры, которая используется, чтобы модулировать интенсивность смещения. Дословно Displacement означает перемещение пикселов поверхности за пределы своего расположения. В большинстве 3D приложений пикселы сдвигаются по нормали к поверхности. В Adobe Flash сдвиг происходит в пространстве 2D, вдоль координат X и Y картинки.
Displacement-фильтры во Flash обычно анимируются динамически изменением их интенсивности (параметры scaleX и scaleY), изменением позиции битмапа смещения (параметр mapPoint), или манипулированием каналов цвета. Этот урок объяснит все эти техники, но также научит и еще одной, которая будет использовать последовательность битмапов, чтобы отрисовывать новую карту смещения в каждом кадре.

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

Пример карты смещения во flash, по осям X и Y .
2) Документ Flash Откройте новый документ во Flash и установите размер 550×368 так, чтобы закрыть вашу фоновую картинку. Установите frame rate = 48fps. Фильтр displacement в действительности работает и при 12fps, но в нашем случае, мы захотим дополнительную анимацию, и она будет работать плавнее при 48fps.

Кликните File > Publish Settings > ActionScript 3.0 Settings и снимите отметку “auto-declare stage instances”.
3) Импорт фонаИмпортируйте rainforest.jpg на сцену.

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

Не снимая выделения с картинки, нажмите F8, чтобы конвертировать ее в символ. В меню типа выберите “Movie Clip”.

Если окно свойств закрыто, нажмите Ctrl + F3, чтобы открыть его. Мы собираемся назвать мувиклип, который только что создали. В поле instance name напишите “bkgd_mc”.
4) Импорт последовательности битмаповТеперь нажмите Ctrl+F8, чтобы создать новый мувиклип. Мы не назовем его сейчас. Вначале мы импортируем последовательность битмапов внутрь этого мувиклипа. Перейдите в File > Import > Import to Stage. Выберите первую картинку последовательности, которая называется ” pred0001.jpg ” . Flash спросит нас, хотим ли мы импортировать все картинки в этой последовательности. Кликните yes.
5) Конвертируем последовательность битмаповВы заметите, что каждый битмап расположен в ключевом кадре на временной шкале мувиклипа. Начнем с фрейма 1, выделите картинку и нажмите F8, чтобы конвертировать ее в мувиклип. Сделайте это в каждом фрейме до конца последовательности. Сделайте это от первого до последнего кадра, не пропустив ни одного фрейма, иначе анимация может не получиться.

После того, как вы это сделаете, каждый ключевой фрейм должен иметь один мувиклип, содержащий кадр с лицом персонажа. Выделите снова первый фрейм и нажмите enter, чтобы просмотреть анимацию.
6) Распределим по слоямВыделите мувиклип в фрейме 1. По правой кнопке мыши выберите distribute to layers. Снова сделайте это для всех мувиклипов во всех фреймах. Когда вы закончите, у вас не будет больше возможности просмотреть анимацию, только картинку в верхнем слое.
7) Перетащим мувиклип на сценуНажмите Ctrl + L, чтобы открыть библиотеку, и перетащите “Symbol 2“ на сцену. Во вкладке properties дайте ему инстанс имя “displ_mc”. Этот мувиклип будет использоваться в нашем фильтре displacement.
8) Создаем класс документаМы собираемся написать код для нашего фильтра карты смещения в файле класса документа. Создайте новый Actionscript файл и назовите его “pred_as3“. Теперь вставьте этот код:
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.

Как вы могли видеть, мы уже импортировали все классы, которые нам нужны в этом уроке, теперь давайте продолжим писать класс документа. Добавим в него этот код:
private var clipcont= new Array();// все фреймы анимации будут храниться в этом массиве
private var count:Number; //часть цикла enterframe; говорит нам, какой фрейм анимации будет показан
private var timer:uint = 0;//устанавливает скорость анимации
public var displ_mc:MovieClip;
public var bkgd_mc:MovieClip;
Мы начнем с нескольких переменных, которые будут использоваться позднее. Их необходимо объявить перед конструктором класса, если они будут использоваться более чем одной функцией в классе документа.
9) Создаем фильтр Displacement Прямо внизу за последней строкой мы начнем писать параметры и конструктор фильтра displacement map.
private var strength1:int = 120; // значение scaleX и scaleY - устанавливает интенсивность фильтра displacement
private var mapBitmap:BitmapData = new BitmapData(320,240);// размер displacement map в пикселах
private var mapPoint:Point = new Point(0,0);// позиция битмапа displacement
private var componentX = BitmapDataChannel.GREEN;// какой цветной канал используется; в действительности не имеет значения , так как он в оттенках серого;
private var componentY = BitmapDataChannel.GREEN;
private var spe:int = 1;//изменяет интенсивность фильтра displacement
//все переменные потом применяются к новому фильтру
private var filter:DisplacementMapFilter = new DisplacementMapFilter(mapBitmap, mapPoint, componentX, componentY, scaleX, scaleY);
private var filterList = new Array();// массив для фильтра.
итак мы установили параметры для интенсивности, размера, позиции и RBG канал. Давайте подробнее рассмотрим каждый из этих параметров..
10) Интенсивность Displacement (Strength)Как было замечено ранее, смещение во flash возможно только по осям X и Y . Параметры, которые устанавливают интенсивность смещения по X и Y - это scaleX и scaleY соответственно. В этом уроке мы собираемся использовать одну и ту же интенсивность по обеим осям X и Y, поэтом у мы используем одну и ту же переменную strength1 для обоих параметров. Ниже приведен пример смещения по горизонтальной оси, где scaleY установлено в 0 (левая картинка), и по вертикальной оси, где scaleX установлено в 0 (справа).
11) Размер карты смещения (Displacement Map)Заметим, что размер установлен 320×240. мы уже знаем, что размер битмапов в анимации и в конструкторе должен иметь такой же размер, как этот. Если значение в конструкторе больше, чем размер битмапов, может быть смещение в областях, где этого не должно быть. Серый цвет #808080 вокруг головы персонажа является нейтральным цветом, с другой стороны любая область, которая является пустой или прозрачный битмап будут в действительности сдвигать картинку фона.

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

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

Параметр mapPoint относится к объекту, но не к сцене.
14) Применяем фильтрТеперь давайте применим фильтр displacement к нашей картинке фона “displ_mc”. Фильтр displacement заносится в массив фильтров, и мы делаем это внутри конструктора класса. Мы также добавляем наши два основных мувиклипа на сцену методом addchild. В AS3 конструктор класса - это первая функция , которая должна быть выполнена в классе документа, и она вызывается автоматически, и это лучше, чем любые функции или методы, которые нужно запускать при загрузке и вызывать из конструктора класса.
public function pred_as3() {
addChild(displ_mc);
addChild(bkgd_mc); // добавляем оба экземпляра мувиклипа на сцену
filterList.push(filter);// добавляем фильтр displacement map в массив.
bkgd_mc.filters = filterList; // применяем массив фильтров к целевому мувиклипу.
storeClips();
}
Последняя строка кода вызывает функцию, которая еще не написана. Имя подсказывает, что эта функция будет запоминать все анимированные мувиклипы в массив. Давайте пойдем вперед и напишем ее сейчас.
15) Сохраняем анимацию в массивИтак мы создали фильтр displacement map и применили его к мувиклипу, но мы еще не добавили никаких битмапов к фильтру. мы собираемся сделать это за 2 шага: вначале мы сохраним анимацию в массив, затем позже мы добавим эту анимацию в фильтр.
private function storeClips():void //сохраняем анимацию в массив
{
count = displ_mc.numChildren;//общее число мувиклипов внутри displ_mc
for (var i:int = 0; i < displ_mc.numChildren; i++)//находим все мувиклипы внутри displ_mc
{
clipcont.push(displ_mc.getChildAt(i));// кадры заносятся в массив clipcont
}
}
Эта функция использует цикл for, чтобы просканировать все мувиклипы внутри displ_mc. Мы берем анимационные фреймы, которые были конвертированы в мувиклипы ранее в этом уроке. Помните, когда я говорил конвертировать их кадр за кадром? Мы сделали это. чтобы фреймы можно было отсортировать правильно, и позже использовать доступный метод getChildAt(). Так как мы не дали им никакие инстанс имена, Flash сортирует их внутри в порядке создания. Если бы битмапы были конвертированы в мувиклипы рандомно, анимация не игралась бы корректно. Поэтому кадры могут быть сейчас помещены в массив clipcont, один за одним.
Код должен выглядеть так:
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();// все кадры анимации хранятся в этом массиве
private var count:Number; //часть цикла для enterframe; говорит, какой кадр анимации показывается
private var timer:uint = 0;
public var displ_mc:MovieClip;
public var bkgd_mc:MovieClip;
private var strength1:int = 120; // устанавливаем интенсивность фильтра displacement
private var mapBitmap:BitmapData = new BitmapData(320,240);// размер displacement map в пикселах
private var mapPoint:Point = new Point(0,0);// позиция битмапа displacement
private var componentX = BitmapDataChannel.GREEN;// какой цветной канал используется; в действительности не имеет значения, так как в оттенках серого;
private var componentY = BitmapDataChannel.GREEN;
private var spe:int = 1;
//все переменные затем применяются к новому фильтру
private var filter:DisplacementMapFilter = new DisplacementMapFilter(mapBitmap, mapPoint, componentX, componentY, scaleX, scaleY);
private var filterList = new Array();// массив фильтров.
//CLASS CONSTRUCTOR
public function pred_as3() {
addChild(displ_mc);
addChild(bkgd_mc); // добавляем оба экземпляра мувиклипов на сцену
storeClips();
filterList.push(filter);// добавлем фильтр displacement в массив.
bkgd_mc.filters = filterList; // применяем набор фильтров к целевому мувиклипу.
}
private function storeClips():void //сохраняем анимацию в массив
{
count = displ_mc.numChildren;//общее число мувиклипов внутри displ_mc
for (var i:int = 0; i < displ_mc.numChildren; i++)//находим все мувиклипы внутри displ_mc
{
clipcont.push(displ_mc.getChildAt(i));// кадры заносятся в массив clipcont
}
}
}
}
16) Анимируем фильтр Displacement Теперь, когда мы имеем готовую анимацию для использования, давайте поместим ее в фильтр displacement . Мы собираемся доступиться к массиву clipcont с циклом “с выдержкой по времени”, используя класс Event.ENTER_FRAME. Каждые 4 кадра будем выбирать новый битмап в массиве и затем применять фильтр, используя метод draw(). После того, как последний фрейм в clipcont будет отрисован, цикл стартует опять и отрисуется первый кадр в clipcont. Это бесконечный цикл.
CODE:
private function animate(e:Event) {
filter.scaleX = strength1; //установим значение scaleY и scaleX
filter.scaleY = strength1;
if(timer > 3) // новый фрейм рисуется каждые 4 фрейма
{
if(count <= 0)
{
count = clipcont.length;// установка ббесконечного цикла
}
count--;
timer = 0;
}
if (clipcont[count])
{
filter.mapBitmap.draw(clipcont[count]);// рисуется новый фрейм анимации
}
bkgd_mc.filters = filterList;//изменяем фильтр
}
Копируем строки вверху в наш actionscript файл. Теперь давайте заставим это выполняться, добавив слушатель событий в конструктор класса.
public function pred_as3() {
addChild(displ_mc);
addChild(bkgd_mc); // добавляем оба экземпляра мувиклипов на сцену
filterList.push(filter);// добавляем фильтр displacement map в массив.
bkgd_mc.filters = filterList; // применяем набор фильтров к целевому мувиклипу
storeClips();
addEventListener(Event.ENTER_FRAME, animate); //вызываем функцию animate в enter frame
}
Измените конструктор класса с методом addEventListener. Теперь функция animate была добавлена на сцену и вызывается в каждом фрейме. Протестируйте этот эффект, нажав Ctrl + Enter. Вы должны увидеть анимированное лицо в левом верхнем углу вашего мувика.
17) Заставим его следовать за мышьюмы имеем цикл анимации, которая выполяется в углу мувика. Давайте заставим карту смещения следовать за мышью, таким способом вы будете иметь возможность наблюдать, как эффект активнго камуфляжа смотрится в различных частях фона. Вставьте эту строку кода внутрь функции animate:
private function animate(e:Event) {
filter.scaleY = strength1; //устанавливаем значения scaleY и scaleX
filter.scaleX = strength1;
timer++;
if(timer > 3) // новый фрейм рисуется каждые 4 фрейма
{
if(count <= 0)
{
count = clipcont.length;// установим бесконечный цикл
}
count--;
timer = 0;
}
if (clipcont[count])
{
filter.mapBitmap.draw(clipcont[count]);// рисуется новый фрейм анимации
}
filter.mapPoint = new Point(mouseX-160, mouseY-240); // карта смещения следует за мышью
bkgd_mc.filters = filterList;
}
Таким способом мы изменяем положение карты смещения в enter frame , используя свойства mouseX и mouseY. Нажмите Ctrl+Enter, чтобы протестировать это. Голова должна сейчас следовать за мышью.
18) Изменяем интенсивность фильтраВ последнем шаге этого урока мы собираемся поиграть немного с интенсивностью нашего фильтра, увеличивая значение параметров scaleX и scaleY на определенный период времени, затем уменьшая значения обратно к первоначальному значению. Чего мы собираемся достичь этим - это сделать эффект более динамичным и … видимым. В то время как все пятно камуфляжа в реальной жизни должно делать вещи менее видимыми, мы попытаемся сделать этот эффект более прикольным. Давайте заморозим анимацию, чтобы вы могли понять, о чем я говорю. В функции animate замените строку :
filter.mapBitmap.draw(clipcont[count]);
на эту строку:
filter.mapBitmap.draw(clipcont[20]);
Вместо отрисовки анимации, мы прикажем flash рисовать один и тот же фрейм снова и снова. Нажмите Ctrl+Enter, чтобы протестировать это.
Эффект выглядит полностью статичным и скучным. Давайте дадим ему некоторое движение. Вставьте код ниже в функцию animate:
private function animate(e:Event) {
filter.scaleY = strength1; //изменяем значение scaleY и scaleX
filter.scaleX = strength1;
timer++;
if(timer > 3) // новый фрейм рисуется каждые 4 фрейма
{
if(count <= 0)
{
count = clipcont.length;// установим бесконечный цикл
}
count--;
timer = 0;
}
if (clipcont[count])
{
filter.mapBitmap.draw(clipcont[20]);// рисуется новый фрейм анимации
}
filter.mapPoint = new Point(mouseX-160, mouseY-240); // displacement map следует за мышью
if (filter.scaleX > 220 || filter.scaleX < 120) // фильтр хранит изменения интенсивности, делаем эффект более динамичным
{
spe *= -1;
}
strength1 += spe;
bkgd_mc.filters = filterList;
}
Теперь тестируем это (Ctrl+Enter).
Ok, вы можете теперь восстановить анимацию, пофиксите строку, которая была изменена:
filter.mapBitmap.draw(clipcont[count]);
Этот эффект будет также полезен в том случае, если вы захотите позже присоединить статичное тело к анимации лица.
Только зарегистрированные пользователи могут оставлять коментарии.
Пожалуйста зарегистрируйтесь или войдите в ваш аккаунт.