Nape 2 — Создаем пространство с телами

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

private var _space:Space;
_space = new Space(new Vec2(0, 400), Broadphase.SWEEP_AND_PRUNE);

Первое на что надо обратить внимание всего один класс пространства — Space. Больше никаких пространств.
Второе, два параметра — первый гравитация (по-умолчанию 0,0), второй это алгоритм для нахождения столкновений в данном пространстве.
В классе Broadphase представлены две константы отвечающие за идентификацию этих алгоритмов:
Broadphase.SWEEP_AND_PRUNE
Broadphase.DYNAMIC_AABB_TREE

Сильно не вникал в сами алгоритмы но вот что пишет Лука по этому поводу.
SWEEP_AND_PRUNE - алгоритм более быстрый чем DYNAMIC_AABB_TREE и если у вас мало динамических постоянно движущихся объектов, то этот метод более предпочтительный.
DYNAMIC_AABB_TREE — этот метод работает намного легче с большим диапазоном размеров объектов чем SWEEP_AND_PRUNE. Также этот метод предпочтительней если в вашем мире много статических, спящих или движущихся объектов.

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

Далее как и в старой версии слушаем ENTER_FRAME и в обработчике вызываем метод step из space с тремя параметрами — моделируемое время, (я так понимаю) количество итераций для скорости и позиции, чем выше это значение тем больше надо времени для просчета но тем точнее симуляция (по умолчанию установлено в 10).

private var _timeStep:Number = 1/30.0;
private var _velocityIterations:int = 10;
private var _positionIterations:int = 10;
 
private function enterFrameHandler(event:Event):void
{
     _space.step(_timeStep, _velocityIterations, _positionIterations);
}

Пространство создано и симуляция работает, вот только пока нечего симулировать.
Добавим тела.
Есть (как и было) три типа тел — статическое, динамическое, кинематическое. Константы для этих типов хранятся в классе BodyType.
Итак создадим тело. Статическую платформу и динамический кубик, который будет падать на нее :)
Конструктор класса Body принимает два параметра — типа тела и позицию в пространстве. Тип задаем, а позицию оставляем, мы потом ее поставим.

private function createBodies():void
{
	var staticBody:Body = new Body(BodyType.STATIC);
	var dynamicBody:Body = new Body(BodyType.DYNAMIC); 
}

Само тело это пустой враппер. Оно не имеет ни массы ни площади. Эти свойства задают фигуры (Shape) внутри тел. Потому, чтобы наше тело обрело физические и геометрические свойства надо добавить в него фигуру.
Есть два типа фигур — круг (Circle) и полигон (Polygon).
В фигуре есть свойство типа Material. Именно оно задает физические свойства фигуре.
Есть такие свойства как: плотность (density), упругость (elasticity), статическое трение (staticFriction), динамическое трение (dynamicFriction), вращательное трение (rollingFriction).
(в классе Material также есть уже заготовлены материалы, такие как дерево, сталь, лед и другие)

Создаем фигуру и добавляем ее в тело (тело может содержать множество разных фигур).
Так как нам надо создать квадратную фигуру то мы должны использовать Polygon. В Polygon первым параметром надо передать массив вершин, вторым материал. Для упрощения нашей жизни в этом классе есть статический метод box, который в качестве параметров принимает ширину и высоту коробки (мы поставим 200х50). Этот метод возвращает массив вершин автоматически выровненные по центру. Материал поставим предустановленный — сталь.

var staticBoxShape:Polygon = new Polygon(Polygon.box(200, 50), Material.steel());

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

Теперь все выше описанное проделываем для динамического тела (только фигуру поменьше сделаем напр. 50х50).

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

Полный код метода createBodies

static public const APP_WIDTH:int = 800;
static public const APP_HEIGHT:int = 600;
 
private function createBodies():void
{
	var staticBody:Body = new Body(BodyType.STATIC);
	var dynamicBody:Body = new Body(BodyType.DYNAMIC);
 
	var staticBoxShape:Polygon = new Polygon(Polygon.box(200, 50), Material.steel());
	staticBody.shapes.add(staticBoxShape);
 
	var dynamicBoxShape:Polygon = new Polygon(Polygon.box(50, 50), Material.wood());
	dynamicBody.shapes.add(dynamicBoxShape);
 
	// Установим тела в нужные нам позиции
	staticBody.position.setxy(APP_WIDTH/2, 450);
	dynamicBody.position.setxy(APP_WIDTH/2, 200);
 
        // Добавим тела в мир
	staticBody.space = _space;
	dynamicBody.space = _space;
}

Все отлично, тела созданы, фигуры созданы, все находится в мире и симуляция шурует во всю. Но на экране ничего не видно!
Потому как мы никакого отображения не добавили. Чтобы самому не придумывать велосипед и не рисовать вручную (или искать какую либо графику) Лука создал класс Debug и два потомка этого класса BitmapDebug и ShapeDebug. Эти классы автоматически нарисуют всю нам нужную графическую отладочною информацию — это и границы самой фигуры и соединения и точки столкновения и все все что нужно будет. Разница между ними небольшая — BitmapDebug использует опкоды алхимии для быстрой отрисовки (отрисовка происходит в битмапы). ShapeDebug использует стандартный набор Graphics, тем самым более медленный (но это не критично для нас, я буду использовать ShapeDebug (BitmapDebug может не работать в FlashPlayer 11.2, так как разрабы заявили что уберут оттуда опкоды)).

Создаем экземпляр класса ShapeDebug, как параметры передаем ширину и высоту нашего приложения (область которую нужно отрисовать), третий параметр цвет бекграунта, он не столь важный, просто используется чтобы Nape лучше подобрал цвета для затенения. Потому добавляем его свойство display в список отображения.
Код выглядит так:

private function initDebugDraw():void
{
	_debug = new ShapeDebug(APP_WIDTH, APP_HEIGHT, 0x555555);
	addChild(_debug.display);
}

В обработчике enter_frame мы каждый кадр очищаем то что нарисовали, рисуем заново и скидываем нарисованное на экран :) (метод draw может принимает для рисования отдельные объекты, но мы ему передаем нарисовать целый мир)
Код обработчика теперь выглядит так:

private function enterFrameHandler(event:Event):void
{
	_space.step(_timeStep, _velocityIterations, _positionIterations);
 
	_debug.clear();
	_debug.draw(_space);
	_debug.flush();
}

(можете поиграться с классом Debug. Большинство свойств по умолчанию отключены)

Еще надо заметить, что уже не надо создавать экземпляр класса Boot (new Boot();) как это было в прошлой версии, также при добавлении фигур в тело не надо вручную обновлять параметры тела, пересчитывать массу и центр масс. Таких методов как body.calcProperties(), body.update(), space.sync(body) больше не существует и это радует, как я говорил в прошлом посту многие рутинные операции автоматизированы и пользователь больше не знает о них.

Ну вот вроде пока и все. Исходник тут. Ниже результат работы.

This movie requires Flash Player 9

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

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

    Как настроить Nape, что-бы тела никогда не засыпали?

  • VirtualMaestro

    Таких настроек нет. Можно отслеживать события тела на «засыпание», а потом будить. Будить можно присвоив любому свойству какое либо значение. Например, можно присвоить малое значение x или y, или применить маленький импульс.

  • g!

    А как а nape 2 при столкновении получить точку столкновения?

    • VirtualMaestro

      У collisionArbiter есть свойство contacts — это список точек контакта. Вот, собственно, с него можно выбрать точку или точки.

      тут есть коммент с кодом http://flashnotes.ru/nape-v2-0-0-reliz.html#comment-738232515