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 );
}
}