Nape 2 — Отбираем участников и обрабатываем их взаимодействия

В новом Nape появилась новая система обработки столкновений. Она намного мощнее и гибче предыдущей, что позволяет намного точнее настроить отбор участников и событий столкновения.
Старая система обработки столкновений путем голосования была удалена в новой версии.
Если в старой версии надо было в каждом кадре определять столкновение посредством анализа колбека, то в новой все построено на слушателях.
Перед приведением примеров разберем всех участников сего действа.

Класс CbEvent - содержит в себе константы всевозможных событий физической симуляции (константы описаны ниже).

Класс Listener - является суперклассом для таких классов слушателей: BodyListener, ConstraintListener, InteractionListener, PreListener.

BodyListener — используется если вы хотите слушать события физического тела (Body).
Для физ тела вы можете слушать два события:
CbEvent.SLEEP — тело засыпает;
CbEvent.WAKE — тело просыпается;

ConstraintListener - используется если необходимо слушать события для соединения.
Для соединения возможно слушать три события:
CbEvent.SLEEP — соединение засыпает;
CbEvent.WAKE — соединение просыпается;
CbEvent.BREAK — соединение уничтожается;

InteractionListener — наверное наиболее используемый тип слушателя использует для событий столкновений между телами.
Для него применимы следующие события:
CbEvent.BEGIN — тела начали сталкиваться (то есть первое касание тел);
CbEvent.ONGOING — тела продолжают сталкиваться (Прим. кубик упал на платформу и лежит. Как только он впервые каснулся платформы вызвалось события CbEvent.BEGIN, но в последующих итерациях он продолжает лежать на платформе, то есть сталкиваться с ней, и пока он лежит постоянно генерируется событие CbEvent.ONGOING. Так оно должно работать в идеале как мне видится.. Но происходит немножко по-другому. Если вы слушаете оба события (BEGIN и ONGOING), то при касании);
CbEvent.END — как только тела прекратили сталкиваться;

PreListener - это слушатель используется, если надо сделать какие либо действия непосредственно перед самим столкновение. Это событие генерируется в тот момент, когда движок зафиксировал событие пересечения тел (колизию) но еще не сделал никаких расчетов. Например, при очередном столкновении вам надо, чтобы именно эти два объекта в этот раз не столкнулись. Именно это (и не только) и разрешает сделать этот слушатель. Или еще пример, классический oneWayPlatform тоже делается с помощью этого слушателя(этот вид слушателя несколько специфичен, потому ниже немнжко подробней опишу).
Для этого типа слушателя надо использовать событие CbEvent.PRE.

Следующий класс InteractionType.
Этот класс содержит константы типов взаимодействия.
Когда вы создаете свой слушатель то указываете какой тип взаимодействия вы хотите слушать.
Есть четыре типа:
InteractionType.COLLISION - столкновение двух участников;
InteractionType.SENSOR - сенсорные пересечения;
InteractionType.FLUID - флюидные взаимодействия;
InteractionType.ANY - все типы взаимодействия;

Класс Callback.
Это тип данных, который передается в обработчик события взаимодействия.
Так как существует четыре типа слушателей то для каждого из них существует соответствующий ему тип callback.
То есть класс Callback является супер классом для:
BodyCallback - для типа слушателя BodyListener;
ConstraintCallback — для типа слушателя ConstraintListener;
InteractionCallback - для типа слушателя InteractionListener;
PreCallback - для типа слушателя PreListener;

В классах InteractionCallback и PreCallback есть два поля int1 и int2 типа Interactor - это участники столкновения.

Теперь о CbType
У класса Interactor есть свойство cbType. Этому свойству можно присвоить экземпляр класса CbType.
Для чего это нужно?
Чтобы идентифицировать типы участников. Это свойство помогает выбрать участников столкновения.
Каждый созданный экземпляр это уникальный тип, которым можно обозначить нужных участников.
Например, есть игра в которой имеются такие участники: герой, враги и призы. Вам надо обработать событие столкновений между этими участниками. Для каждого участика вы можете создать тип по которому и будете отбирать и обрабатывать.

var heroType:CbType = new CbType();
var enemyType:CbType = new CbType();
var prizeType:CbType = new CbType();

и потом каждому созданному экземпляру героя, врага или приза присваивать этот тип.

var hero:Body;
hero.cbType = heroType;
 
var enemy:Body;
enemy.cbType = enemyType;
 
var prize:Body;
prize.cbType = prizeType;

Ну что ж, вроде как сделали обзор всех классов-участников в создании, настройке и обработки событий взаимодействий.
Теперь примеры, без них пока все мутно :)

Для начала что то простое, классическое )) — динамический кубик и статическая платформа.
Скажем, мы хотим отследить столкновение кубика и платформы.
Далее кусок кода (тела уже создал ранее и установил им имена в поле userData для вывода их в обработчике — для куба _cube.userData = «Cube», для платформы _platform.userData = «Platform»):

private function initCollisionListeners():void
{
	// Создаем и устанавливаем callback types для тел
	var dynamicBodyType:CbType = new CbType();
	var staticBodyType:CbType = new CbType();
 
	_cube.cbType = dynamicBodyType;
	_platform.cbType = staticBodyType;
 
	// Создаем слушателя для события столкновения
	var beginCollideListener:InteractionListener = 
        new InteractionListener(CbEvent.BEGIN, InteractionType.COLLISION, dynamicBodyType, staticBodyType, beginCollisionHandler);
 
	// Добавляем в список слушателей мира
	_space.listeners.add(beginCollideListener);
}
 
/**
 */
private function beginCollisionHandler(cb:InteractionCallback):void
{
	trace("BEGIN");
	trace("Имя первого участника= " + cb.int1.userData);
	trace("Имя второго участника= " + cb.int2.userData);
}

Так как мы хотим слушать события взаимодействия мы используем тип InteractionListener.
Наша задача обработать столкновение, потому мы используем CbEvent.BEGIN и InteractionType.COLLISION.
Далее мы устанавливаем типы участников, которых хотим отслеживать — dynamicBodyType и staticBodyType.
И последний параметр сам обработчик. Так как мы используем тип слушателя InteractionListener то как параметр в этот обработчик будет передаваться объект типа InteractionCallback.
Вроде пока ничего сложного :)

Давайте вернемся к нашему примеру с героем, врагами и призами.
Сделаем небольшую игру в которой у нас буду падать призы на платформу где ходит враг.
Герою надо собрать как можно больше призов. Нам надо создать такие взаимодействия:
1. Приз касается героя — приз помещается в корзинку героя.
2. Приз касается врага — приз помещается в корзинку врага.
3. Герой касается врага — герой погибает и респаунится на своей платформе.
4. Герой касается земли и только тогда может прыгать.
5. Герой падает «за борт» и тоже погибает.

Исходники тут.

This movie requires Flash Player 9

Поделиться в соц. сетях

Опубликовать в LiveJournal
Опубликовать в Google Plus
  • ggman

    Отличная статья с отличным примером!!

  • VirtualMaestro

    Рад, что помогла :)

  • ggman

    А что это в исходниах за файл InitNape?

  • VirtualMaestro

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

  • ggman

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

  • VirtualMaestro

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

  • J.Logan

    Не подскажете, как можно отслеживать прикосновение с одной из сторон тела?

    Детальнее, что имею ввиду, набросал в этом мувике:
    http://www.flasher.ru/forum/attachment.php?attachmentid=29802&d=1373389452

    На форуме Flasher этот вопрос уже задал, но там на темы Nape почти не отвечают.

    • VirtualMaestro

      Лучше бы знать задачу полностью. Какого эффекта требуется добиться?

      • J.Logan

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

        В принципе, элементарно: После касания с помощью CbEvent.BEGIN я булеаном даю программе понять, что с ногами персонажа началось касание и он может прыгать.
        От CbEvent.END булеан меняет условие, давая понять, что касание прекратилось и прыгать персонаж не может, иначе он может отталкиваться от воздуха.

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