So I was looking for a way to get certain dynamic bodies to push others out of the way but not the other way around. Essentially like the way kinematic bodies work, but I want it to be dynamic. So let me explain why I want to do this. I am working on a 2d platform puzzle game where the character can move around in a manner similar to the way Mario moves. Now I have some boulders (big round heavy rocks) which the character should not be able to move just by pushing into them. He has to move them in other ways (e.g. set off an explosion next to it, etc..). So my character is a dynamic body and so is the boulder. I could almost achieve what I want by setting the mass of the boulder much higher than the character. But not quite because the character can still move it if he is insistent enough. I want it to be truly immovable from the character's point of view. So I don't think Box2D supports this out of the box, so I've made some changes to support it. I've hooked into the PreSolve callback and done something like this:
Code:
void CGameLayer::PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
{
b2Fixture* pFixtureA = contact->GetFixtureA();
b2Fixture* pFixtureB = contact->GetFixtureB();
b2Body* pBodyA = pFixtureA->GetBody();
b2Body* pBodyB = pFixtureB->GetBody();
if (pBodyA->GetType() == b2_dynamicBody && pBodyB->GetType() == b2_dynamicBody)
{
CAnimatedObject* pAnimatedObjectA = (CAnimatedObject*)pBodyA->GetUserData();
CAnimatedObject* pAnimatedObjectB = (CAnimatedObject*)pBodyB->GetUserData();
if (pAnimatedObjectA->GetObjectType() == eOT_Character && pAnimatedObjectB->GetObjectType() == eOT_Boulder)
{
contact->SetBodyBKinematic(true);
}
else if(pAnimatedObjectA->GetObjectType() == eOT_Boulder && pAnimatedObjectB->GetObjectType() == eOT_Character)
{
contact->SetBodyAKinematic(true);
}
}
}
This works similar to disabling contacts, note the "SetBodyAKinematic" & "SetBodyBKinematic" functions are new and added by myself. It effectively makes one of the bodies have infinite mass as far as this contact is concerned. I've attached the Box2D patch below which implements these new functions. I thought they might be useful for others and may be worth trying to get into the official Box2D code (not sure what the procedure for submitting patches is).
Code:
diff --git "a/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF66D.tmp\\b2ContactSolver-8088ef-left.h" "b/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF66C.tmp\\b2ContactSolver-cb91b7-right.h"
index 5064702..1415b6f 100644
--- "a/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF66D.tmp\\b2ContactSolver-8088ef-left.h"
+++ "b/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF66C.tmp\\b2ContactSolver-cb91b7-right.h"
@@ -49,6 +49,10 @@ struct b2ContactConstraint
b2Mat22 K;
b2Body* bodyA;
b2Body* bodyB;
+ float32 invMassA;
+ float32 invIA;
+ float32 invMassB;
+ float32 invIB;;
b2Manifold::Type type;
float32 radius;
float32 friction;
diff --git "a/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF65B.tmp\\b2ContactSolver-8088ef-left.cpp" "b/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF65A.tmp\\b2ContactSolver-cb91b7-right.cpp"
index 7928b62..143379b 100644
--- "a/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF65B.tmp\\b2ContactSolver-8088ef-left.cpp"
+++ "b/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF65A.tmp\\b2ContactSolver-cb91b7-right.cpp"
@@ -63,6 +63,10 @@ b2ContactSolver::b2ContactSolver(b2Contact** contacts, int32 contactCount,
b2ContactConstraint* cc = m_constraints + i;
cc->bodyA = bodyA;
cc->bodyB = bodyB;
+ cc->invMassA = bodyA->m_invMass;
+ cc->invIA = bodyA->m_invI;
+ cc->invMassB = bodyB->m_invMass;
+ cc->invIB = bodyB->m_invI;
cc->manifold = manifold;
cc->normal = worldManifold.normal;
cc->pointCount = manifold->pointCount;
@@ -73,6 +77,17 @@ b2ContactSolver::b2ContactSolver(b2Contact** contacts, int32 contactCount,
cc->radius = radiusA + radiusB;
cc->type = manifold->type;
+ if (contact->m_flags & b2Contact::e_bodyAKinematic)
+ {
+ cc->invMassA = 0.0f;
+ cc->invIA = 0.0f;
+ }
+ if (contact->m_flags & b2Contact::e_bodyBKinematic)
+ {
+ cc->invMassB = 0.0f;
+ cc->invIB = 0.0f;
+ }
+
for (int32 j = 0; j < cc->pointCount; ++j)
{
b2ManifoldPoint* cp = manifold->points + j;
@@ -91,7 +106,7 @@ b2ContactSolver::b2ContactSolver(b2Contact** contacts, int32 contactCount,
rnA *= rnA;
rnB *= rnB;
- float32 kNormal = bodyA->m_invMass + bodyB->m_invMass + bodyA->m_invI * rnA + bodyB->m_invI * rnB;
+ float32 kNormal = cc->invMassA + cc->invMassB + cc->invIA * rnA + cc->invIB * rnB;
b2Assert(kNormal > b2_epsilon);
ccp->normalMass = 1.0f / kNormal;
@@ -103,7 +118,7 @@ b2ContactSolver::b2ContactSolver(b2Contact** contacts, int32 contactCount,
rtA *= rtA;
rtB *= rtB;
- float32 kTangent = bodyA->m_invMass + bodyB->m_invMass + bodyA->m_invI * rtA + bodyB->m_invI * rtB;
+ float32 kTangent = cc->invMassA + cc->invMassB + cc->invIA * rtA + cc->invIB * rtB;
b2Assert(kTangent > b2_epsilon);
ccp->tangentMass = 1.0f / kTangent;
@@ -123,10 +138,10 @@ b2ContactSolver::b2ContactSolver(b2Contact** contacts, int32 contactCount,
b2ContactConstraintPoint* ccp1 = cc->points + 0;
b2ContactConstraintPoint* ccp2 = cc->points + 1;
- float32 invMassA = bodyA->m_invMass;
- float32 invIA = bodyA->m_invI;
- float32 invMassB = bodyB->m_invMass;
- float32 invIB = bodyB->m_invI;
+ float32 invMassA = cc->invMassA;
+ float32 invIA = cc->invIA;
+ float32 invMassB = cc->invMassB;
+ float32 invIB = cc->invIB;
float32 rn1A = b2Cross(ccp1->rA, cc->normal);
float32 rn1B = b2Cross(ccp1->rB, cc->normal);
@@ -170,10 +185,10 @@ void b2ContactSolver::WarmStart()
b2Body* bodyA = c->bodyA;
b2Body* bodyB = c->bodyB;
- float32 invMassA = bodyA->m_invMass;
- float32 invIA = bodyA->m_invI;
- float32 invMassB = bodyB->m_invMass;
- float32 invIB = bodyB->m_invI;
+ float32 invMassA = c->invMassA;
+ float32 invIA = c->invIA;
+ float32 invMassB = c->invMassB;
+ float32 invIB = c->invIB;
b2Vec2 normal = c->normal;
b2Vec2 tangent = b2Cross(normal, 1.0f);
@@ -200,10 +215,10 @@ void b2ContactSolver::SolveVelocityConstraints()
float32 wB = bodyB->m_angularVelocity;
b2Vec2 vA = bodyA->m_linearVelocity;
b2Vec2 vB = bodyB->m_linearVelocity;
- float32 invMassA = bodyA->m_invMass;
- float32 invIA = bodyA->m_invI;
- float32 invMassB = bodyB->m_invMass;
- float32 invIB = bodyB->m_invI;
+ float32 invMassA = c->invMassA;
+ float32 invIA = c->invIA;
+ float32 invMassB = c->invMassB;
+ float32 invIB = c->invIB;
b2Vec2 normal = c->normal;
b2Vec2 tangent = b2Cross(normal, 1.0f);
float32 friction = c->friction;
@@ -573,10 +588,10 @@ bool b2ContactSolver::SolvePositionConstraints(float32 baumgarte)
b2Body* bodyA = c->bodyA;
b2Body* bodyB = c->bodyB;
- float32 invMassA = bodyA->m_mass * bodyA->m_invMass;
- float32 invIA = bodyA->m_mass * bodyA->m_invI;
- float32 invMassB = bodyB->m_mass * bodyB->m_invMass;
- float32 invIB = bodyB->m_mass * bodyB->m_invI;
+ float32 invMassA = bodyA->m_mass * c->invMassA;
+ float32 invIA = bodyA->m_mass * c->invIA;
+ float32 invMassB = bodyB->m_mass * c->invMassB;
+ float32 invIB = bodyB->m_mass * c->invIB;
// Solve normal constraints
for (int32 j = 0; j < c->pointCount; ++j)
diff --git "a/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF63A.tmp\\b2Contact-8088ef-left.h" "b/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF639.tmp\\b2Contact-cb91b7-right.h"
index 14e6120..e26f685 100644
--- "a/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF63A.tmp\\b2Contact-8088ef-left.h"
+++ "b/C:\\Users\\Martin\\AppData\\Local\\Temp\\b2CF639.tmp\\b2Contact-cb91b7-right.h"
@@ -82,6 +82,12 @@ public:
/// Has this contact been disabled?
bool IsEnabled() const;
+ /// Set one of the bodies in this contact to be kinematic pushing
+ /// the other out of the way. This can be used inside the pre-solve
+ /// contact listener.
+ void SetBodyAKinematic(bool flag);
+ void SetBodyBKinematic(bool flag);
+
/// Get the next contact in the world's contact list.
b2Contact* GetNext();
const b2Contact* GetNext() const;
@@ -122,6 +128,12 @@ protected:
// This bullet contact had a TOI event
e_bulletHitFlag = 0x0010,
+ // BodyA can be made kinematic for this contact
+ e_bodyAKinematic = 0x0020,
+
+ // BodyB can be made kinematic for this contact
+ e_bodyBKinematic = 0x0040,
+
};
/// Flag this contact for filtering. Filtering will occur the next time step.
@@ -204,6 +216,30 @@ inline bool b2Contact::IsTouching() const
return (m_flags & e_touchingFlag) == e_touchingFlag;
}
+inline void b2Contact::SetBodyAKinematic(bool flag)
+{
+ if (flag)
+ {
+ m_flags |= e_bodyAKinematic;
+ }
+ else
+ {
+ m_flags &= ~e_bodyAKinematic;
+ }
+}
+
+inline void b2Contact::SetBodyBKinematic(bool flag)
+{
+ if (flag)
+ {
+ m_flags |= e_bodyBKinematic;
+ }
+ else
+ {
+ m_flags &= ~e_bodyBKinematic;
+ }
+}
+
inline b2Contact* b2Contact::GetNext()
{
return m_next;
@@ -239,4 +275,4 @@ inline void b2Contact::FlagForFiltering()
m_flags |= e_filterFlag;
}
-#endif
+#endif
\ No newline at end of file