Trouble with AABB collision response and physics
- by WCM
I have been racking my brain trying to figure out a problem I am having with physics and basic AABB collision response. I am fairly close as the physics are mostly right. Gravity feels good and movement is solid. The issue I am running into is that when I land on the test block in my project, I can jump off of it most of the time. If I repeatedly jump in place, I will eventually get stuck one or two pixels below the surface of the test block.
If I try to jump, I can become free of the other block, but it will happen again a few jumps later. I feel like I am missing something really obvious with this. I have two functions that support the detection and function to return a vector for the overlap of the two rectangle bounding boxes. I have a single update method that is processing the physics and collision for the entity.
I feel like I am missing something very simple, like an ordering of the physics vs. collision response handling. Any thoughts or help can be appreciated. I apologize for the format of the code, tis prototype code mostly.
The collision detection function:
public static bool Collides(Rectangle source, Rectangle target)
{
if (source.Right < target.Left ||
source.Bottom < target.Top ||
source.Left > target.Right ||
source.Top > target.Bottom)
{
return false;
}
return true;
}
The overlap function:
public static Vector2 GetMinimumTranslation(Rectangle source, Rectangle target)
{
Vector2 mtd = new Vector2();
Vector2 amin = source.Min();
Vector2 amax = source.Max();
Vector2 bmin = target.Min();
Vector2 bmax = target.Max();
float left = (bmin.X - amax.X);
float right = (bmax.X - amin.X);
float top = (bmin.Y - amax.Y);
float bottom = (bmax.Y - amin.Y);
if (left > 0 || right < 0) return Vector2.Zero;
if (top > 0 || bottom < 0) return Vector2.Zero;
if (Math.Abs(left) < right)
mtd.X = left;
else
mtd.X = right;
if (Math.Abs(top) < bottom)
mtd.Y = top;
else
mtd.Y = bottom;
// 0 the axis with the largest mtd value.
if (Math.Abs(mtd.X) < Math.Abs(mtd.Y))
mtd.Y = 0;
else
mtd.X = 0;
return mtd;
}
The update routine (gravity = 0.001f, jumpHeight = 0.35f, moveAmount = 0.15f):
public void Update(GameTime gameTime)
{
Acceleration.Y = gravity;
Position += new Vector2((float)(movement * moveAmount * gameTime.ElapsedGameTime.TotalMilliseconds), (float)(Velocity.Y * gameTime.ElapsedGameTime.TotalMilliseconds));
Velocity.Y += Acceleration.Y;
Vector2 previousPosition = new Vector2((int)Position.X, (int)Position.Y);
KeyboardState keyboard = Keyboard.GetState();
movement = 0;
if (keyboard.IsKeyDown(Keys.Left))
{
movement -= 1;
}
if (keyboard.IsKeyDown(Keys.Right))
{
movement += 1;
}
if (Position.Y + 16 > GameClass.Instance.GraphicsDevice.Viewport.Height)
{
Velocity.Y = 0;
Position = new Vector2(Position.X, GameClass.Instance.GraphicsDevice.Viewport.Height - 16);
IsOnSurface = true;
}
if (Collision.Collides(BoundingBox, GameClass.Instance.block.BoundingBox))
{
Vector2 mtd = Collision.GetMinimumTranslation(BoundingBox, GameClass.Instance.block.BoundingBox);
Position += mtd;
Velocity.Y = 0;
IsOnSurface = true;
}
if (keyboard.IsKeyDown(Keys.Space) && !previousKeyboard.IsKeyDown(Keys.Space))
{
if (IsOnSurface)
{
Velocity.Y = -jumpHeight;
IsOnSurface = false;
}
}
previousKeyboard = keyboard;
}
This is also a full download to the project.
https://www.box.com/s/3rkdtbso3xgfgc2asawy
P.S. I know that I could do this with the XNA Platformer Starter Kit algo, but it has some deep flaws that I am going to try to live without. I'd rather go the route of collision response via an overlay function.
Thanks for any and all insight!