Статьи

Создайте основанный на физике платформер за час

В этом уроке вы научитесь создавать платформенную игру на основе физики как можно быстрее с помощью World Construction Kit.


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

Это немного шатко, но это можно исправить — и подождите, пока не увидите, как быстро и легко это сделать!


Загрузите библиотеки Box2D Alchemy Port и WCK. Получите исходный код от github, а дополнительную информацию смотрите на сайте www.sideroller.com .


Нажмите «Проект» и выберите «Новый проект» из списка. Выберите AS3 Project в качестве шаблона проекта, назовите свой проект, укажите его в пустой директории и нажмите OK.

Найдите библиотеки Box2D / WCK, которые вы скачали на шаге 1, и поместите следующие папки в папку lib вашего нового проекта: Box2D , Box2DAS , дополнения , гравитация , разное , формы и wck .

Снова нажмите «Проект» и выберите «Свойства». Нажмите на вкладку «Classpaths» и добавьте свою папку lib.

Откройте Main.as в исходном файле и добавьте выделенный код. FlashDevelop должен автоматически генерировать все остальное.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class Main extends WCK
{
    public function Main():void
    {
        if (stage) init();
        else addEventListener(Event.ADDED_TO_STAGE, init);
    }
     
    private function init(e:Event = null):void
    {
        removeEventListener(Event.ADDED_TO_STAGE, init);
        // entry point
    }
}

Откройте Flash Professional. Нажмите Ctrl + Shift + F12, чтобы открыть Параметры публикации. Нажмите вкладку Flash. Выберите опцию «Экспортировать SWC»

Флеш физический платформер WCK Box2D

…. а затем нажмите кнопку « Параметры» рядом с полем со списком ActionScript 3.0 .

Флеш физический платформер WCK Box2D

На вкладке «Исходный путь» нажмите на значок «Перейти к пути» и выберите свою папку lib . Затем нажмите на вкладку «Путь к библиотеке» и выберите значок «перейти к SWC». Выберите файл lib / Box2DAS / Box2D.swc .

Нажмите кнопку «ОК» в разделе «Дополнительные параметры ActionScript 3» и снова в окне «Параметры публикации». Сохраните ваш FLA в папке \ src \ вашего проекта FlashDevelop (в той же папке, что и Main.as).

Наконец, нажмите Ctrl + F3, чтобы открыть свойства документа и установить класс документа на Main.


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

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

Флеш физический платформер WCK Box2D

Установите точку регистрации в центре. * Примечание: очень важно, чтобы вы регистрировали все игровые символы таким образом. Невыполнение этого условия повлияет на то, как ваш объект реагирует на гравитацию и столкновения.

Нажмите «Экспорт для Actionscript» и установите базовый класс в shapes.Box


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

Выберите объект Static Box на сцене и нажмите F8.

Флеш физический платформер WCK Box2D

Как и в случае со Static Box, установите точку регистрации мира в центре и установите флажок « Экспорт для ActionScript» .

Установите базовый класс на wck.World


Щелкните правой кнопкой мыши по вновь созданному символу мира в библиотеке.

Выберите «Определение компонента …»

В поле Class введите wck.World

Флеш физический платформер WCK Box2D

Это главная точка продажи для World Construction Kit. Если вы теперь щелкнете по объекту World на сцене и откроете панель свойств, нажав Ctrl + F3, вы можете отредактировать набор проверяемых свойств компонента World под заголовком «Параметры компонента».

Флеш физический платформер WCK Box2D

Хорошо, теперь мы собираемся сделать то же самое с нашим статическим объектом.

Щелкните правой кнопкой мыши по вашему символу Static Box в библиотеке.

Выберите «Определение компонента …»

В поле Класс введите wck.BodyShape

Откройте панель свойств, выбрав объект Static Box на сцене и нажав Ctrl + F3.

Прокрутите окно «Параметры компонента» вниз и измените «тип» с динамического на статический. Если вы забудете это сделать, ваши статические компоненты (стены, полы, платформы) станут подвержены гравитации и упадут с экрана во время выполнения.


Выберите ваш статический объект внутри мира. Скопируйте и вставьте его пару раз.

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

Вот пример одной из моих попыток:

Флеш физический платформер WCK Box2D

Очевидно, что «быть художником» не является обязательным условием для этого урока.


Какой хороший платформер без убедительного главного героя?

Находясь внутри объекта World, нарисуйте прямоугольник. Не стесняйтесь проявлять творческий подход здесь. Это лучшее, что я смог сделать:

Флеш физический платформер WCK Box2D

Преобразуйте своего персонажа в символ, но пока не объявляйте базовый класс.

Щелкните правой кнопкой мыши по вашему новому символу Героя в библиотеке.

Выберите «Определение компонента …»

В поле Класс введите wck.BodyShape


Откройте FlashDevelop.

Убедитесь, что ваш проект открыт. В папке \ src \ создайте новую папку с именем «View». В «View» создайте новую папку с именем «Characters».

Щелкните правой кнопкой мыши «View» и добавьте новый класс.

Назовите свой класс как-нибудь как HeroCharacter.as и установите базовый класс на shapes.Box .

Флеш физический платформер WCK Box2D

Ваша структура папок теперь должна выглядеть так:

Флеш физический платформер WCK Box2D

Это точка входа для добавления функциональности вашим персонажам.

Добавьте следующий код в наш новый класс:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public class HeroCharacter extends Box {
     
    private var contacts:ContactList;
     
    public override function create():void {
        reportBeginContact = true;
        reportEndContact = true;
        contacts = new ContactList();
        contacts.listenTo(this);
         
        fixedRotation = true;
         
        listenWhileVisible(world, StepEvent.STEP, world_stepEventHandler, false, 0, true);
        listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, this_beginContactHandler, false, 0, true);
         
        super.create();
    }
}

Установив для reportBeginContact и reportEndContact значение true , мы устанавливаем свойства BodyShape класса BodyShape . Мы указываем, что мы хотели бы, чтобы BodyShape отправлял ContactEvents когда столкновения начинаются и когда конфликты заканчиваются. Затем мы создаем экземпляр ContactList и просим его « listenTo this ». ContactList.listenTo(this) создает прослушиватели для ContactEvent.BEGIN_CONTACT и ContactEvent.END_CONTACT . Затем он создает обработчики для каждого из них, которые хранят информацию о столкновении. Вы можете увидеть все это, поместив курсор на ContactList и нажав Ctrl + F4 в FlashDevelop.

Установив fixedRotation в true , мы гарантируем, что наш герой не будет вращаться вперед или назад при столкновении с объектами.

listenWhileVisible — это еще один способ добавления прослушивателей событий. Мы могли бы использовать addEventListener(StepEvent.STEP, parseInput, false, 0, true); но добавленная функциональность здесь заключается в том, что listenWhileVisible удалит прослушиватели событий и назначит их для сборки мусора, когда Entity будет удалена из игры. Для наших целей listenWhileVisible является более оптимизированной версией addEventListener . * Примечание. Как и в случае с addEventListener , всегда используйте слабые ссылки, чтобы неиспользуемые объекты могли быть использованы для сборки мусора.

Используя super.create() мы вызываем метод create() для BodyShape . Это позволяет нам расширить функциональность метода create() вместо его замены.


Давайте начнем с создания нашего обработчика StepEvent для ввода игрока.

1
2
3
4
private function world_stepEventHandler(e:StepEvent):void
{
     
}

В каждый интервал времени StepEvent будет отправляться из класса b2World в Box2D. Шаг по умолчанию составляет 0,05 секунды. Вы можете легко изменить параметр timeStep , вернувшись во Flash Professional и открыв параметры компонента World.

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

1
2
3
4
5
6
private function world_stepEventHandler(e:StepEvent):void
{
    var left:Boolean = Input.kd(‘LEFT’);
    var right:Boolean = Input.kd(‘RIGHT’);
    var jump:Boolean = Input.kp(‘UP’);
}

Метод Input.kd может принимать несколько аргументов. Итак, если мы хотим позволить пользователю иметь возможность управлять HeroCharacter с помощью WASD и пробела, мы можем изменить код следующим образом:

1
2
3
4
5
6
private function world_stepEventHandler(e:StepEvent):void
{
    var left:Boolean = Input.kd(‘LEFT’, ‘A’);
    var right:Boolean = Input.kd(‘RIGHT’, ‘D’);
    var jump:Boolean = Input.kp(‘UP’, ‘ ‘, ‘W’);
}

Input.kd() прослушивает Input.kd() клавиши, а Input.kp() прослушивает нажатие клавиши.


Когда импульс применяется к твердому телу, импульс тела изменяется. Импульс — это произведение массы на скорость. Поэтому, когда мы хотим изменить скорость (скорость и направление) нашего игрока, мы будем использовать метод на b2body называемый ApplyImpulse() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private function world_stepEventHandler(e:StepEvent):void
{
    var left:Boolean = Input.kd(‘LEFT’, ‘A’);
    var right:Boolean = Input.kd(‘RIGHT’, ‘D’);
    var jump:Boolean = Input.kp(‘UP’, ‘ ‘, ‘W’);
     
    if (jump) {
        b2body.ApplyImpulse(new V2(0, -2), b2body.GetWorldCenter());
    }
    else if(left) {
        b2body.ApplyImpulse(new V2(-2, 0), b2body.GetWorldCenter());
    }
    else if(right) {
        b2body.ApplyImpulse(new V2(2, 0), b2body.GetWorldCenter());
    }
}

ApplyImpulse() принимает два параметра: мировой импульсный вектор и точку приложения импульса. На данный момент мы передадим новый 2D-вектор в качестве первого параметра для прыжка, перемещения влево и вправо (нам нужно будет отрегулировать, как мы справимся с прыжком чуть позже). Второй параметр для каждого метода b2body.GetWorldCenter() — это b2body.GetWorldCenter() . Этот метод возвращает мировое положение центральной массы нашего героя. Это важно, потому что ApplyImpulse изменит угловую скорость нашего героя, если он не ApplyImpulse на его центральную массу (именно поэтому мы использовали регистрацию центра для героя во Flash).


Вернитесь в Flash Professional и установите класс символа героя на «view.characters.HeroCharacter» и оставьте базовый класс пустым. Затем установите имя экземпляра вашего героя на «герой».

В параметрах компонента компонента World отмените выбор параметра «allowDragging» и выберите «прокрутка». Таким образом, пользователь не сможет перемещать вашего персонажа с помощью мыши, и камера будет следовать за вашим игроком, когда он будет двигаться. Наконец, в поле ‘focusOn’ введите ‘hero’, имя экземпляра вашего героя.

Нажмите Ctrl + Enter, чтобы проверить фильм. Вы заметите, что можете перемещать своего персонажа, нажимая влево и вправо, и можете прыгать с пробелом. Но если вы продолжите нажимать пробел, вы будете продолжать прыгать до бесконечности.

Причина, по которой мы не можем продолжать прыгать до бесконечности, заключается в том, что, как только мы в воздухе, нашим ногам уже нечего толкать, чтобы нас подтолкнуть. Нет равных сил у наших ног отталкивания. Когда мы твердо посажены на землю, сила, которая помогает нам прыгать вверх и удерживает нас от падения через пол, называется нормальной силой. Что нам нужно сделать, это определить, какая нормальная сила действует на ноги наших игроков. Если нет нормальной силы, то он не может сделать прыжок. Мы сделаем это, используя наш ContactList.

Вернитесь в FlashDevelop. Давайте еще раз изменим наш обработчик событий шага:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
private function world_stepEventHandler(e:StepEvent ):void
{
    var manifold:b2WorldManifold = null;
    if(!contacts.isEmpty()) {
        manifold = getNormalForce();
    }
    var left:Boolean = Input.kd(‘LEFT’, ‘A’);
    var right:Boolean = Input.kd(‘RIGHT’, ‘D’);
    var jump:Boolean = Input.kp(‘UP’, ‘ ‘, ‘W’);
                 
    if (jump && manifold) {
        var v:V2 = manifold.normal.multiplyN( -3);
        b2body.ApplyImpulse(v, b2body.GetWorldCenter());
    }
    else if(left) {
        b2body.ApplyImpulse(new V2(-.5, 0), b2body.GetWorldCenter());
    }
    else if(right) {
        b2body.ApplyImpulse(new V2(.5, 0), b2body.GetWorldCenter());
    }
}

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

Теперь давайте напишем метод getNormalForce() .

01
02
03
04
05
06
07
08
09
10
11
12
13
private function getNormalForce():b2WorldManifold
{
    var manifold:b2WorldManifold = null;
    contacts.forEach(function(keys:Array, contactEvent:ContactEvent) {
        var tempManifold:b2WorldManifold = contactEvent.getWorldManifold();
        if (tempManifold.normal.y > 0) {
                tempManifold.normal = new V2(0, tempManifold.normal.y);
                manifold = tempManifold;
        }
    });
    contacts.clean();
    return manifold;
}

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

Сначала мы объявляем локальную переменную с именем manifold и устанавливаем ее в null . Это параметр, который мы будем возвращать. Если персонаж героя находится в контакте с чем-то справа или слева от него (но не на земле), эта функция вернет нулевой коллектор.

Используя метод contacts.forEach() , мы можем проверить каждый ContactEvent в нашем ContactList. Все ContactEvents имеют свойство worldManifold. Поэтому мы создаем другую локальную переменную с именем tempManifold и устанавливаем для нее значение, возвращаемое каждым contactEvent.GetWorldManifold. Затем мы проверяем, больше ли temp.normal.y больше нуля. Здесь мы спрашиваем, есть ли нормальная сила оси Y?

Если герой находится на земле или платформе, мы обнуляем любую нормальную силу оси X. Невыполнение этого условия приводит к прыжкам с багги, когда игрок толкается к стене. Не стесняйтесь экспериментировать с этим. Если вы не обнуляете x, игрок получает классную (но ненадежную) способность Metroid к прыжкам со стены.

Наконец, очистите список контактов. Мы не хотим обрабатывать одни и те же контакты более одного раза.


Теперь, когда у нас есть главный герой, который может бегать и прыгать, давайте добавим несколько предметов, которые он может подобрать. Вернитесь в Flash Professional, нарисуйте круг или эллипс для монеты и преобразуйте его в символ. Установите класс и базовый класс, как показано:

Флеш физический платформер WCK Box2D

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


Прямо сейчас монеты являются неподвижными, статичными объектами. Мы хотели бы изменить это. Вернитесь к FlashDevelop и откройте класс HeroCharacter . Добавьте обработчик событий для таких столкновений:

1
2
3
4
private function this_beginContactHandler(e:ContactEvent):void
{
     
}

Это обработчик для слушателя, который мы создали на шаге 11. Добавьте следующий код:

1
2
3
4
5
6
7
private function this_beginContactHandler(e:ContactEvent):void
{
    var coin:Coin = e.other.m_userData as Coin;
    if(coin) {
        coin.remove();
    }
}

Сначала мы создаем локальную переменную с именем coin того же типа, что и класс монет, созданный вами во Flash. ContactEvent отслеживает другой прибор Box2D, участвующий в столкновении. Если это монета, мы удаляем ее со сцены, создавая иллюзию, что она была собрана.


Создайте папку в каталоге \ src \ под названием «модель». Внутри «model» создайте папку с именем «Scoreboard» и создайте новый класс « ScoreBoard который расширяет EventDispatcher . Так как мы хотим, чтобы за один раз был только один экземпляр табло, мы будем следовать шаблону проектирования Singleton. В начале этого года был краткий совет по шаблону Singleton на Activetuts +, если вы хотите получить ссылку.

Напишите следующий код в классе ScoreBoard:

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
package model.scoreboard
{
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
 
    public class ScoreKeeper extends EventDispatcher
    {
        private static var _instance:ScoreKeeper;
         
        public function ScoreKeeper()
        {
            if (_instance != null)
            {
                throw new IllegalOperationError(«Use ScoreBoard.getInstance() to get a reference to the Singleton ScoreKeeper.»);
            }
            else
            {
                initialize();
            }
        }
         
        private function initialize():void
        {
             
        }
         
        public static function getInstance():ScoreKeeper
        {
            if (_instance == null) _instance = new ScoreKeeper();
            return _instance;
        }
         
    }
 
}

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

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

Теперь нам нужно ScoreKeeper, чтобы фактически начать вести счет. Нам нужна переменная для хранения счета, метод, который увеличивает счет, метод получения для оценки, чтобы другие классы могли получить к нему доступ, и public static const для хранения нашего типа события.

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
package model.scoreboard
{
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
 
    public class ScoreKeeper extends EventDispatcher
    {
        public static const SCORE_CHANGED:String = «SCORE_CHANGED»;
        private var _score:uint;
        private static var _instance:ScoreKeeper;
         
        public function ScoreKeeper()
        {
            if (_instance != null)
            {
                throw new IllegalOperationError(«Use ScoreBoard.getInstance() to get a reference to the Singleton ScoreKeeper.»);
            }
            else
            {
                initialize();
            }
        }
         
        private function initialize():void
        {
            _score = 0;
        }
         
        public function incrementScore():void
        {
            _score++;
            dispatchEvent(new Event(«SCORE_CHANGED»));
        }
         
        public static function getInstance():ScoreKeeper
        {
            if (_instance == null) _instance = new ScoreKeeper();
            return _instance;
        }
         
        public function get score():uint { return _score;
         
    }
 
}

И это все, что нам нужно для нашего ScoreKeeper. Теперь давайте создадим компонент вида для отображения номера счета. Войдите во Flash и на сцене (не внутри символа мира) вытащите табло. Здесь важно только то, что вы используете Text Tool для рисования TextField с именем экземпляра ‘ score ‘. Преобразуйте TextField в символ видеоклипа с именем ScoreBoard .

Вернувшись в FlashDevelop, в папку world, создайте класс «ScoreDisplay», который расширяет MovieClip. Все, что нам нужно сделать здесь, это получить экземпляр ScoreKeeper и подписаться на его события. Это должно выглядеть так:

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
package view.world
{
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.text.TextField;
    import model.scoreboard.ScoreKeeper;
 
    public class ScoreDisplay extends MovieClip
    {
        private var _scoreKeeper:ScoreKeeper = ScoreKeeper.getInstance();
         
        public function ScoreDisplay()
        {
            this.score.text = «0»;
            _scoreKeeper.addEventListener(ScoreKeeper.SCORE_CHANGED, scoreBoard_ScoreChangedHandler, false, 0, true);
        }
         
        private function scoreBoard_ScoreChangedHandler(e:Event):void
        {
            this.score.text = _scoreKeeper.score.toString();
        }
         
    }
 
}

Вернитесь во Flash и откройте свойства символа ScoreBoard в библиотеке. Измените класс на view.world.ScoreDisplay .

Флеш физический платформер WCK Box2D

У вас есть последний шаг. Вернитесь в класс HeroCharacter и добавьте две строки кода:

1
2
3
4
5
6
7
8
private function this_beginContactHandler(e:ContactEvent):void
{
    var coin:Coin = e.other.m_userData as Coin;
    if(coin) {
        coin.remove();
        scoreBoard.incrementScore();
    }
}
1
2
3
4
public class HeroCharacter extends Box {
 
    private var contacts:ContactList;
    private var scoreKeeper:ScoreKeeper = ScoreKeeper.getInstance();

Перейдите во Flash Professsional и поместите экземпляр StaticBox (тот же, который мы использовали для создания стен и пола) внутри экземпляра World. Убедитесь, что вы установили его тип на static в Component Parameters и что платформа достаточно низкая, чтобы ваш игрок мог перейти к нему.


WCK делает создание качающихся платформ очень простым. Мы можем сделать все это во Flash IDE без написания кода.

Начните с рисования круга. Преобразуйте круг в символ с именем Joint и установите для базового класса значение wck.Joint . Затем щелкните правой кнопкой мыши символ «Соединение» в библиотеке и перейдите к « Определение компонента» . Установите класс как wck.Joint . На панели «Свойства» установите имя экземпляра в качестве anchor а в параметрах компонента измените type на « Revolute . Это соединение, которое придаст нашей платформе маятниковое действие.

Нарисуйте платформу с помощью инструмента «Прямоугольник». Выберите его и конвертируйте в символ. Установите базовый класс на extras.Platform . Щелкните правой кнопкой мыши на символе в библиотеке и в определении компонента установите для Class значение extras.Platform .

Вытащите еще два экземпляра Объединенного класса в Мир и поместите каждый из них на любом конце Платформы. Макет должен выглядеть так:

Флеш физический платформер WCK Box2D

Для каждого нового экземпляра Joint перейдите в Параметры компонента и измените type на « Distance », а в поле target2Name напишите « anchor ». Протестируйте свой фильм, и у вас должна быть качающаяся платформа.


В FlashDevelop добавьте новый класс в папку \ characters \ EnemyCharacter . Вот код, который мы собираемся написать (это будет выглядеть очень знакомо):

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
package view.characters
{
    import Box2DAS.Common.V2;
    import Box2DAS.Dynamics.ContactEvent;
    import Box2DAS.Dynamics.StepEvent;
    import shapes.Box;
    import wck.ContactList;
 
    public class EnemyCharacter extends Box {
         
        private var contacts:ContactList;
        private var left:Boolean = true;
        private var right:Boolean;
         
        public override function create():void {
            fixedRotation = true;
            reportBeginContact = true;
         
            super.create();
     
            contacts = new ContactList();
            contacts.listenTo(this);
            listenWhileVisible(world, StepEvent.STEP, world_stepEventHandler, false, 0, true);
            listenWhileVisible(this, ContactEvent.BEGIN_CONTACT, this_beginContactHandler, false, 0, true);
             
        }
         
        private function world_stepEventHandler(e:StepEvent ):void
        {
            if(left) {
                b2body.ApplyImpulse(new V2(-.1, 0), b2body.GetWorldCenter());
            }
            else if(right) {
                b2body.ApplyImpulse(new V2(.1, 0), b2body.GetWorldCenter());
            }
        }
         
        private function this_beginContactHandler(e:ContactEvent):void
        {
            var wall:StaticBox = e.other.m_userData as StaticBox;
            if(wall) {
                left = !left;
                right = !right;
            }
        }
    }
 
}

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

Вернитесь во Flash и нарисуйте вражеского персонажа и преобразуйте его в символ с базовым классом, для которого установлено значение view.characters.EnemyCharacter а для класса — Enemy .

Последнее, что нам нужно сделать, это обработать контакт между персонажем игрока и вражеским персонажем. В классе HeroCharacter добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
private function this_beginContactHandler(e:ContactEvent):void
{
    var coin:Coin = e.other.m_userData as Coin;
    trace(coin);
    if(coin) {
        coin.remove();
        scoreKeeper.incrementScore();
    }
    else {
        var enemy:EnemyCharacter = e.other.m_userData as EnemyCharacter;
        if (enemy)
        {
            var tempManifold:b2WorldManifold = e.getWorldManifold();
            if (tempManifold.normal.y > 0)
            {
                Util.addChildAtPosOf(world, new BadGuyFX(), enemy);
                enemy.remove();
            }
        }
    }
}

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

Я хотел добавить анимацию раздавливания EnemyCharacter, поэтому во Flash я сделал ролик с анимацией временной шкалы, когда враг сокрушен. Я установил для базового класса этого объекта BadGuyFX значение misc.FX , класс в библиотеке WCK, который один раз воспроизводит собственную анимацию временной шкалы, а затем устанавливает себя в null . Затем я добавил его в рабочую область с помощью метода Util addChildAtPosOf() . Благодаря анимации удаление врага не кажется таким внезапным.


Теперь, когда у вас есть рабочий прототип платформера, я призываю вас продолжать исследовать то, что может предложить WCK. Я особенно рекомендую поиграть в Component Parameters ваших игровых объектов. Это действительно забавный и быстрый способ изменить физику игрового мира без написания кода. Я надеюсь, вам понравился этот урок! Спасибо за прочтение!