Resolving collisions between dynamic game objects
- by TheBroodian
I've been building a 2D platformer for some time now, I'm getting to the point where I am adding dynamic objects to the stage for testing. This has prompted me to consider how I would like my character and other objects to behave when they collide.
A typical staple in many 2D platformer type games is that the player takes damage upon touching an enemy, and then essentially becomes able to pass through enemies during a period of invulnerability, and at the same time, enemies are able to pass through eachother freely.
I personally don't want to take this approach, it feels strange to me that the player should receive arbitrary damage for harmless contact to an enemy, despite whether the enemy is attacking or not, and I would like my enemies' interactions between each other (and my player) to be a little more organic, so to speak.
In my head I sort of have this idea where a game object (player, or non player) would be able to push other game objects around by manner of 'pushing' each other out of one anothers' bounding boxes if there is an intersection, and maybe correlate the repelling force to how much their bounding boxes are intersecting.
The problem I'm experiencing is I have no idea what the math might look like for something like this? I'll show what work I've done so far, it sort of works, but it's jittery, and generally not quite what I would pass in a functional game:
//Clears the anti-duplicate buffer
collisionRecord.Clear();
//pick a thing
foreach (GameObject entity in entities)
{
//pick another thing
foreach (GameObject subject in entities)
{
//check to make sure both things aren't the same thing
if (!ReferenceEquals(entity, subject))
{
//check to see if thing2 is in semi-near proximity to thing1
if (entity.WideProximityArea.Intersects(subject.CollisionRectangle) ||
entity.WideProximityArea.Contains(subject.CollisionRectangle))
{
//check to see if thing2 and thing1 are colliding.
if (entity.CollisionRectangle.Intersects(subject.CollisionRectangle) ||
entity.CollisionRectangle.Contains(subject.CollisionRectangle) ||
subject.CollisionRectangle.Contains(entity.CollisionRectangle))
{
//check if we've already resolved their collision or not.
if (!collisionRecord.ContainsKey(entity.GetHashCode()))
{
//more duplicate resolution checking.
if (!collisionRecord.ContainsKey(subject.GetHashCode()))
{
//if thing1 is traveling right...
if (entity.Velocity.X > 0)
{
//if it isn't too far to the right...
if (subject.CollisionRectangle.Contains(new Microsoft.Xna.Framework.Rectangle(entity.CollisionRectangle.Right, entity.CollisionRectangle.Y, 1, entity.CollisionRectangle.Height)) ||
subject.CollisionRectangle.Intersects(new Microsoft.Xna.Framework.Rectangle(entity.CollisionRectangle.Right, entity.CollisionRectangle.Y, 1, entity.CollisionRectangle.Height)))
{
//Find how deep thing1 is intersecting thing2's collision box;
float offset = entity.CollisionRectangle.Right - subject.CollisionRectangle.Left;
//Move both things in opposite directions half the length of the intersection, pushing thing1 to the left, and thing2 to the right.
entity.Velocities.Add(new Vector2(-(((offset * 4) * (float)gameTime.ElapsedGameTime.TotalMilliseconds)), 0));
subject.Velocities.Add(new Vector2((((offset * 4) * (float)gameTime.ElapsedGameTime.TotalMilliseconds)), 0));
}
}
//if thing1 is traveling left...
if (entity.Velocity.X < 0)
{
//if thing1 isn't too far left...
if (entity.CollisionRectangle.Contains(new Microsoft.Xna.Framework.Rectangle(subject.CollisionRectangle.Right, subject.CollisionRectangle.Y, 1, subject.CollisionRectangle.Height)) ||
entity.CollisionRectangle.Intersects(new Microsoft.Xna.Framework.Rectangle(subject.CollisionRectangle.Right, subject.CollisionRectangle.Y, 1, subject.CollisionRectangle.Height)))
{
//Find how deep thing1 is intersecting thing2's collision box;
float offset = subject.CollisionRectangle.Right - entity.CollisionRectangle.Left;
//Move both things in opposite directions half the length of the intersection, pushing thing1 to the right, and thing2 to the left.
entity.Velocities.Add(new Vector2((((offset * 4) * (float)gameTime.ElapsedGameTime.TotalMilliseconds)), 0));
subject.Velocities.Add(new Vector2(-(((offset * 4) * (float)gameTime.ElapsedGameTime.TotalMilliseconds)), 0));
}
}
//Make record that thing1 and thing2 have interacted and the collision has been solved, so that if thing2 is picked next in the foreach loop, it isn't checked against thing1 a second time before the next update.
collisionRecord.Add(entity.GetHashCode(), subject.GetHashCode());
}
}
}
}
}
}
}
}
One of the biggest issues with my code aside from the jitteriness is that if one character were to land on top of another character, it very suddenly and abruptly resolves the collision, whereas I would like a more subtle and gradual resolution.
Any thoughts or ideas are incredibly welcome and helpful.