Collision resolution between multiple AABB objects

Started by
4 comments, last by EWClay 11 years, 2 months ago

Greetings everyone,

I'm working on implementing my own *very* basic 2D collision engine for AABB objects to use in my 2D platformer I'm trying to make.

I've managed to get the following working:

  • Collision detection
  • Collision between one movable sprite and one nonmovable sprite (mass == 0)
  • Collision between exactly two movable sprites

What I'm having trouble with is resolving multiple collisions at a time. For instance, suppose that the player is pushing another object into a third object along the x axis. In this case, the second object should push up against the third object with the player pushing against the middle object. Instead, all of my objects are penetrating each other and the resolution isn't happening. In the attached image, the player (rectangle 1) is pushing another object (rectangle 2) into a third object (rectangle 3).

Here are the relevant portions of my code. Again, I've never implemented any kind of collision detection before so this is probably hideous and I'm sure that I am doing an unfathomable number of things wrong. ANY critique or suggestions would be appreciated but I'd specifically like to address the above problem.

If anyone needs additional information or code then just let me know and I'd be happy to upload it.


void CollisionManager::HandleSpriteCollisions(const std::vector<Sprite*>& sprites)
{
	for(size_t k1 = 0; k1 < sprites.size(); ++k1)
	{
		PhysicsComponent* physics = sprites[k1]->GetFirstComponentOfType<PhysicsComponent>();
		if(physics)
			physics->m_velocityChangesToApply = Vector2D::ZeroVector();

		for(size_t k2 = k1 + 1; k2 < sprites.size(); ++k2)
		{
			if(k1 == k2)
			        continue;

			bool spritesOverlap = DoSpritesOverlap(sprites[k1], sprites[k2]);
			if(spritesOverlap)
				HandleSpriteCollision(sprites[k1], sprites[k2]);
		}
	}

	for(size_t k1 = 0; k1 < sprites.size(); ++k1)
	{
		PhysicsComponent* physics = sprites[k1]->GetFirstComponentOfType<PhysicsComponent>();
		if(physics)
			physics->m_position += physics->m_velocityChangesToApply;
	}
}

void CollisionManager::HandleSpriteCollision( Sprite* spriteA, Sprite* spriteB )
{
	Vector2D overlapAxis;
	float overlapAmount = CalculateOverlapAmount(spriteA, spriteB, overlapAxis);
	if(overlapAmount == 0)
		return;

	PhysicsComponent* aPhysicsComponent = spriteA->GetFirstComponentOfType<PhysicsComponent>();
	PhysicsComponent* bPhysicsComponent = spriteB->GetFirstComponentOfType<PhysicsComponent>();
	if(!aPhysicsComponent || !bPhysicsComponent)
		return;

	float spriteAMass = aPhysicsComponent->m_mass;
	float spriteBMass = bPhysicsComponent->m_mass;
	float totalMass = spriteAMass + spriteBMass;
	float proportionalMassA = spriteAMass / totalMass;
	float proportionalMassB = spriteBMass / totalMass;

	float penetrationDepth = overlapAmount + 1;

	Vector2D spriteDirectionVector = (bPhysicsComponent->m_collisionShape->position - aPhysicsComponent->m_collisionShape->position).Normalize();
	Vector2D hitNormalA = overlapAxis * penetrationDepth;
	Vector2D hitNormalB = hitNormalA * -1.f;

	Vector2D resolutionVector;

	if(aPhysicsComponent->m_mass == 0 && bPhysicsComponent->m_mass == 0)
		return;

	if(aPhysicsComponent->m_mass > 0 && bPhysicsComponent->m_mass > 0)
	{
		Vector2D impulse = hitNormalA;
		float restitutionCoefficient = -0.5f;

		impulse *= (-(1.0f + restitutionCoefficient));
		impulse /= (1/spriteAMass + 1/spriteBMass);

		aPhysicsComponent->m_velocityChangesToApply += (hitNormalA);
		bPhysicsComponent->m_velocityChangesToApply += (hitNormalB);
	}
	else if(aPhysicsComponent->m_mass == 0)
	{
		bPhysicsComponent->m_velocityChangesToApply += ( hitNormalB );
	}
	else if(bPhysicsComponent->m_mass == 0)
	{
		aPhysicsComponent->m_velocityChangesToApply += ( hitNormalA );
	}
}

Advertisement

if(aPhysicsComponent->m_mass > 0 && bPhysicsComponent->m_mass > 0)
{
Vector2D impulse = hitNormalA;
float restitutionCoefficient = -0.5f;

impulse *= (-(1.0f + restitutionCoefficient));
impulse /= (1/spriteAMass + 1/spriteBMass);

aPhysicsComponent->m_velocityChangesToApply += (hitNormalA);
bPhysicsComponent->m_velocityChangesToApply += (hitNormalB);
}

Would this work?

aPhysicsComponent->m_velocityChangesToApply += (hitNormalA) * impulse / spriteAMass;
bPhysicsComponent->m_velocityChangesToApply += (hitNormalB) * impulse / spriteBMass;

if(aPhysicsComponent->m_mass > 0 && bPhysicsComponent->m_mass > 0)
{
Vector2D impulse = hitNormalA;
float restitutionCoefficient = -0.5f;

impulse *= (-(1.0f + restitutionCoefficient));
impulse /= (1/spriteAMass + 1/spriteBMass);

aPhysicsComponent->m_velocityChangesToApply += (hitNormalA);
bPhysicsComponent->m_velocityChangesToApply += (hitNormalB);
}

Would this work?

aPhysicsComponent->m_velocityChangesToApply += (hitNormalA) * impulse / spriteAMass;
bPhysicsComponent->m_velocityChangesToApply += (hitNormalB) * impulse / spriteBMass;

Well both hitNormalA/B and impulse are both vectors. Ideally impulse should be some force and direction applied on collision to both objects for a bounce effect, but I'm not even using that right now. currently I'm just trying to move the objects apart by the penetration amount (hitNormalA = hitNormal * penetrationDepth) to separate them.

What's happening is that my method breaks when any one object is colliding with more than one other object. I think that the middle object is being acted upon by both sides and can't resolve either collision as its always blocked by one of the other two objects.

So that makes me think that it may not necessarily be a problem with my collision resolution code but is instead more of a fundamental, conceptual problem with how I'm conducting my collision tests?

In the first loop you clear the velocity change when you first encounter a sprite, but it may have already been in a collision by that point.

For example if the first rectangle hits the second, the second will receive a velocity change, which will be cleared before it is applied.

You may also need to run through the whole list multiple times to resolve all intersections.

In the first loop you clear the velocity change when you first encounter a sprite, but it may have already been in a collision by that point.

For example if the first rectangle hits the second, the second will receive a velocity change, which will be cleared before it is applied.

You may also need to run through the whole list multiple times to resolve all intersections.

Good god, I feel ridiculously foolish for missing that. I'll test that out later today and see if it fixes it, but it would explain a lot. Sometimes its the simple things I guess.

I had tried running through the process multiple times and that lessened the problem. Is this an actual approach that some physics engines take? It seems so inefficient.

Yes, but they will narrow down the list by finding potential collisions first, and the iteration will be a tight loop over the minimum amount of data possible.

This topic is closed to new replies.

Advertisement