Можно получать информацию о контактах, реализовав методы класса b2Contactl_istener (обработчик контактов). При появлении, существовании на протяжении нескольких итераций или уничтожении контакта будет вызываться соответствующий метод обработчика контактов. Следует помнить, что две фигуры могут иметь несколько точек контакта.
class MyContactListener : public b2ContactListener
{
public:
void Add(const b2ContactPoint* point)
// обрабатываем добавление контакта
void Persist(const b2ContactPoint* point)
// контакт уже существует на протяжении нескольких итераций
void Remove(const b2ContactPoint* point) // обрабатываем удаление контакта
void Result(const b2ContactResult* point)
// обрабатываем контакты после вычисления
импульсов
Внимание
Не следует хранить ссылку на контакт, передаваемый в b2Contactl_istener. Вместо этого необходимо делать глубокое копирование данных контакта в свой собственный буфер. Пример ниже показывает один из способов сделать это.
Разрешение контактов происходить за несколько итераций, поэтому контакт может быть
добавлен или удалён в течении одного шага моделирования. Поэтому ваш код должен уметь обрабатывать такую ситуацию.
О контактах сообщается сразу же как только они создаются, существуют или удаляются. Это происходит перед тем как будет вызван "решальщик" столкновений, поэтому объект b2ContactPoint не содержит вычисленного импульса. Однако, содержит относительную скорость тел в точке контакта, поэтому можно приблизительно оценить импульс. Если вы реализуете метод Result, то будете получать объект b2ContactResult для точек контакта после вызова "решальщика" столкновений. Этот объект будет содержать импульсы, вычисленные для текущей итерации. Аналогично, метод Result может вызваться несколько раз на протяжении одного шага моделирования.
Кажется заманчивым, реализовать игровую логику, которая изменяет игровой мир при обработке контактов. Например, можно получить столкновение, которое применяет повреждение и пытается уничтожить соответствующий игровой объект и физическое тело. Однако Box2D не позволяет изменять физический мир при обработке контактов, так как можно уничтожить объекты, которые Box2D обрабатывает в данный момент, что приведёт к обращению к недействительным указателям.
Правильным методом обработки контактов считается сохранение нужных контактов в буфер и работа сними уже после шага моделирования. Необходимо обрабатывать контакты сразу же после шага моделирования, иначе другой код может изменить физический мир, что приведет к недостоверным данным в буфере контактов. При обработке буфера контактов можно изменять физический мир, но это следует делать осторожно, чтобы указатели, хранящиеся в буфере контактов, не стали недействительными. Тестовый стенд содержит пример обработки контактов с защитой от недействительных указателей.
Данный код из теста CollisionProcessing демонстрирует то, как надо работать с удаленными телами при обработке буфера контактов. Желательно внимательно читать комментарии. В этом коде предполагается, что все точки контактов находятся в буфере m_points.
// Мы собираемся уничтожить несколько тел в зависимости от контактов. // Нам необходимо сохранить тела которые необходимо уничтожить, // так как между ними может содержаться несколько контактов.
const int32 k_maxNuke = 6; b2Body* nuke[k_maxNuke]; int32 nukeCount = 0;
// Пробегаемся по буферу контактов. Уничтожаем тела которые // столкнулись с более тяжелыми телами
for (int32 i = 0; i < m_pointCount; ++i)
{
ContactPoint* point = m_points + i;
b2Body* bodyl = point->shapel->GetBody(); b2Body* body2 = point->shape2->GetBody(); float32 massl = bodyl->GetMass(); float32 mass2 = body2->GetMass();
if (massl > O.Of && mass2 > O.Of) {
if (mass2 > massl)
nuke[nukeCount++] = bodyl; else
nuke[nukeCount++] = body2;
if (nukeCount == k_maxNuke) break;
} }
// Сортируем массив nuke для того чтобы сгруппировать одни и те же тела std::sort(nuke, nuke + nukeCount);
// Уничтожаем тела, пропуская дубликаты
int32 i = 0;
while (i < nukeCount)
{
b2Body* b = nuke[i++];
while (i < nukeCount && nuke[i] == b)
{
++i; }
m_world->DestroyBody(b); }
8.3. Фильтрацияконтактов
Часто в игре требуется, чтобы некоторые тела не сталкивались. Например, может
потребоваться создать дверь, через которую могут проходить только определённые
персонажи. Это называется фильтрацией контактов, так как некоторые столкновения
отфильтровываются.
Box2D позволяет добиться строго определённой фильтрации контактов, для этого
необходимо реализовать класс b2ContactFilter. В этом классе необходимо реализовать
метод ShouldCollide, который принимает два указателя на фигуры. Этот метод
возвращает истину, если фигуры должны взаимодействовать.
Стандартная реализация метода ShouldCollide, использующая b2FilterData, определена в
Глава 6, Фигуры.
bool b2ContactFilter::ShouldCollide(
b2Shape* shapel, b2Shape* shape2 ) {
const b2FilterData& filterl = shapel->GetFilterData();
const b2FilterData& filter2 = shape2->GetFilterData();
if (
filterl.grouplndex == filter2.grouplndex && filterl.grouplndex != 0