I am writing a 2D tile based game engine in XNA, and I've recently fine-tuned my collision detection using the answers provided here and more specifically here.
The tile based collision detection now works flawlessly and fixed all the small problems I had with my previous code. However, implementing this forced me to rewrite how movement was handled. Instead of moving the player all at once and then resolving each collision along the smallest axis of intersection depth, it now updates and resolves on the X and Y axes separately.
This is fine, but now my diagonal movement is much faster than my horizontal and vertical movement and I am unsure of how to normalize it.
I'm sure I'm just overlooking something simple, is there a straight forward way of doing this?
public void HandleInput()
{
//Set horizontal and vertical values accordingly based on direction of key presses
int horizontal = InputHelper.IsKeyDown(Keys.A) ? -1 : (InputHelper.IsKeyDown(Keys.D) ? 1 : 0);
int vertical = InputHelper.IsKeyDown(Keys.W) ? -1 : (InputHelper.IsKeyDown(Keys.S) ? 1 : 0);
//If there is movement update sprite position, round the position to nearest pixel and then check for collision
if (vertical != 0)
{
sprite.Position += Vector2.UnitY * sprite.Speed * vertical;
sprite.Position = new Vector2(sprite.Position.X, (float)Math.Round(sprite.Position.Y));
HandleCollisions(Direction.Vertical);
UpdateSpriteAnimation(Direction.Vertical, vertical);
}
if (horizontal != 0)
{
sprite.Position += Vector2.UnitX * sprite.Speed * horizontal;
sprite.Position = new Vector2((float)Math.Round(sprite.Position.X), sprite.Position.Y);
HandleCollisions(Direction.Horizontal);
UpdateSpriteAnimation(Direction.Horizontal, horizontal);
}
}
private void HandleCollisions(Direction direction)
{
// Get the player's bounding rectangle and find neighboring tiles.
Rectangle playerBounds = sprite.spriteBounds;
int leftTile = playerBounds.Left / Engine.TileSize;
int topTile = playerBounds.Top / Engine.TileSize;
int rightTile = (int)Math.Ceiling((float)playerBounds.Right / Engine.TileSize) - 1;
int bottomTile = (int)Math.Ceiling(((float)playerBounds.Bottom / Engine.TileSize)) - 1;
// For each potentially colliding tile,
for (int y = topTile; y <= bottomTile; ++y)
{
for (int x = leftTile; x <= rightTile; ++x)
{
Vector2 depth;
//If this tile is collidable, and it intersects sprite bounds
if (tileMap.CollisionLayer.GetCellIndex(x, y) == 0 && TileIntersectsPlayer(playerBounds, GetTileBounds(y, x), direction, out depth))
{
//Resolve the collision along the given axis
sprite.Position += depth;
// Perform further collisions with the new bounds.
playerBounds = sprite.spriteBounds;
}
}
}
}