
Новый nape. Обработка событий.
Новый nape:
1. Новый nape. Hello world
2. Новый nape. Обработка событий. (Его вы сейчас читаете)
3. Новый nape. Testbed. И немного теории
4. Новый nape. Создание тел, материалы, фильтрация столкновений
5. Новый nape. Соединение тел
6. Новый nape. Приложение сил к телам и raycast
В новом nape предусмотрена обработка самых разных событий:
1. Засыпание/просыпание тела;
2. Засыпание/просыпание соединения (ограничения взаимного движения, joint) тел;
3. Превышение допустимой нагрузки и разрыв соединения тел;
4. Начало/конец/продолжение контакта между телами.
На сегодняшний день впрочем не реализована большая часть этих возможностей.
Засыпание тела
Но, например, засыпание тела реализовано, а поскольку обработка всех событий будет сделана единообразно, рассмотрим как отреагировать на это событие. Самый простой способ это сделать такой:
Так тело будет сразу после засыпания снова падать сверху на землю.
Другой вариант добавления слушателя:
Оба подхода к добавлению в мир применимы не только к слушателям, но и к телам, и к многим другим объектам.
В nape есть понятия типа тела, CbType. Обработчики событий в nape задаются по типам. Например можно задать телу тип «главный герой» или «бонус», и назначить обработчик для засыпания главного героя или столкновения главного героя с бонусом.
Добавим еще одно тело и зададим ему тип.
Немного поменяем код создания обработчика события:
Раньше в качестве типа было указано значение CbType.DEFAULT — это тип тела присваиваемый по умолчанию. Теперь обычное тело заснет, а наше новое тело при попытке заснуть будет телепортировать наверх.
Обработка столкновений
Но конечно самым важным и интересным для нас является обработка столкновений. К сожалению автор пока еще не реализовал эту обработку через описанный механизм. Но у нас есть несколько альтернатив:
1. Часто нам не нужно оповещение, что произошло столкновение, достаточно знать что определенное тело взаимодействует с каким-то другим телом. У объекта Body в nape есть функция normalImpulse, она возвращает значение импульса, полученного телом в результате столкновений в течение последнего вызова space.step() (точнее только ту часть импульса, которая пришлась вдоль линии соединяющей центры масс столкнувшихся тел.) Можно получить либо сумму всех импульсов, либо импульс от столкновения с конкретным телом. Также можно получить импульсы только от новых столкновений. В нашем примере мы можем вызывать каждый шаг такой код:
И когда тело лежит на земле будем получать вектор направленный вверх.
2. Если же нам нужно узнать с какими телами соприкасается определенное тело, то можно воспользоваться свойством arbiters. Например так:
3. Ну и наконец автор nape сделал для нас некий класс, который, пока не сделан основной механизм, позволяет все-таки получить именно обработчики событий столкновений. Код этого класса можно взять здесь. Вот он:
Пользоваться им легко и просто. Добавим еще тип тел groundType для земли и напишем следующий код:
Очень удобно, что тела приходят в функцию-обработчик уже в нужном порядке.
Полный исходный код получившегося примера:
1. Новый nape. Hello world
2. Новый nape. Обработка событий. (Его вы сейчас читаете)
3. Новый nape. Testbed. И немного теории
4. Новый nape. Создание тел, материалы, фильтрация столкновений
5. Новый nape. Соединение тел
6. Новый nape. Приложение сил к телам и raycast
В новом nape предусмотрена обработка самых разных событий:
1. Засыпание/просыпание тела;
2. Засыпание/просыпание соединения (ограничения взаимного движения, joint) тел;
3. Превышение допустимой нагрузки и разрыв соединения тел;
4. Начало/конец/продолжение контакта между телами.
На сегодняшний день впрочем не реализована большая часть этих возможностей.
Засыпание тела
Но, например, засыпание тела реализовано, а поскольку обработка всех событий будет сделана единообразно, рассмотрим как отреагировать на это событие. Самый простой способ это сделать такой:
space.listeners.add(new BodyListener(CbEvent.SLEEP, CbType.DEFAULT, bodySleep));
private function bodySleep(body:Body):void { body.position.y = 0; }
Так тело будет сразу после засыпания снова падать сверху на землю.
Другой вариант добавления слушателя:
var l:BodyListener = new BodyListener(CbEvent.SLEEP, CbType.DEFAULT, bodySleep);
l.space = space;
Оба подхода к добавлению в мир применимы не только к слушателям, но и к телам, и к многим другим объектам.
В nape есть понятия типа тела, CbType. Обработчики событий в nape задаются по типам. Например можно задать телу тип «главный герой» или «бонус», и назначить обработчик для засыпания главного героя или столкновения главного героя с бонусом.
Добавим еще одно тело и зададим ему тип.
public var someType:CbType = new CbType(); // Новый тип тела
...
var body2:Body = new Body(BodyType.DYNAMIC, new Vec2(200, 300));
body2.cbType = someType; // Зададим тип тела
body2.shapes.add(new Circle(50, null, Material.rubber()));
body2.align();
body2.space = space;
Немного поменяем код создания обработчика события:
space.listeners.add(new BodyListener(CbEvent.SLEEP, someType, bodySleep ));
Раньше в качестве типа было указано значение CbType.DEFAULT — это тип тела присваиваемый по умолчанию. Теперь обычное тело заснет, а наше новое тело при попытке заснуть будет телепортировать наверх.
Обработка столкновений
Но конечно самым важным и интересным для нас является обработка столкновений. К сожалению автор пока еще не реализовал эту обработку через описанный механизм. Но у нас есть несколько альтернатив:
1. Часто нам не нужно оповещение, что произошло столкновение, достаточно знать что определенное тело взаимодействует с каким-то другим телом. У объекта Body в nape есть функция normalImpulse, она возвращает значение импульса, полученного телом в результате столкновений в течение последнего вызова space.step() (точнее только ту часть импульса, которая пришлась вдоль линии соединяющей центры масс столкнувшихся тел.) Можно получить либо сумму всех импульсов, либо импульс от столкновения с конкретным телом. Также можно получить импульсы только от новых столкновений. В нашем примере мы можем вызывать каждый шаг такой код:
trace(body.normalImpulse(ground));
И когда тело лежит на земле будем получать вектор направленный вверх.
2. Если же нам нужно узнать с какими телами соприкасается определенное тело, то можно воспользоваться свойством arbiters. Например так:
for (var i:int = 0; i < body.arbiters.length; ++i)
{
var arb:Arbiter = body.arbiters.at(i);
if (arb.isCollisionArbiter())
trace("Collides with " + (arb.body1 == body ? arb.body2 : arb.body1));
}
3. Ну и наконец автор nape сделал для нас некий класс, который, пока не сделан основной механизм, позволяет все-таки получить именно обработчики событий столкновений. Код этого класса можно взять здесь. Вот он:
package {
import nape.space.Space;
import nape.phys.Body;
import nape.callbacks.CbType;
import nape.dynamics.Arbiter;
public class Collisions {
private var interesting:Object;
private function pairId(i1:int, i2:int):int {
return i1<i2 ? (i1<<16)|i2 : (i2<<16)|i1;
}
private var interacting:Object;
public function Collisions() { interesting = {}; interacting = {}; }
public function addPair(c1:CbType, c2:CbType, begin:Function, end:Function):Boolean {
var id:int = pairId(c1.id,c2.id);
if(interesting[id]!=null) return false;
interesting[id] = {begin:begin, end:end, c1:c1, c2:c2, val:false, stamp:-1};
return true;
}
public function remPair(c1:CbType,c2:CbType):Boolean {
var id:int = pairId(c1.id,c2.id);
var ret:Boolean = interesting[id]!=null;
if(ret) interesting[id] = null;
return ret;
}
public function handleCallbacks(space:Space):void {
//handle begin
var arbs:int = space.arbiters.length;
for(var i:int = 0; i<arbs; i++) {
var arb:Arbiter = space.arbiters.at(i);
if(!arb.isCollisionArbiter()) continue;
var id:int = pairId(arb.body1.cbType.id,arb.body2.cbType.id);
var obj:Object = interesting[id];
if(obj==null) continue;
var id2:int = pairId(arb.body1.id,arb.body2.id);
var tin:Object = interacting[id2];
if(tin!=null) { tin.stamp = space.timeStamp; continue; }
var b1:Body, b2:Body;
if(obj.c1 == arb.body1.cbType) {
b1 = arb.body1; b2 = arb.body2;
}else {
b1 = arb.body2; b2 = arb.body1;
}
if(obj.begin!=null)
obj.begin(b1,b2);
interacting[id2] = {obj:obj, b1:b1, b2:b2, stamp:space.timeStamp};
}
//handle end
for (var val:* in interacting) {
tin = interacting[val];
if(tin.stamp!=space.timeStamp) {
//ensure it's not due to sleeping
if(!((!tin.b1.isDynamic() || tin.b1.isSleeping)
&& (!tin.b2.isDynamic() || tin.b2.isSleeping))) {
if (tin.obj.end != null) tin.obj.end(tin.b1, tin.b2);
interacting[val] = null;
delete interacting[val];
}
}
}
}
}
}
Пользоваться им легко и просто. Добавим еще тип тел groundType для земли и напишем следующий код:
public var collisions:Collisions = new Collisions();
...
collisions.addPair(groundType, someType, collisionBegin, collisionEnd);
...
collisions.handleCallbacks(space);
...
private function collisionBegin(b1:Body, b2:Body):void { trace("begin " + b1 + b2); }
private function collisionEnd(b1:Body, b2:Body):void { trace("end " + b1 + b2); }
Очень удобно, что тела приходят в функцию-обработчик уже в нужном порядке.
Полный исходный код получившегося примера:
package
{
import flash.Boot;
import flash.display.*;
import flash.events.*;
import nape.dynamics.*;
import nape.geom.*;
import nape.phys.*;
import nape.shape.*;
import nape.space.*;
import nape.util.*;
import nape.callbacks.*;
public class Main extends Sprite
{
public var space:Space = new Space(new Vec2(0, 100)); // Мир
public var debug:Debug = new ShapeDebug(800, 600, 0xFFFFFF); // Отладочный вывод
public var someType:CbType = new CbType(); // Новый тип тела
public var groundType:CbType = new CbType(); // Тип тела земли
public var body:Body; // Тела
public var body2:Body;
public var ground:Body;
public var collisions:Collisions = new Collisions(); // Класс для обработки событий столкновений
public function Main():void
{
new Boot(); // Обязательно нужно для работы nape
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
body = new Body(BodyType.DYNAMIC, new Vec2(400, 300)); // Новое тело
body.shapes.add(new Circle(50, null, Material.wood())); // Добавим фигуру
body.align(); // Нужно совместить центр фигуры и ее центр масс, иначе будут глюки
body.space = space; // Добавим тело в мир
body2 = new Body(BodyType.DYNAMIC, new Vec2(200, 300));
body2.cbType = someType; // Зададим тип тела
body2.shapes.add(new Circle(50, null, Material.rubber()));
body2.align();
body2.space = space;
ground = new Body(BodyType.STATIC); // Земля
ground.cbType = groundType;
ground.shapes.add(new Polygon(Polygon.rect(0, 580, 800, 100)));
ground.space = space;
// Добавим типы тел столкновения которых мы хотим отслеживать
collisions.addPair(groundType, someType, collisionBegin, collisionEnd);
// Добавим обработку события засыпания тела
space.listeners.add(new BodyListener(CbEvent.SLEEP, someType, bodySleep ));
addChild(debug.display);
addEventListener(Event.ENTER_FRAME, enterFrame);
}
private function bodySleep(body:Body):void
{
body.position.y = 0;
}
private function collisionBegin(b1:Body, b2:Body):void
{
trace("begin " + b1 + b2);
}
private function collisionEnd(b1:Body, b2:Body):void
{
trace("end " + b1 + b2);
}
private function enterFrame(e:Event):void
{
space.step(1 / 30.0);
// Найдем какой импульс приложили окружающие предметы к телу body.
//trace(body.normalImpulse(ground));
// Найдем все тела, с которыми соприкасается тело body
for (var i:int = 0; i < body.arbiters.length; ++i)
{
var arb:Arbiter = body.arbiters.at(i);
if (arb.isCollisionArbiter())
trace("Collides with " + (arb.body1 == body ? arb.body2 : arb.body1));
}
// Обработаем столкновения
collisions.handleCallbacks(space);
// Вывод на экран
debug.clear();
debug.draw(space);
}
}
}
- +18
- romamik
Комментарии (6)
конечно хочется чтобы Лука поскорее дописал все события)