Фильтрация коллизий в Box2Web
В свободное от проектов время, потихонечку делаю арканоид и собираю шишки вместе с опытом. После пробы написания арканоида с описанием физики «ручками», возникло желание попробовать, что предлагают физические движки. Мой выбор пал на 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, чтобы отключить коллизи между объектами одинакового типа. Фильтрацию по группам можно делать с помощью маски и групп, но использовать фильтрацию по группам коллизий стоит только при необходимости отключить столкновения между одинаковыми объектами.
Поиграть в тестовую версию арканоида можно тут
Комментарии