Okay, this is really starting to annoy me.
First of all, detecting collisions is nice and easy. I have a list of tiles, and a player. The player and each tile have their own rectangles. (The tiles are 16x16, the player is 10x14). To detect a collision, I simply iterate through the list of tiles to see if the player is colliding with any of them, and if so, set a value to true.
^The above works all fine and dandy, but I've tried just about every way I can think of to respond to that collision. I've ended up with some almost there but buggy results, and I can't find a single tutorial on 2D collision RESPONSE. Sure, there are thousands of tutorials out there about detection, but I'm having more trouble then I would think I would when I look for stuff about response.
The two methods I use for collision are;
public bool CanMove(Rectangle playerRect, List<Tile> tiles, Direction d, Vector2 speed)
{
bool b = true;
Rectangle r = new Rectangle();
if (d == Direction.Up)
{
foreach (Tile ti in tiles)
{
r = new Rectangle(playerRect.X, (int)(playerRect.Y - speed.Y), playerRect.Width, playerRect.Height);
if (r.Intersects(ti.rect))
{
b = false;
return b;
}
}
}
if (d == Direction.Down)
{
int adjust = 1;
if (playerPos.Y < 0)
adjust -= 1;
foreach (Tile ti in tiles)
{
r = new Rectangle(playerRect.X, (int)(playerRect.Y + speed.Y + adjust), playerRect.Width, playerRect.Height);
if (r.Intersects(ti.rect))
{
if (Math.Abs(r.Y - ti.rect.Y) > 12.5f && Math.Abs(r.Y - ti.rect.Y) < 13.5f && Math.Abs(r.X - ti.rect.X) > 14.5f && Math.Abs(r.X - ti.rect.X) < 15.5f)
{
playerPos.Y -= 1;
}
b = false;
return b;
}
}
}
if (d == Direction.Gravity)
{
int adjust = 1;
if (playerPos.Y < 0)
adjust -= 1;
foreach (Tile ti in tiles)
{
r = new Rectangle(playerRect.X, (int)(playerRect.Y + speed.Y + adjust), playerRect.Width, playerRect.Height);
if (r.Intersects(ti.rect))
{
if (Math.Abs(r.Y - ti.rect.Y) > 12.5f && Math.Abs(r.Y - ti.rect.Y) < 13.5f && Math.Abs(r.X - ti.rect.X) > 14.5f && Math.Abs(r.X - ti.rect.X) < 15.5f)
{
playerPos.Y -= 1;
}
//adjustTile = ti;
b = false;
return b;
}
}
}
if (d == Direction.Left)
{
foreach (Tile ti in tiles)
{
r = new Rectangle((int)(playerRect.X - speed.X), playerRect.Y, playerRect.Width, playerRect.Height);
if (r.Intersects(ti.rect))
{
adjustTile = ti;
b = false;
return b;
}
}
}
if (d == Direction.Right)
{
foreach (Tile ti in tiles)
{
r = new Rectangle((int)(playerRect.X + speed.X), playerRect.Y, playerRect.Width, playerRect.Height);
if (r.Intersects(ti.rect))
{
b = false;
return b;
}
}
}
return b;
}
And the portion of my update method that's necessary;
if (kbs.IsKeyDown(Keys.Right) && kbs.IsKeyUp(Keys.Left))
{
direction = Direction.Right;
}
if (kbs.IsKeyDown(Keys.Left) && kbs.IsKeyUp(Keys.Right))
{
direction = Direction.Left;
}
//if (IsColliding(playerRectangle, tileList))
if (playerSpeed.Y < 0)
{
playerSpeed.Y += 0.15f;
}
else
{
playerSpeed.Y += 0.2f;
}
//playerPos.X += playerSpeed.X;
if (kbs.IsKeyDown(Keys.Space) && pkbs.IsKeyUp(Keys.Space))
{
playerPos = Vector2.Zero;
playerSpeed.Y = 0;
}
playerRectangle = new Rectangle((int)playerPos.X + 3, (int)playerPos.Y + 2, 10, 14);
if (playerRectangle.Contains(ms.X - viewport.Width / 2, ms.Y - viewport.Height / 2))
{
player.rectY = 16;
}
else
{
player.rectY = 0;
}
if (CanMove(playerRectangle, tileList, Direction.Gravity, new Vector2(0, playerSpeed.Y)))
{
if (playerSpeed.Y == 0)
playerSpeed.Y = 1f;
playerPos.Y += playerSpeed.Y;
}
else
{
canJump = true;
playerSpeed.Y = 0f;
//if (adjustTile != null && playerPos.Y != adjustTile.rect.Y - 15)
//{
// playerPos.Y = adjustTile.rect.Y - 15;
// adjustTile = null;
//}
playerPos.Y = playerRectangle.Y - 2;
}
if (CanMove(playerRectangle, tileList, Direction.Left, new Vector2(playerSpeed.X, 0)))
{
//playerPos.X -= playerSpeed.X;
player.rectY = 0;
}
else
{
player.rectY = 16;
}
if (CanMove(playerRectangle, tileList, Direction.Right, new Vector2(playerSpeed.X, 0)))
{
//playerPos.X -= playerSpeed.X;
player.rectY = 0;
}
else
{
player.rectY = 16;
}
#region Check input
if (kbs.IsKeyDown(Keys.Left) && pkbs.IsKeyUp(Keys.Right))
{
player.flipped = true;
}
if (kbs.IsKeyDown(Keys.Right) && pkbs.IsKeyUp(Keys.Left))
{
player.flipped = false;
}
if (kbs.IsKeyDown(Keys.Up) && pkbs.IsKeyUp(Keys.Up))
{
if (CanMove(playerRectangle, tileList, Direction.Up, new Vector2(0, 2)))
{
if (canJump)
{
playerSpeed.Y -= 4.2f;
}
player.rectY = 0;
}
else
{
player.rectY = 16;
}
}
if (kbs.IsKeyDown(Keys.Down))
{
if (CanMove(playerRectangle, tileList, Direction.Down, new Vector2(0, 2)))
{
//playerRectangle.Y += 2;
player.rectY = 0;
}
else
{
player.rectY = 16;
}
}
if (kbs.IsKeyDown(Keys.Left))
{
playerSpeed.X = 2f;
if (CanMove(playerRectangle, tileList, Direction.Left, new Vector2(playerSpeed.X, 0)))
{
playerPos.X -= playerSpeed.X;
player.rectY = 0;
}
else
{
player.rectY = 16;
}
if (player.rectY == 16)
{
playerPos.Y -= 1;
}
}
if (kbs.IsKeyDown(Keys.Right))
{
playerSpeed.X = 2f;
if (CanMove(playerRectangle, tileList, Direction.Right, new Vector2(playerSpeed.X, 0)))
{
playerPos.X += playerSpeed.X;
player.rectY = 0;
}
else
{
player.rectY = 16;
}
if (player.rectY == 16)
{
if (playerPos.Y / 16 != (int)playerPos.Y / 16)
{
playerPos.Y -= 1;
}
}
}
if (kbs.IsKeyUp(Keys.Right) && kbs.IsKeyUp(Keys.Left))
{
playerSpeed.X = 0;
}
#endregion
This is almost there, but there are one or two kinks in it.
If I'm up against a wall of tiles, and jump, I'll end up in the roof(If not going completely through it) if it's low enough.
The reason I add the 'playerpos.y -= 1' in certain spots is because the player would get stuck when coming down on a corner at the right angle.
Also, when the player goes to jump, and come back down, he will hit an invisible wall (usually about 8 pixels or so above the tile he would be landing on) before continuing to fall and land on the said tile.
Any tips or advice?
And yes, this is 2D, there is gravity and such, I'm trying to get it to work kinda like the way mario collisions work.
EDIT:
The new code is actually what I came up with to start with, the HandleCollisions()
thing was an attempt to try and implement the Platformer Starter kit code, which didn't work very well. The GetRectangleIntersectionDepth()
might come in handy though.