Tile Collision & Sliding against tiles
- by Devin Rawlek
I have a tile based map with a top down camera. My sprite stops moving when he collides with a wall in any of the four directions however I am trying to get the sprite to slide along the wall if more than one directional key is pressed after being stopped. Tiles are set to 32 x 32.
Here is my code;
// Gets Tile Player Is Standing On
var splatterTileX = (int)player.Position.X / Engine.TileWidth;
var splatterTileY = (int)player.Position.Y / Engine.TileHeight;
// Foreach Layer In World Splatter Map Layers
foreach (var layer in WorldSplatterTileMapLayers)
{
// If Sprite Is Not On Any Edges
if (splatterTileX < layer.Width - 1 && splatterTileX > 0 && splatterTileY < layer.Height - 1 && splatterTileY > 0)
{
tileN = layer.GetTile(splatterTileX, splatterTileY - 1); // North
tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
tileE = layer.GetTile(splatterTileX + 1, splatterTileY); // East
tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
tileS = layer.GetTile(splatterTileX, splatterTileY + 1); // South
tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West
tileW = layer.GetTile(splatterTileX - 1, splatterTileY); // West
tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
}
// If Sprite Is Not On Any X Edges And Is On -Y Edge
if (splatterTileX < layer.Width - 1 && splatterTileX > 0 && splatterTileY == 0)
{
tileE = layer.GetTile(splatterTileX + 1, splatterTileY); // East
tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
tileS = layer.GetTile(splatterTileX, splatterTileY + 1); // South
tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West
tileW = layer.GetTile(splatterTileX - 1, splatterTileY); // West
}
// If Sprite Is On +X And -Y Edges
if (splatterTileX == layer.Width - 1 && splatterTileY == 0)
{
tileS = layer.GetTile(splatterTileX, splatterTileY + 1); // South
tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West
tileW = layer.GetTile(splatterTileX - 1, splatterTileY); // West
}
// If Sprite Is On +X Edge And Y Is Not On Any Edge
if (splatterTileX == layer.Width - 1 && splatterTileY < layer.Height - 1 && splatterTileY > 0)
{
tileS = layer.GetTile(splatterTileX, splatterTileY + 1); // South
tileSW = layer.GetTile(splatterTileX - 1, splatterTileY + 1); // South-West
tileW = layer.GetTile(splatterTileX - 1, splatterTileY); // West
tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
tileN = layer.GetTile(splatterTileX, splatterTileY - 1); // North
}
// If Sprite Is On +X And +Y Edges
if (splatterTileX == layer.Width - 1 && splatterTileY == layer.Height - 1)
{
tileW = layer.GetTile(splatterTileX - 1, splatterTileY); // West
tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
tileN = layer.GetTile(splatterTileX, splatterTileY - 1); // North
}
// If Sprite Is Not On Any X Edges And Is On +Y Edge
if (splatterTileX < (layer.Width - 1) && splatterTileX > 0 && splatterTileY == layer.Height - 1)
{
tileW = layer.GetTile(splatterTileX - 1, splatterTileY); // West
tileNW = layer.GetTile(splatterTileX - 1, splatterTileY - 1); // North-West
tileN = layer.GetTile(splatterTileX, splatterTileY - 1); // North
tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
tileE = layer.GetTile(splatterTileX + 1, splatterTileY); // East
}
// If Sprite Is On -X And +Y Edges
if (splatterTileX == 0 && splatterTileY == layer.Height - 1)
{
tileN = layer.GetTile(splatterTileX, splatterTileY - 1); // North
tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
tileE = layer.GetTile(splatterTileX + 1, splatterTileY); // East
}
// If Sprite Is On -X Edge And Y Is Not On Any Edges
if (splatterTileX == 0 && splatterTileY < (layer.Height - 1) && splatterTileY > 0)
{
tileN = layer.GetTile(splatterTileX, splatterTileY - 1); // North
tileNE = layer.GetTile(splatterTileX + 1, splatterTileY - 1); // North-East
tileE = layer.GetTile(splatterTileX + 1, splatterTileY); // East
tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
tileS = layer.GetTile(splatterTileX, splatterTileY + 1); // South
}
// If Sprite Is In The Top Left Corner
if (splatterTileX == 0 && splatterTileY == 0)
{
tileE = layer.GetTile(splatterTileX + 1, splatterTileY); // East
tileSE = layer.GetTile(splatterTileX + 1, splatterTileY + 1); // South-East
tileS = layer.GetTile(splatterTileX, splatterTileY + 1); // South
}
// Creates A New Rectangle For TileN
tileN.TileRectangle = new Rectangle(splatterTileX * Engine.TileWidth,
(splatterTileY - 1) * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And N Tile
var tileNCollision = player.Rectangle.Intersects(tileN.TileRectangle);
// Creates A New Rectangle For TileNE
tileNE.TileRectangle = new Rectangle((splatterTileX + 1) * Engine.TileWidth,
(splatterTileY - 1) * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And NE Tile
var tileNECollision = player.Rectangle.Intersects(tileNE.TileRectangle);
// Creates A New Rectangle For TileE
tileE.TileRectangle = new Rectangle((splatterTileX + 1) * Engine.TileWidth,
splatterTileY * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And E Tile
var tileECollision = player.Rectangle.Intersects(tileE.TileRectangle);
// Creates A New Rectangle For TileSE
tileSE.TileRectangle = new Rectangle((splatterTileX + 1) * Engine.TileWidth,
(splatterTileY + 1) * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And SE Tile
var tileSECollision = player.Rectangle.Intersects(tileSE.TileRectangle);
// Creates A New Rectangle For TileS
tileS.TileRectangle = new Rectangle(splatterTileX * Engine.TileWidth,
(splatterTileY + 1) * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And S Tile
var tileSCollision = player.Rectangle.Intersects(tileS.TileRectangle);
// Creates A New Rectangle For TileSW
tileSW.TileRectangle = new Rectangle((splatterTileX - 1) * Engine.TileWidth,
(splatterTileY + 1) * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And SW Tile
var tileSWCollision = player.Rectangle.Intersects(tileSW.TileRectangle);
// Creates A New Rectangle For TileW
tileW.TileRectangle = new Rectangle((splatterTileX - 1) * Engine.TileWidth,
splatterTileY * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And Current Tile
var tileWCollision = player.Rectangle.Intersects(tileW.TileRectangle);
// Creates A New Rectangle For TileNW
tileNW.TileRectangle = new Rectangle((splatterTileX - 1) * Engine.TileWidth,
(splatterTileY - 1) * Engine.TileHeight,
Engine.TileWidth,
Engine.TileHeight);
// Tile Collision Detection Between Player Rectangle And Current Tile
var tileNWCollision = player.Rectangle.Intersects(tileNW.TileRectangle);
// Allow Sprite To Occupy More Than One Tile
if (tileNCollision && tileN.TileBlocked == false)
{
tileN.TileOccupied = true;
}
if (tileECollision && tileE.TileBlocked == false)
{
tileE.TileOccupied = true;
}
if (tileSCollision && tileS.TileBlocked == false)
{
tileS.TileOccupied = true;
}
if (tileWCollision && tileW.TileBlocked == false)
{
tileW.TileOccupied = true;
}
// Player Up
if (keyState.IsKeyDown(Keys.W) ||
(gamePadOneState.DPad.Up == ButtonState.Pressed))
{
player.CurrentAnimation = AnimationKey.Up;
if (tileN.TileOccupied == false)
{
if (tileNWCollision && tileNW.TileBlocked || tileNCollision && tileN.TileBlocked || tileNECollision && tileNE.TileBlocked)
{
playerMotion.Y = 0;
}
else
playerMotion.Y = -1;
}
else if (tileN.TileOccupied)
{
if (tileNWCollision && tileNW.TileBlocked || tileNECollision && tileNE.TileBlocked)
{
playerMotion.Y = 0;
}
else
playerMotion.Y = -1;
}
}
// Player Down
if (keyState.IsKeyDown(Keys.S) ||
(gamePadOneState.DPad.Down == ButtonState.Pressed))
{
player.CurrentAnimation = AnimationKey.Down;
// Check Collision With Tiles
if (tileS.TileOccupied == false)
{
if (tileSWCollision && tileSW.TileBlocked || tileSCollision && tileS.TileBlocked || tileSECollision && tileSE.TileBlocked)
{
playerMotion.Y = 0;
}
else
playerMotion.Y = 1;
}
else if (tileS.TileOccupied)
{
if (tileSWCollision && tileSW.TileBlocked || tileSECollision && tileSE.TileBlocked)
{
playerMotion.Y = 0;
}
else
playerMotion.Y = 1;
}
}
// Player Left
if (keyState.IsKeyDown(Keys.A) ||
(gamePadOneState.DPad.Left == ButtonState.Pressed))
{
player.CurrentAnimation = AnimationKey.Left;
if (tileW.TileOccupied == false)
{
if (tileNWCollision && tileNW.TileBlocked || tileWCollision && tileW.TileBlocked || tileSWCollision && tileSW.TileBlocked)
{
playerMotion.X = 0;
}
else
playerMotion.X = -1;
}
else if (tileW.TileOccupied)
{
if (tileNWCollision && tileNW.TileBlocked || tileSWCollision && tileSW.TileBlocked)
{
playerMotion.X = 0;
}
else
playerMotion.X = -1;
}
}
// Player Right
if (keyState.IsKeyDown(Keys.D) ||
(gamePadOneState.DPad.Right == ButtonState.Pressed))
{
player.CurrentAnimation = AnimationKey.Right;
if (tileE.TileOccupied == false)
{
if (tileNECollision && tileNE.TileBlocked || tileECollision && tileE.TileBlocked || tileSECollision && tileSE.TileBlocked)
{
playerMotion.X = 0;
}
else
playerMotion.X = 1;
}
else if (tileE.TileOccupied)
{
if (tileNECollision && tileNE.TileBlocked || tileSECollision && tileSE.TileBlocked)
{
playerMotion.X = 0;
}
else
playerMotion.X = 1;
}
}
I have my tile detection setup so the 8 tiles around the sprite are the only ones detected. The collision variable is true if the sprites rectangle intersects with one of the detected tiles. The sprites origin is centered at 16, 16 on the image so whenever this point goes over to the next tile it calls the surrounding tiles.
I am trying to have collision detection like in the game Secret of Mana.
If I remove the diagonal checks the sprite will pass through thoses tiles because whichever tile the sprites origin is on will be the detection center. So if the sprite is near the edge of the tile and then goes up it looks like half the sprite is walking through the wall.
Is there a way for the detection to occur for each tile the sprite's rectangle touches?