Соединения (Joints) в Box2D

В Box2D есть специальные объекты (joints), которые используются для соединения двух тел. С помощью их можно создавать такие объекты как поршни, веревки, колеса, транспортные средства, цепи и т.п. Благодаря joint-ам можно создавать более привлекательные и интересные физические сцены, уровни для игр и т.п.

Давайте рассмотрим все виды соединений, а затем перейдём к конкретным примерам.

Виды соединений

Вот список всех доступных видов соединений в версии Box2DFlash 2.1a:

  • Weld — крепкое соединение, похоже на то, как если бы тела склеили между собой в какой-либо точке;
  • Revolute — вращательное соединение, как общий штифт для обоих тел в одной точке и вокруг этой точки они могут вращаться;
  • Distance — точка для обоих тел, которая будет оставаться на фиксированном расстоянии от обоих тел;
  • Prismatic — вращение мужду телами отсутствует и они могут скользить между собой по определённой оси;
  • Line — это комбинация revolute и prismatic соединений, очень полезно для крепления колеса с одновременной амортизацией подвески;
  • Pulley — два тела соединяются как верёвкой которая проходит через две точки в мире и со стороны это похоже на весы, т.к. то тело, которое тяжелее будет перетягивать верёвку — опускаться, а другое тело подниматься. как-то так, лучше потом взглянуть на пример);
  • Friction — уменьшает трение движения между двумя телами;
  • Gear — контролирует два других соединения (revolute или prismatic) так что движения одного тела влияют на движение второго;
  • Mouse — тянет тело за точку соединения в определённые мировые координаты.

Теперь рассмотрим общие понятия для соединений.

Создание соединений

Все соединения создаются аналогичным образом используя тела (body) и арматуру (fixtures), затем нужно будет настроить параметры, которые необходимы для создаваемого соединения. Рассмотрим обобщённый пример создания соединения (ххх будет заменен одним из совместных имен выше):

// создаем настройки соединения
var jDef: b2XXXJointDef = new b2XXXJointDef();

// здесь настраиваем специальные параметры соединения, с помощью полей класса jDef.
// например jDef.bodyA = _world.GetGroundBody();

// создаем соединение в мире, присваивая его переменной joint
var joint: b2XXXJoint = _world.CreateJoint(jDef) as b2XXXJoint;

Создание соединения функцией CreateJoint() аналогично уже знакомым нам созданиям тел, когда мы использовали функцию CreateBody() которая возвращает ссылку на b2Body класс, а так же CreateFixture() возвращает b2Fixture.

Т.к. разные соединения зачастую имеют совершенно различное поведение, то они были реализованы отдельными классами, такими как b2RevoluteJoint, b2DistanceJoint и т.д. Класс b2Joint является родителем всех классов соединений, но это абстрактный класс который никогда не используется непосредственно. Таким образом, при создании соединений, когда функция CreateJoint() возвращает нам тип b2Joint, на самом деле это указатель на конкретный класс соединения. Вот почему возвращаемое значение из CreateJoint() было приведено к типу b2XXXJoint в примере выше. Конечно, если вам не нужно держать ссылку на соединение, то можно просто вызвать CreateJoint() для создания его в мире без сохранения возвращаемого значения.

Примеры

Для примера рассмотрим MouseJoint которое уже было реализовано ранее в статье «Перемещение объектов мышкой в Box2D».

Создание соединения заключено в обработчике события onMouseDown() нажатия мыши:

private function onMouseDown(e:MouseEvent):void 
		{
			// если соединение уже есть, то уничтожаем его
			if (_mJoint)
				destroyMouseJoint();
			// получаем тело под курсором
			var body:b2Body = GetBodyAtMouse();
			if (body) {
				var md:b2MouseJointDef = new b2MouseJointDef(); // создаем настройки соединения
				md.bodyA = _world.GetGroundBody(); // один конец крепим к миру
				md.bodyB = body; // другой к телу
				md.target.Set(mouseX * MPP, mouseY * MPP); // соединение создается от курсора мыши
				md.collideConnected = true;
				md.maxForce = 3000; // макс. сила которая может быть приложена к телу
				_mJoint = _world.CreateJoint(md) as b2MouseJoint; // создаем соединение
				body.SetAwake(true); // будим тело
			}
		}

Данное соединение притягивает тело с определённой силой к точке в мировых координатах target.

Для данного соединения характерны такие свойства как:

  • target — точка в мировых координатах к которой притягивается тело B;
  • maxForce — максимальная сила с которой будет притягиваться тело B;
  • frequency и dampingRatio — настройка этих параметров используется для создания эффекта пружины для соединения.

Подробные и разнообразные примеры других соединений я буду постепенно освещать в следующих статьях.

За основу кода к последующим статьям о joint-ах я взял код из той же статьи «Перемещение объектов мышкой в Box2D» и немного улучшил его:

  • добавил 2-е функции для создания физического круга и прямоугольника, параметры которым можно указывать не в метрах, а в пикселях для полноценного удобства;
  • добавил профайлер hi-res-stats от mrdoob, отображение которого можно скрывать и показывать на клавишу «P»;
  • сделал функцию создания физических стен с возможностью указать какие стены создавать, а создание адаптивным под размеры сцены;
  • возможность пересоздать сцену на клавишу «R».

Структура всех проектов будет практически одинаковая и будет выглядеть так как на рисунке 1.

box2d project tree
Рис. 1. Базовое дерево проекта.

Советую читать следующие статьи в определённом порядке, т.к. сложность реализации соединений идёт в них по нарастающей:

Читать про Weld Joint

Читать про Revolute Joint