Game Development Community

One-side collision with edge and chain shapes

by Richard Ranft · in Torque 2D Professional · 08/14/2014 (1:46 pm) · 42 replies

This follows from Meredith's description here.

Ok, here's how I did it - and it relies on the relative position of scene object to collision fixture body - in Scene::BeginContact():
// Fetch scene objects.
    SceneObject* pSceneObjectA = static_cast<SceneObject*>(pPhysicsProxyA);
    SceneObject* pSceneObjectB = static_cast<SceneObject*>(pPhysicsProxyB);
    // ---------------------------------
    // inserted this

	if (pSceneObjectA->getOneWayCol() || pSceneObjectB->getOneWayCol())
	{
		// one way collision only applies to edge or chain shapes
		if (pSceneObjectA->getOneWayCol() && (pFixtureA->GetType() == b2Shape::Type::e_chain || pFixtureA->GetType() == b2Shape::Type::e_edge))
		{
			// one or both have one way collision set, so test our directions
			b2Manifold* manifold = pContact->GetManifold();

			Vector2 nPos = Vector2(pSceneObjectA->getPosition().x, pSceneObjectA->getPosition().y);
			nPos.Normalize();
			b2Vec2 vPos = b2Vec2(nPos.x, nPos.y);

			Vector2 nLPos = Vector2(pFixtureA->GetBody()->GetLocalCenter().x, pFixtureA->GetBody()->GetLocalCenter().y);
			nLPos.Normalize();
			b2Vec2 vLPos = b2Vec2(nLPos.x, nLPos.y);

			b2Vec2 nrml = vPos - vLPos;

			Vector2 nVel = Vector2(pSceneObjectB->getLinearVelocity().x, pSceneObjectB->getLinearVelocity().y);
			nVel.Normalize();
			b2Vec2 velB = b2Vec2(nVel.x, nVel.y);

			F32 prod = atan2(nrml.x, nrml.y) - atan2(velB.x, velB.y);
			if (prod < -0.5f)
			{
				pContact->SetEnabled(false);
			}
		}
		else if (pSceneObjectB->getOneWayCol() && (pFixtureB->GetType() == b2Shape::Type::e_chain || pFixtureB->GetType() == b2Shape::Type::e_edge))
		{
			// one or both have one way collision set, so test our directions
			b2Manifold* manifold = pContact->GetManifold();

			Vector2 nPos = Vector2(pSceneObjectB->getPosition().x, pSceneObjectB->getPosition().y);
			nPos.Normalize();
			b2Vec2 vPos = b2Vec2(nPos.x, nPos.y);

			Vector2 nLPos = Vector2(pFixtureB->GetBody()->GetLocalCenter().x, pFixtureB->GetBody()->GetLocalCenter().y);
			nLPos.Normalize();
			b2Vec2 vLPos = b2Vec2(nLPos.x, nLPos.y);

			b2Vec2 nrml = vPos - vLPos;

			Vector2 nVel = Vector2(pSceneObjectA->getLinearVelocity().x, pSceneObjectA->getLinearVelocity().y);
			nVel.Normalize();
			b2Vec2 velA = b2Vec2(nVel.x, nVel.y);

			F32 prod = atan2(nrml.x, nrml.y) - atan2(velA.x, velA.y);
			if (prod > 0.0f)
			{
				pContact->SetEnabled(false);
			}
		}
	}
    // -------------------------
    // Initialize the contact.
    TickContact tickContact;
    tickContact.initialize( pContact, pSceneObjectA, pSceneObjectB, pFixtureA, pFixtureB );

Modified SceneObject with a bool and get/set() for mColOneWay, modified b2Contact with m_reEnable and use that to gate the flag Meredith mentioned above.

In b2Contact::Update() right at the top of the method:
b2Manifold oldManifold = m_manifold;

	// Re-enable this contact.
	if (m_reEnable == true)
		m_flags |= e_enabledFlag;

The one-way part is determined by the positional relation between the fixture and the SceneObject - the "solid side" is the side facing away from the SceneObject - so the local center position of the fixture has to be off-center of the SceneObject. This is sub-optimal, but there seems to be a common theme on the Box2D forums suggesting that there isn't a good way to get the actual edge normal.

To test, I threw this in TruckToy:
// -----------------------------------------------------------------------------

function TruckToy::createOneWayWall( %this, %posX, %posY, %startX, %startY, %endX, %endY )
{
    %obj = new SceneObject();   
    %obj.setBodyType( "static" );
    %obj.setPosition( %posX, %posY );
    %obj.setSize( 0.25, 0.25 );
    %obj.setSceneLayer( TruckToy.ObstacleDomain );
    %obj.setSceneGroup( TruckToy.ObstacleDomain );
    %obj.setCollisionGroups( TruckToy.GroundDomain, TruckToy.ObstacleDomain );
    %obj.setDefaultFriction( TruckToy.ObstacleFriction );
    %obj.createEdgeCollisionShape( %startX, %startY, %endX, %endY );
    %obj.OneWayCollision = true;
    %obj.setAwake( false );
    SandboxScene.add( %obj );

    return %obj;
}
Then called it from ::reset():
%this.createOneWayWall(-92, TruckToy.FloorLevel + 0.75, 1, -3, 1, 3);

I should just make another branch on GitHub....

About the author

I was a soldier, then a computer technician, an electrician, a technical writer, game programmer, and now software test/tools developer. I've been a hobbyist programmer since the age of 13.

Page«First 1 2 3 Next»
#41
12/06/2015 (6:09 am)
I'll merge it in then. Thanks!

T6 has come up a few times in discussion before. I can't promise we'll ever get to it, but at some point we might look at T6 to see if we can gleam anything from it. Right now we've got plenty to do just trying to keep up with our 6 target platforms and all the bugs they generate.
#42
12/06/2015 (8:44 am)
Heh - no kidding. Thanks for the hard work and just keep on keepin' on....
Page«First 1 2 3 Next»