Статьи

Создание эффекта статического искажения с использованием фильтра карты смещения

Два раза в месяц мы возвращаемся к любимым постам наших читателей на протяжении всей истории Activetuts +. Этот учебник был впервые опубликован год назад в январе 2010 года.

В этом уроке вы узнаете, как использовать класс DisplacementMapFilter в AS3 для создания многократно используемого эффекта статического искажения для нажатия кнопок.


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


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

пример смещения

Вы можете прочитать больше о картографировании смещения здесь .


Создайте новый файл Flash (ActionScript 3).

Настройки вашего фильма будут различаться в зависимости от вашей игры. Для этой демонстрации я настраиваю свой фильм как 500×300, черный фон и 30 кадров в секунду.

Настройка файла Flash

Создайте новый символ кнопки на сцене («Вставка»> «Новый символ»). Создайте 4 состояния для вашей кнопки. Точный дизайн должен соответствовать вашей игре. Что-то светящееся и полупрозрачное хорошо работает с этим эффектом.
Я использовал шрифт Genetrix Square для себя, но вы должны использовать то, что соответствует внешнему виду вашей игры.

Дайте вашей кнопке имя экземпляра ‘button1’.

свойства кнопки

Если вы сохраните и протестируете свой фильм («Управление»> «Тестировать ролик»), теперь вы должны увидеть свою кнопку на сцене, реагирующую на мышь с заданными вами состояниями переключения. Как это:


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

Создайте новый файл ActionScript с именем «JitteryButton.as». Сохраните этот файл в том же каталоге, что и ваш основной файл Flash. Добавьте этот код, чтобы создать оболочку для нашей кнопки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package {
 
    import flash.display.Sprite;
    import flash.display.SimpleButton;
 
    public class JitteryButton extends Sprite {
 
        private var myButton:SimpleButton;
 
        // CLASS CONSTRUCTOR
        public function JitteryButton(button:SimpleButton) {
            myButton = button;
        }
 
}

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


Создайте новый файл ActionScript с именем «Game.as». Это будет класс документа для нашего фильма. Сохраните его в том же каталоге, что и основной файл Flash.

Этот код добавит нашу пользовательскую оболочку кнопки вокруг кнопки на сцене:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package {
    import flash.display.MovieClip;
 
    public class Game extends MovieClip {
 
        private var startButton:JitteryButton;
 
        // CLASS CONSTRUCTOR
        public function Game() {
            // create the jittery button from the simple button on the stage
            startButton = new JitteryButton(button1);
 
            // add the new button to the stage
            addChild(startButton);
        }
 
    }
 
}

Этот код создает новый экземпляр нашего пользовательского класса JitteryButton и передает ему кнопку на сцене (‘button1’).

Конечно, ваш класс документа в конечном итоге будет выглядеть по-другому, поскольку в нем будет код вашей игры. Здесь мы просто заинтересованы в коде нашей кнопки.

Вернувшись внутрь своего Flash-файла, установите класс документа в «Game». Помните, вы не включаете расширение файла здесь.

установить класс документа

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


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

Откройте Photoshop и создайте новое изображение, заполненное нейтральным серым (# 808080). Изображение должно быть немного шире вашей кнопки и примерно в 3 или 4 раза выше. Моя кнопка — 277х56, а изображение — 310х220.

Мы начинаем с нейтрального серого, потому что это не повлияет на наше изображение.

создать новый образ

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

Дублируйте фоновый слой и назовите новый слой «Шум». Теперь у вас должно быть 2 слоя, заполненных нейтральным серым. Выберите новый слой шума и выберите «Фильтр»> «Шум»> «Добавить шум». Установите сумму в 120% и распределение в форме. Проверьте Монохроматический.

Хит ОК.

Установите слой «Noise» на 10% непрозрачности.

добавить шум к слою шума

Создайте новый слой под названием «Линии». Теперь используйте кисть 1px, чтобы добавить горизонтальные черные и серые линии к изображению.

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

рисовать горизонтальные линии

Выберите «Файл»> «Сохранить для Web и устройств» и сохраните изображение в виде 8-цветного GIF-файла с именем «staticMap.gif».

сохранить для веба

Вернувшись во Flash, импортируйте файл staticMap.gif в свою библиотеку («Файл»> «Импорт»> «Импорт в библиотеку …»). Откройте свойства связи, выберите « Экспорт для ActionScript» и установите для имени класса «StaticMap».

импортировать изображение

Теперь мы можем ссылаться на это изображение в нашем коде, используя имя класса StaticMap.


Добавьте эту функцию в ваш класс JitteryButton:

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
// create the displacement map filter
private function createDMFilter():DisplacementMapFilter {
 
    var mapBitmap:BitmapData = new StaticMap(0,0);
    var mapPoint:Point = new Point(0, 0);
    var channels:uint = BitmapDataChannel.RED;
    var componentX:uint = channels;
    var componentY:uint = channels;
    var scaleX:Number = 5;
    var scaleY:Number = 1;
    var mode:String = DisplacementMapFilterMode.COLOR;
    var color:uint = 0;
    var alpha:Number = 0;
 
    return new DisplacementMapFilter(
                    mapBitmap,
                    mapPoint,
                    componentX,
                    componentY,
                    scaleX,
                    scaleY,
                    mode,
                    color,
                    alpha );
 
}

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

Чтобы это работало, нам нужно импортировать эти классы вверху нашего класса JitteryButton:

1
2
3
4
5
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.filters.DisplacementMapFilter;
import flash.filters.DisplacementMapFilterMode;
import flash.geom.Point;

(Вы можете узнать больше о классе DisplacementMapFilter в документации AS3 )


Теперь мы создадим переменную для хранения фильтра. Мы применяем его к кнопке, устанавливая свойство кнопки «filters» для массива, который содержит наш фильтр.

Вот класс JitteryButton (строки 18 и 25 новые):

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
package {
 
    import flash.display.Sprite;
    import flash.display.SimpleButton;
    import flash.display.BitmapData;
    import flash.display.BitmapDataChannel;
    import flash.filters.DisplacementMapFilter;
    import flash.filters.DisplacementMapFilterMode;
    import flash.geom.Point;
 
    import caurina.transitions.Tweener;
 
    public class JitteryButton extends Sprite{
 
        private var myButton:SimpleButton;
 
        //create a variable to hold the displacement map filter
        private var dmFilter:DisplacementMapFilter = createDMFilter();
 
        // CLASS CONSTRUCTOR
        public function JitteryButton(button:SimpleButton) {
            myButton = button;
 
            // apply the filter to the button
            myButton.filters = new Array(dmFilter);
        }
 
 
        // create the displacement map filter
        private function createDMFilter():DisplacementMapFilter {
 
            var mapBitmap:BitmapData = new StaticMap(0,0);
            var mapPoint:Point = new Point(0, 0);
            var channels:uint = BitmapDataChannel.RED;
            var componentX:uint = channels;
            var componentY:uint = channels;
            var scaleX:Number = 5;
            var scaleY:Number = 1;
            var mode:String = DisplacementMapFilterMode.COLOR;
            var color:uint = 0;
            var alpha:Number = 0;
 
            return new DisplacementMapFilter(
                            mapBitmap,
                            mapPoint,
                            componentX,
                            componentY,
                            scaleX,
                            scaleY,
                            mode,
                            color,
                            alpha );
 
        }
 
    }
}

Если мы сейчас сохраним и протестируем файл, то увидим, что к нашей кнопке применяется фильтр карты смещения

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

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


Это простая функция, которая возвращает случайное целое число в указанном диапазоне:

1
2
3
4
5
// returns a random number between the specified range (inclusive)
private function randRange(min:int, max:int):int {
    var randomNum:int = Math.floor(Math.random() * (max — min + 1)) + min;
    return randomNum;
}

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

Добавьте его в свой класс JitteryButton.


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

Мы также можем анимировать положение изображения StaticMap относительно нашей кнопки. Это гарантирует, что одни и те же области кнопки не всегда сдвигаются.

Чтобы оживить эффект, мы добавим функцию с именем displayStatic, которая вызывается каждый кадр, чтобы обновить эти два свойства фильтра. Добавьте эту функцию в ваш класс JitteryButton:

1
2
3
4
5
6
// called on ENTER_FRAME
private function displayStatic(e:Event):void {
    dmFilter.scaleX = randRange(fuzzMin, fuzzMax);
    dmFilter.mapPoint = new Point(0, randRange(0, -160));
    myButton.filters = new Array(dmFilter);
}

Первая строка этой функции рандомизирует величину горизонтального смещения до значения между переменными fuzzMin и fuzzMax. Добавьте эти две переменные в ваш класс JitteryButton:

1
2
private var fuzzMin:int = 0;
private var fuzzMax:int = 2;

Вторая строка функции displayStatic рандомизирует Y-позицию StaticMap относительно нашей кнопки. Мы уже сказали фильтру использовать наше изображение StaticMap, поэтому нам просто нужно обновить позицию.

Третья строка просто повторно применяет фильтр к нашей кнопке.

Последнее, что нам нужно сделать, чтобы получить эту анимацию, это добавить слушатель события ENTER_FRAME. Добавьте эту строку в функцию конструктора JitteryButton:

1
2
// start displaying the static effect
addEventListener(Event.ENTER_FRAME, displayStatic);

И не забудьте импортировать класс Event вверху файла JitteryButton:

1
import flash.events.Event;

Если вы сохраните и протестируете фильм сейчас, вы увидите, что эффект заставляет нашу кнопку мерцать и прыгать:

Это довольно круто, но мы хотим, чтобы эффект реагировал и на мышь. Onward …


Теперь мы добавим две функции для регулировки интенсивности эффекта джиттера. Мы назовем эффект, который у нас в данный момент имеет Низкую интенсивность, поэтому добавим настройки для Средней и Высокой интенсивности. Добавьте эти функции в ваш класс JitteryButton:

01
02
03
04
05
06
07
08
09
10
11
12
13
// increase the intensity of the static to MEDIUM
private function setStaticMedium(e:MouseEvent = null):void {
    fuzzMin = 2;
    fuzzMax = 6;
    staticLength = randRange(8, 12);
}
 
// increase the intensity of the static to HIGH
private function setStaticHigh(e:MouseEvent = null):void {
    fuzzMin = 12;
    fuzzMax = 25;
    staticLength = 12;
}

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

Мы также добавили переменную staticLength. Мы будем использовать это, чтобы установить, как долго должен длиться более интенсивный эффект (количество кадров), прежде чем вернуться к низкой интенсивности. Добавьте эту переменную в ваш класс JitteryButton и измените функцию displayStatic следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private var staticLength:int;
 
// called on ENTER_FRAME
private function displayStatic(e:Event):void {
    dmFilter.scaleX = randRange(fuzzMin, fuzzMax);
    dmFilter.mapPoint = new Point(0, randRange(0, -160));
    myButton.filters = new Array(dmFilter);
 
    staticLength—;
     
    if(staticLength <= 0){
        fuzzMin = 0;
        fuzzMax = 2;
    }
}

Этот новый код уменьшает значение staticLength и сбрасывает fuzzMin и fuzzMax до значений низкой интенсивности, как только значение staticLength достигает нуля.


Чтобы наша кнопка реагировала на мышь, нам нужно добавить два прослушивателя событий мыши и функцию обработчика событий для каждого.

Добавьте слушателей мыши в функцию конструктора вашего класса JitteryButton:

1
2
3
// add the rollover event listeners to the button
myButton.addEventListener(MouseEvent.ROLL_OVER, onButtonRollOver);
myButton.addEventListener(MouseEvent.ROLL_OUT, onButtonRollOut);

Теперь создайте два обработчика событий, на которые ссылаются эти две новые строки. Они также идут в классе JitteryButton:

1
2
3
4
5
6
7
8
9
// called on button ROLL_OVER
private function onButtonRollOver(e:MouseEvent):void {
    setStaticHigh();
}
 
// called on button ROLL_OUT
private function onButtonRollOut(e:MouseEvent):void {
    setStaticMedium();
}

Чтобы все это заработало, нам нужно импортировать класс MouseEvent вверху нашего файла JitteryButton:

1
import flash.events.MouseEvent;

Теперь, когда наша кнопка обнаруживает событие ROLL_OVER, она вызывает обработчик события, который, в свою очередь, вызывает нашу функцию setStaticHigh. Эта функция увеличивает значения fuzzMin и fuzzMax (используемые для установки горизонтального сдвига) на время, указанное в переменной staticLength.


Мы могли бы остановиться здесь. Наш эффект хорошо анимируется и реагирует на нажатия клавиш мыши. Я все еще чувствую, что здесь чего-то не хватает. Давайте добавим небольшой эффект масштабирования.

Для этого шага вам нужно будет загрузить библиотеку Tweener, если у вас ее еще нет. Поместите папку ‘caurina’ в каталог вашего проекта и импортируйте классы Tweener вверху вашего файла JitteryButton:

1
import caurina.transitions.Tweener;

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

01
02
03
04
05
06
07
08
09
10
11
// called on button ROLL_OVER
private function onButtonRollOver(e:MouseEvent):void {
    Tweener.addTween(myButton, {scaleX: 1.1, time: .5, transition: «easeOutElastic»});
    setStaticHigh();
}
 
// called on button ROLL_OOUT
private function onButtonRollOut(e:MouseEvent):void {
    Tweener.addTween(myButton, {scaleX: 1, time: .5, transition: «easeOutElastic»});
    setStaticMedium();
}

Здесь мы добавляем анимацию в обработчик ролловера, который масштабирует свойство scaleX кнопки до 110% за 0,5 секунды. Мы используем упругий тип перехода, чтобы придать ему ощущение бодрости. В обработчике развертывания мы делаем то же самое в обратном порядке, уменьшая его до 100%.

Вы можете узнать больше о том, как использовать Tweener в документации Tweener .


Последнее, что нам нужно сделать, чтобы завершить этот эффект, это добавить немного звука. Я сделал свой звуковой эффект в Garage Band. Вы можете сделать свой собственный или попытаться найти один онлайн.

Когда у вас есть тот, который вам нравится, импортируйте его в свою библиотеку и установите связь для экспорта как «StaticSound».

Чтобы добавить его в наш JitteryButton, нам сначала нужно импортировать класс Sound:

1
import flash.media.Sound;

Затем мы его инициализируем (добавим эту строку непосредственно перед функцией конструктора):

1
private var staticSound:Sound = new StaticSound();

В обработчике ролловера мы сообщим звук для воспроизведения:

1
2
3
4
5
6
// called on button ROLL_OVER
private function onButtonRollOver(e:MouseEvent):void {
    Tweener.addTween(myButton, {scaleX: 1.1, time: .5, transition: «easeOutElastic»});
    setStaticHigh();
    staticSound.play();
}

Теперь мы в порядке. Проверьте свой фильм, и все должно работать. Если ваша кнопка или звук не работают должным образом, проверьте исходные файлы, чтобы увидеть мой законченный класс JitteryButton.


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

Если вы хотите добавить больше кнопок в свое игровое меню, просто создайте новую кнопку и добавьте ее на сцену. Дайте ему имя экземпляра «button2». Затем внутри вашего класса документов (файл Game.as) создайте новый JitteryButton и передайте ему новую кнопку. Вот как это может выглядеть:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package {
    import flash.display.MovieClip;
 
    public class Game extends MovieClip {
 
        private var startButton:JitteryButton;
        private var menuButton:JitteryButton;
 
        // CLASS CONSTRUCTOR
        public function Game() {
            // create the jittery buttons from the simple buttons on the stage
            startButton = new JitteryButton(button1);
            addChild(startButton);
 
            // adding a new button is easy!
            menuButton = new JitteryButton(button2);
            addChild(menuButton);
        }
 
    }
 
}

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

Если вы хотите изменить внешний вид этого эффекта, вы можете попробовать использовать различные типы изображений для графики StaticMap и скорректировать значения для fuzzMin и fuzzMax.

Это мой первый урок, поэтому дайте мне знать, если что-нибудь можно сделать лучше в следующий раз. Спасибо за прочтение!