Фильтрация коллизий в Box2Web

13 Июня 2014 1584 , ,

АрканоидВ свободное от проектов время, потихонечку делаю арканоид и собираю шишки вместе с опытом. После пробы написания арканоида с описанием физики «ручками», возникло желание попробовать, что предлагают физические движки. Мой выбор пал на Box2dweb, к сожалению именно для Box2web нет документации , но т.к. движок является портом с Actions Script то для разработки мне хватает документации для флеш версии. Описывать возможности и основы работы с box2dweb я не буду, на эту тему есть очень хорошая статья, где все подробно рассказано и достаточно для общего понятия.  Перейду к делу, в нашей игре существует множество разных игровых объектов: стол, шарик, бонусы, блоки и т.д. И все эти тела могут между собой взаимодействовать и сталкиваться при движении. В box2dweb всего есть три типа тел:

  • Статичные — данные тела остаются неподвижными на протяжении всего времени игры, хотя можно и перемещать объект. Обычно используется для создания земли (ground) в играх.
  • Динамические тела — название говорит само за себя, это наши подвижные игровые объекты.
  • Кинематические тела — кинмеатические тела, похожи на статические, с тем отличием, что они не сталкиваются с статическими телами.

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

Я приведу комментиарованый кусок кода из арканоида.

1
2
3
4
5
6
7
8
9
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
//Создаем шарик

var Bullet= function(physics, blockInfo) { //В аргументы принимаем физику мира и информацию о блоке

var body = this.body = new b2BodyDef(); //создаем тело
body.position = new b2Vec2(blockInfo.x, blockInfo.y); // позиция
body.linearVelocity = new b2Vec2(blockInfo.vx || 0, blockInfo.vy || 0); //векторная скорость
body.userData = blockInfo.userData || {}; //доп. инфа о блоке
body.active = true; //тело активно
body.allowSleep = false; //Тело никогда не спит
body.angle = 0; //Угол поворота
body.angularVelocity = 0; //Угловая скорость
body.awake = true; //тело в активном состоянии (не спит)
body.bullet = false;  //режим пули
body.fixedRotation = false;
body.type = b2Body.b2_dynamicBody; //Динамическое тело
body.x = blockInfo.x || 1;
body.y = blockInfo.y || 1;
body = physics.gameWorld.CreateBody(body); //Добавляем тело в мир

var fixture = new b2FixtureDef(); //добавляем крепление

//Создаем новый экземпляр filterData
var fixture_filter = new Box2D.Dynamics.b2FilterData();
fixture_filter.categoryBits = 0x0001; //Приписываем первую категорию
fixture_filter.maskBits = 0x0002; //Маскируем для столкновений 2 категорию
fixture.filter = fixture_filter; //добавляем фильтр к креплению

fixture.density = 5;
fixture.friction = 0.5 || blockInfo.friction;
fixture.restitution = 1;
fixture.w = blockInfo.w || 1;
fixture.h = blockInfo.h || 1;
fixture.shape = new b2PolygonShape();
fixture.shape.SetAsBox(fixture.w, fixture.h);
body.CreateFixture(fixture); //добавляем крепление к телу
return body;
}

Почему не 1, 2 и 3? Потому что Box2D категории (и маски) являются битовыми полями (кодированные как шорты, таким образом, на 16 бит)  Это означает, что возможные категории могут быть только степенями 2 (в десятичной: 1, 2, 4, 8, 16, 32, 64, 128 …, или в шестнадцатеричной: 0 × 1, 0 × 2, 0 × 4, 0 × 8 , 0 × 10, 0 × 20, 0 × 40, 0 × 80 …). 16 бит означает, что существует всего 16 возможных категорий , от 0 × 0001 до 0 × 8000.

Принцип работы маски

1
2
3
4
for each object o1 in the world
for each object o2 in the world
//если есть коллизия
isCollisionEnabled = (o1.filter.maskBits & o2.filter.collisionBits) ≠ 0

Аналогичный код будет и для создания бонуса, с тем отличием, что в параметре maskBits мы указываем = 0x0001 т.е. группу к которой принадлежит шар маскируем для столкновений

1
fixture_filter.maskBits = 0x0001; //Маскируем для столкновений 1 категорию

Все, теперь наш шар и бонус не будут между собой сталкиваться, но давайте до конца рассмотрим возможности фильтрации коллизий в box2web. Второй способ который мы можем применить, это использовать свойство groupIndex при описании filter_fixture. К примеру мы не хотим, чтобы все шары сталкивались между собой в игре, мы можем использовать отрицательное значение -1, чтобы отключить коллизи между объектами одинакового типа. Фильтрацию по группам можно делать с помощью маски и групп, но использовать фильтрацию по группам коллизий стоит только при необходимости отключить столкновения между одинаковыми объектами.

Поиграть в тестовую версию арканоида можно тут 

Подписывайтесь на обновления

Читайте RSS ленту

Комментарии

Добавить комментарий