Статьи

Обнаружение комбинаций клавиш, легкий путь

Вы когда-нибудь были поражены разнообразием атак в файтингах, таких как Mortal Kombat, Super Smash Bros, Soul Calibur и других? Теперь вы можете узнать, как создать движок для определения комбинаций клавиш, а также создать свою собственную игру-файтинг!


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

Комбинации в этой демонстрации: ASDF , AAA и SSS . Введите их!


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


Для этого урока мы будем использовать чистый проект AS3 FlashDevelop с preloader. Мы создадим проект с предварительным загрузчиком, чтобы вам было проще, если вы хотите продолжать работать над конечным результатом игры. Начнем с открытия FlashDevelop и выбора нашего проекта:

Начиная новый проект на FlashDevelop

С этим мы можем начать работать над нашими классами.

Чтобы использовать графику, которую мы создадим во Flash Pro в рамках нашего проекта AS3, нам нужно экспортировать наши изображения из файла .fla в формат .swc. Более подробную информацию об этом формате можно найти в Варианте 2 этого руководства по FlashDevelop . Создайте новый FLA AS3 во Flash Professional, затем измените настройки в нашем файле .fla, чтобы экспортировать его содержимое в формат .swc: перейдите в « Файл»> «Параметры публикации» (или нажмите Ctrl + Shift + F12) и выберите «Экспорт SWC» поле под вкладкой «Flash».

Опция экспорта в SWC

Если у вас нет Flash Professional, не беспокойтесь. Я включил окончательный файл SWC в пакет загрузки для этого урока. Загрузите его, затем перейдите к шагу 6.


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

Кнопка, которую мы будем использовать

Для получения более подробной информации о том, как была создана кнопка, найдите исходные файлы для этого урока!


Теперь мы должны дать нашей кнопке «вверх» и «вниз» изображения. Прежде чем сделать это, нам нужно превратить его в символ. Давайте преобразуем его в символ, дадим ему имя KeyButtonImage и экспортируем как SWCAssets.KeyButtonImage. Мы добавляем пакет SWCAssets в имя класса для организационных целей, когда начинаем кодирование. Это будет более ясно позже.

Создание символа для хранения изображения нашей кнопки

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

Вниз изображение нашей кнопки

Теперь, когда у нас есть готовое изображение кнопки, пришло время сгенерировать наш файл .swc и добавить его в наш проект FlashDevelop. Для публикации нажмите Alt + Shit + F12. Вы заметите, что файл .swc был создан в том же каталоге флэш-файла. Скопируйте этот файл и поместите его в папку «lib» нашего проекта FlashDevelop. Щелкните правой кнопкой мыши и выберите «Добавить в библиотеку», чтобы FlashDevelop распознал файл. Имя файла станет синим после его добавления в библиотеку.

Генерация .swc файла
Добавление файла .swc в библиотеку

FlashDevelop теперь готов начать использовать изображение нашей кнопки!


Наше изображение готово, поэтому нам нужно создать класс для хранения изображения и добавить к нему функциональные возможности. В FlashDevelop добавьте новый класс в папку src, назовите его KeyButton и поместите flash.display.Sprite в качестве базового класса.

Создание класса KeyButton

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

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
package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.text.TextFormatAlign;
    import SWCAssets.KeyButtonImage;
     
    public class KeyButton extends Sprite
    {
        private var _image:KeyButtonImage;
         
        private var _text:TextField;
         
        public function KeyButton(letter:String)
        {
            _image = new KeyButtonImage();
            _image.stop();
             
            addChild(_image);
             
            _text = new TextField();
            _text.defaultTextFormat = new TextFormat(«Verdana», 28, 0xFFFFFF, true, null, null, null, null, TextFormatAlign.CENTER);
            _text.text = letter;
            _text.height = 38;
             
            _text.x = -(_text.width / 2);
            _text.y = -(_text.height / 2);
             
            _text.mouseEnabled = false;
             
            addChild(_text);
        }
         
    }
 
}

В строках 11 и 13 мы объявляем переменные, которые будут содержать изображение нашей кнопки и текст письма соответственно. В конструкторе нашего класса (строка 15) мы запрашиваем строку, которая будет буквой нашей кнопки.

Так как наш KeyButtonImage имеет два фрейма, в строке 18 мы вызываем метод stop() чтобы остановить его цикл по ним. Мы определим конкретные моменты для изображения, чтобы переключать кадры позже. Строка 20 добавляет изображение в дочерний список кнопки.

От строки 22 до строки 30 мы создаем наше текстовое поле, которое будет содержать букву, позиционируем ее и отключаем от событий мыши (не обязательно, но хорошо, если предполагается, что ваше текстовое поле должно делать только отображение текста). Строка 32 добавляет текст в дочерний список кнопки.


Наш класс KeyButton уже может показывать изображение и представлять букву, поэтому на этом шаге мы добавим несколько кнопок на экран. Поскольку мы создаем только пример для проверки нашего класса, мы не будем добавлять все буквы в этом примере. Вместо этого мы будем работать только с 4 буквами (но наш класс сможет определять комбинации с любыми клавишами!): A, S, D и F. Теперь мы добавим их на наш экран:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private var keyButtons:Array = [];
private var keys:Array = [«A», «S», «D», «F»];
 
private function init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
     
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        keyButtons[i] = new KeyButton(keys[i]);
        KeyButton(keyButtons[i]).x = 100 + (100 * i);
        KeyButton(keyButtons[i]).y = 50;
         
        addChild(KeyButton(keyButtons[i]));
    }
}

В строке 1 создается массив, содержащий все кнопки на нашем экране. Это будет очень полезно позже, потому что это позволит нам проходить по массиву вместо проверки кнопка за кнопкой. Строка 2 только определяет, с какими ключами мы будем работать (как сказано, A, S, D и F). Строки 12-16 находятся внутри цикла, который создаст 4 кнопки и разместит их на экране.

Теперь вы можете скомпилировать проект и увидеть кнопки на экране:

Кнопки на экране

Теперь мы готовы начать работу по обнаружению комбо. Для этого мы создадим класс ComboHandler . Просто выполните те же шаги, которые вы сделали для создания класса KeyCombo , но на этот раз наш класс ComboHandler не будет иметь базового класса.

Какой должна быть первая часть класса ComboHandler ? Что ж, сначала нам нужно определить, когда была нажата клавиша. Чтобы сделать это, нам нужно добавить прослушиватель событий на сцену (помните: как предложено в ссылке на ActionScript 3 , чтобы глобально прослушивать прослушиватели событий KeyboardEvent , их нужно добавить на сцену!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package
{
    import flash.display.Stage;
    import flash.events.KeyboardEvent;
     
    public class ComboHandler
    {
         
        public static function initialize(stageReference:Stage):void
        {
            stageReference.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        }
         
        private static function onKeyDown(e:KeyboardEvent):void
        {
        }
         
    }
 
}

Этот код только строит базовую структуру класса ComboHandler . Гораздо больше будет добавлено позже! Обратите внимание, что мы использовали только статические методы. Это потому, что в нашем примере у нас будет только один класс ComboHandler . Некоторые предложения по улучшению этого класса доступны на заключительном этапе.

Наш класс ComboHandler должен быть инициализирован с помощью метода initialize() для добавления слушателя на сцену. В нашем Main классе мы должны инициализировать класс перед работой с ним. Давайте отправимся на Main.as и сделаем это:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private function init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
     
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        keyButtons[i] = new KeyButton(keys[i]);
        KeyButton(keyButtons[i]).x = 100 + (100 * i);
        KeyButton(keyButtons[i]).y = 50;
         
        addChild(KeyButton(keyButtons[i]));
    }
     
    ComboHandler.initialize(stage);
}

У нас есть базовая структура класса ComboHandler , поэтому теперь нам нужно добавить что-то к нему. Первым делом нужно зарегистрировать комбо в классе, чтобы он мог обнаружить комбо.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
private static var combos:Dictionary;
 
public static function initialize(stageReference:Stage):void
{
    combos = new Dictionary();
     
    stageReference.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
 
public static function registerCombo(comboName:String, comboKeys:Array):Boolean
{
    if (combos[comboName])
    {
        return false;
    }
     
    combos[comboName] = comboKeys;
     
    return true;
}

В строке 1 мы создаем словарь, о котором мы говорили. Строка 5 инициализирует его, а функция registerCombo() регистрирует комбо в словаре. Эта функция вернет true если комбо было успешно зарегистрировано, или false если в классе уже была комбо с таким именем (в этом случае вам может потребоваться удалить старую комбо — см. Шаг 19!).


Еще одна вещь, которую должен иметь наш класс, — это максимальный интервал между нажатиями клавиш. В некоторых играх, в которых есть комбо, вы, вероятно, заметили, что когда вы нажимаете, например, клавишу A, подождите секунду и нажмете клавишу B (при условии, что есть комбинация «AB»), комбо не будет обнаружено, потому что вы тоже ждали много, чтобы нажать клавишу B. Это происходит потому, что между каждым нажатием клавиши существует максимальный интервал времени. Это именно то, что мы будем делать в нашем классе. Итак, в классе ComboHandler :

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
private static var pressedKeys:Array;
 
private static const MAX_INTERVAL:int = 250;
private static var interval:int;
 
public static function initialize(stageReference:Stage):void
{
    combos = new Dictionary();
     
    interval = 0;
     
    stageReference.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
 
private static function onKeyDown(e:KeyboardEvent):void
{
    if (getTimer() — interval > MAX_INTERVAL)
    {
        pressedKeys = [];
    }
     
    interval = getTimer();
     
    pressedKeys.push(e.keyCode);
}

В строке 1 мы создаем массив с pressedKeys . Этот массив будет содержать все клавиши, нажатыми пользователем, с интервалом времени между двумя клавишами меньше, чем максимальный интервал.

Строки 3 и 4 определяют константу MAX_INTERVAL , которая будет нашим максимальным интервалом, и переменную interval , которая поможет нам вычислить, каково было время интервала между двумя нажатиями клавиш.

Функция onKeyDown() уже почти завершена: в ней мы сначала обнаруживаем, превышает ли интервал между текущим нажатием клавиши и последним нажатием клавиши максимальный интервал. Если это так, то мы сбрасываем наш массив pressedKeys , чтобы удалить все ключи, которые были в нем. После этого мы обновляем переменную интервала до текущего времени (см. Документацию getTimer () для получения более подробной информации о том, как мы вычисляем интервал) и push() текущую клавишу в массиве pressedKeys .


В функции onKeyDown() нашего ComboHandler отсутствует последняя вещь: возможность проверить, был ли после нажатия клавиши комбо выполнено пользователем. Вот что мы будем делать сейчас:

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
private static function onKeyDown(e:KeyboardEvent):void
{
    if (getTimer() — interval > MAX_INTERVAL)
    {
        pressedKeys = [];
    }
     
    interval = getTimer();
     
    pressedKeys.push(e.keyCode);
     
    checkForCombo();
}
 
private static function checkForCombo():void
{
    var i:int;
    var comboFound:String = «»;
     
    for (var comboName:String in combos)
    {
        if (pressedKeys.join(» «).indexOf((combos[comboName] as Array).join(» «)) > -1)
        {
            comboFound = comboName;
             
            break;
        }
    }
     
    // Combo Found
    if (comboFound != «»)
    {
        pressedKeys = [];
    }
}

Строка 12 — единственное изменение, которое мы сделали в нашей функции onKeyDown() : вызов функции checkForCombo() . Это проверит, была ли выполнена комбо или нет.

Способ проверки выполнения комбо очень интересен: мы работаем со строками. Работа со строками, в этом случае, позволяет нам обнаруживать вещи, которые были бы сложнее, не работая с ними. Например, представьте, если у нас есть комбинация с ключами ASDF, но массив pressedKeys имеет следующую последовательность ключей: ASFDASDF. Даже если пользователь нажал первые четыре клавиши («ASFD», которые не соответствуют комбо) в течение срока, это не должно изменить тот факт, что пользователь выполнил комбо, как указано последними 4 ключи («ASDF»). Без струн наша работа могла бы быть намного дольше.

Идея работы со строками заключается в следующем: мы помещаем все ключи в pressedKeys внутри строки, разделяя каждый индекс пробелом (таким образом, pressedKeys.join(" ") функция pressedKeys.join(" ") ), затем проверяем, есть ли в ней определенная подстрока , Эта конкретная подстрока является строкой, образованной ключами комбо, также каждый ключ отделяется пробелом. Если эта подстрока найдена, это означает, что комбо было выполнено.

Вы можете проверить это самостоятельно с помощью следующего кода:

1
2
pressedKeys = [«A», «S», «F», «D», «A», «S», «D», «F»];
checkForCombo();

… хотя вы также захотите добавить временный вызов trace(comboFound) в checkForCombo() чтобы увидеть результат.

Обратите внимание, однако, что этот метод не будет работать во всех случаях. Это не сработает, если вместо массива Strings у нас будет, например, массив объектов. Если бы у нас был массив Object, функция toString () по умолчанию, которая вызывается при вызове join (), выдает «[object Object]», и, следовательно, все наши объекты в массиве будут «одинаковыми» в созданной строке. , Если вы все еще хотите это сделать, просто переопределите стандартную функцию toString () и поместите туда собственный текст. На шаге 14 мы сделаем это в классе ComboEvent — посмотрите на него для справки!

Теперь мы перестанем фокусироваться на классе ComboHandler и снова поработаем над созданными нами кнопками. (Удалите тестовый код, который вы только что добавили.)


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

1
2
3
4
5
6
7
8
9
public function onUp():void
{
    _image.gotoAndStop(«Up»);
}
 
public function onDown():void
{
    _image.gotoAndStop(«Down»);
}

Обратите внимание, что этот код изменяет только кадры изображения кнопки. Нам по-прежнему нужно вызывать их, когда нажата соответствующая клавиша. Мы сделаем это в Main.as:

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
private function init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
     
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        keyButtons[i] = new KeyButton(keys[i]);
        KeyButton(keyButtons[i]).x = 100 + (100 * i);
        KeyButton(keyButtons[i]).y = 50;
         
        addChild(KeyButton(keyButtons[i]));
    }
     
    ComboHandler.initialize(stage);
     
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
}
 
private function onKeyDown(e:KeyboardEvent):void
{
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        if (String.fromCharCode(e.keyCode) == keys[i])
        {
            KeyButton(keyButtons[i]).onDown();
             
            break;
        }
    }
}
 
private function onKeyUp(e:KeyboardEvent):void
{
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        if (String.fromCharCode(e.keyCode) == keys[i])
        {
            KeyButton(keyButtons[i]).onUp();
             
            break;
        }
    }
}

Строки 18 и 19 являются единственными дополнениями к функции init() . Они добавляют больше слушателей событий на сцене, чтобы определить, когда клавиша была нажата и когда клавиша была отпущена. Мы будем использовать этих двух слушателей, чтобы сообщить нашим кнопкам, должны ли они быть вверх или вниз.

Единственное, что вы, возможно, никогда не видели в onKeyDown() и onKeyUp() функция String.fromCharCode() . Эта функция возвращает строку с кодами символов, которые вы передаете в качестве аргументов. Поскольку мы передаем только один код символа, эта функция будет возвращать строку только с одним символом, и если она соответствует строкам, которые есть в массиве ключей, это означает, что мы должны сказать соответствующей кнопке действовать.

Теперь вы можете проверить кнопки, идущие вверх и вниз!


Прямо сейчас наш класс ComboHandler обнаруживает нажатия клавиш, обрабатывает интервалы времени, проверяет, когда были выполнены комбо, но он все еще не действует, когда комбо было выполнено. Как это может сказать другим вещам, что комбо было сделано? Это то, что мы начнем здесь. Поскольку Flash имеет очень сильную систему событий, мы отправим событие, когда будет обнаружен комбо, и позволим другим объектам получать эти события.

Для получения дополнительной информации о пользовательских событиях, пожалуйста, перейдите по этой ссылке на 8bitrocket . Создайте класс ComboEvent с этим кодом в нем:

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
package
{
    import flash.events.Event;
     
    public class ComboEvent extends Event
    {
        public static const COMBO_FINISHED:String = «comboFinished»;
         
        public var params:Object;
         
        public function ComboEvent(type:String, params:Object, bubbles:Boolean = false, cancelable:Boolean = false)
        {
            super(type, bubbles, cancelable);
             
            this.params = params;
        }
         
        public override function clone():Event
        {
            return new ComboEvent(type, this.params, bubbles, cancelable);
        }
         
        public override function toString():String
        {
            return formatToString(«ComboEvent», «params», «type», «bubbles», «cancelable»);
        }
         
    }
 
}

В строке 9 мы объявляем переменную params . Он будет содержать информацию о выполненном комбо (в этом примере мы будем указывать только имя, но вы можете указать все, что захотите). Обратите внимание на функцию clone() в классе. Он создан для того, чтобы повторно отправленные события могли содержать ту же информацию, что и исходное событие. Для получения дополнительной информации об этом, посетите этот пост в блоге на Бит 101


Теперь в нашем классе ComboHandler пришло время действовать, когда комбо было выполнено. Для этого нам нужно отправить ComboEvent . События могут отправляться только объектами, которые наследуются от EventDispatcher , но ComboHandler не наследуется от EventDispatcher . Вместо того, чтобы наследовать его от EventDispatcher (что заставило бы нас иметь экземпляр ComboHandler , который нам не нужен для целей этого урока), мы создадим объект EventDispatcher в классе и сделаем этот объект событиями отправки. Кроме того, другие классы будут прослушивать этот объект для событий. В нашем файле ComboHandler.as:

Импортируйте это:

1
import flash.events.EventDispatcher;

И добавьте этот код:

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
public static var dispatcher:EventDispatcher;
 
public static function initialize(stageReference:Stage):void
{
    combos = new Dictionary();
     
    interval = 0;
     
    dispatcher = new EventDispatcher();
     
    stageReference.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
}
 
private static function checkForCombo():void
{
    var i:int;
    var comboFound:String = «»;
     
    for (var comboName:String in combos)
    {
        if (pressedKeys.join(» «).indexOf((combos[comboName] as Array).join(» «)) > -1)
        {
            comboFound = comboName;
             
            break;
        }
    }
     
    // Combo Found
    if (comboFound != «»)
    {
        pressedKeys = [];
         
        dispatcher.dispatchEvent(new ComboEvent(ComboEvent.COMBO_FINISHED, {comboName: comboFound} ));
    }
}

В строке 1 мы объявляем наш диспетчерский объект. В строке 9 мы его инициализируем. В строке 34 мы отправляем ComboEvent . И это все для класса ComboHandler . Теперь нам нужно послушать событие, которое отправляется.


Проверка пользовательских событий выполняется точно так же, как вы работаете с любым другим событием в AS3: добавьте прослушиватель в диспетчер и создайте функцию, которая будет что-то делать, когда прослушиватель получает событие. Наш Main класс должен быть тем, кто получит событие, потому что он покажет что-то на экране, когда обнаружена комбо.

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 init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
     
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        keyButtons[i] = new KeyButton(keys[i]);
        KeyButton(keyButtons[i]).x = 100 + (100 * i);
        KeyButton(keyButtons[i]).y = 50;
         
        addChild(KeyButton(keyButtons[i]));
    }
     
    ComboHandler.initialize(stage);
     
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
     
    ComboHandler.dispatcher.addEventListener(ComboEvent.COMBO_FINISHED, onComboComplete);
}
 
private function onComboComplete(e:ComboEvent):void
{
     
}

ComboHandler строка — это место, где мы добавляем слушателя в ComboHandler .


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

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
private var textField:TextField;
 
private function init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
     
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        keyButtons[i] = new KeyButton(keys[i]);
        KeyButton(keyButtons[i]).x = 100 + (100 * i);
        KeyButton(keyButtons[i]).y = 50;
         
        addChild(KeyButton(keyButtons[i]));
    }
     
    ComboHandler.initialize(stage);
     
    textField = new TextField();
    textField.defaultTextFormat = new TextFormat(«Verdana», 20, 0x000000, true);
    textField.x = 100;
    textField.y = 200;
    textField.width = 350;
    textField.text = «No combo»;
     
    addChild(textField);
     
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
     
    ComboHandler.dispatcher.addEventListener(ComboEvent.COMBO_FINISHED, onComboComplete);
}
 
private function onComboComplete(e:ComboEvent):void
{
    textField.text = e.params.comboName;
}

Строка 1 содержит объявление текстового поля, которое мы будем использовать. Строки 20-27 содержат инициализацию текстового поля. В нашей функции onComboComplete() мы помещаем только комбо-имя в текст текстового поля. Это оно! Теперь осталось только зарегистрировать несколько комбо и протестировать класс!


В файле Main.as давайте зарегистрируем несколько комбинаций: я зарегистрирую «AAA Combo», «SSS Combo» и «ASDF Combo». Вы можете зарегистрировать столько комбо, сколько захотите!

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
private function init(e:Event = null):void
{
    removeEventListener(Event.ADDED_TO_STAGE, init);
     
    var i:int;
     
    for (i = 0; i < 4; i++)
    {
        keyButtons[i] = new KeyButton(keys[i]);
        KeyButton(keyButtons[i]).x = 100 + (100 * i);
        KeyButton(keyButtons[i]).y = 50;
         
        addChild(KeyButton(keyButtons[i]));
    }
     
    ComboHandler.initialize(stage);
    ComboHandler.registerCombo(«AAA Combo», [65, 65, 65]);
    ComboHandler.registerCombo(«SSS Combo», [83, 83, 83]);
    ComboHandler.registerCombo(«ASDF Combo», [65, 83, 68, 70]);
     
    textField = new TextField();
    textField.defaultTextFormat = new TextFormat(«Verdana», 20, 0x000000, true);
    textField.x = 100;
    textField.y = 200;
    textField.width = 350;
    textField.text = «No combo»;
     
    addChild(textField);
     
    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
     
    ComboHandler.dispatcher.addEventListener(ComboEvent.COMBO_FINISHED, onComboComplete);
}

Скомпилируйте проект и вот что вы получите:


Что если мы хотим удалить зарегистрированное комбо? Следующая функция может быть добавлена ​​в файл ComboHandler.as для сброса положения словаря комбо по умолчанию. Он вернет true, если комбо было удалено, и false, если это не так (не было зарегистрировано комбо с таким именем).

01
02
03
04
05
06
07
08
09
10
11
public static function removeCombo(comboName:String):Boolean
{
    if (combos[comboName])
    {
        combos[comboName] = undefined;
         
        return true;
    }
     
    return false;
}

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

Однако базовый комбо-обработчик подходит не для всех игр. Что, если вы хотите использовать игру, в которой есть разные персонажи, и у каждого персонажа есть свои комбинации, и вам нужен больший контроль над обработчиками? Вы можете легко это сделать, если разрешите создание экземпляров класса ComboHandler , что останется для вас проблемой.

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

У вас было творческое использование для этого класса? Поделитесь этим с нами в разделе комментариев!