# Sloped tiles in Platformer

I'm extreme new to game development and this is my second project. I’m working on a platform game that expands on the Platformer example provided at the app hub, so far I’ve got parallax scrolling (horizontal and vertical), moving platforms, tight touch controls, double jumps, melee attacks projectiles, and Game State management all working. BUT for the life of me I can’t figure out how to implement sloped tiles. I’ve searched for a ton of tutorials but I can’t find any good ones (by good I mean ones that i can understand). Again I would but grateful for any help that someone could provide.

Here is the code i am using for collisions (I've edit it to try and incorporate the code from the link below but still no luck. it runs but this code still treats the sloped tile as if they were regular tiles:

Edited Code

    /// <summary>
/// Detects and resolves all collisions between the player and his neighboring
/// tiles. When a collision is detected, the player is pushed away along one
/// axis to prevent overlapping. There is some special logic for the Y axis to
/// handle platforms which behave differently depending on direction of movement.
/// </summary>

private void HandleCollisions()
{
// Get the player's bounding rectangle and find neighboring tiles.
Rectangle bounds = BoundingRectangle;
int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

// Was the player on a ramp last frame?
bool wasOnRamp = isOnRamp;

float dist =
(float)Math.Sqrt
(Math.Pow
(MathHelper.Clamp
(bounds.Center.X - bounds.Left, 0, Tile.Width), 2) +
Math.Pow
(MathHelper.Clamp
(bounds.Bottom - bounds.Bottom, 0, Tile.Height), 2));

float slopeTopLeft =
(bounds.Bottom -
((bounds.Center.X - bounds.Left) *
(float)Math.Sin
(Math.Atan2
(bounds.Height,
(bounds.Center.X - bounds.Left)))));

float slopeTopRight =
(bounds.Bottom -
((bounds.Center.X - bounds.Right) *
(float)Math.Sin
(Math.Atan2
(bounds.Height,
(bounds.Center.X - bounds.Right)))));

// Reset flag to search for ground collision.
isOnGround = false;
isOnRamp = false;

//For each potentially colliding movable tile.
foreach (var movableTile in level.movableTiles)
{

// Reset flag to search for movable tile collision.
movableTile.PlayerIsOn = false;

//check to see if player is on tile.
if ((BoundingRectangle.Bottom >= movableTile.BoundingRectangle.Top) &&
(BoundingRectangle.Left >= movableTile.BoundingRectangle.Left - (BoundingRectangle.Width)) &&
(BoundingRectangle.Right <= movableTile.BoundingRectangle.Right + (BoundingRectangle.Width)))
{
if ((BoundingRectangle.Bottom < movableTile.BoundingRectangle.Top + 5))
{

movableTile.PlayerIsOn = true;

}

}

bounds = HandleCollision(bounds, movableTile.Collision, movableTile.BoundingRectangle);
}

//  for (int x = leftTile; x <= rightTile; ++x)
// {
//  TileCollision collision = Level.GetCollision(x, bottomTile);
// if (collision == TileCollision.SlantedUp || collision == TileCollision.SlantedDown)
// {
//    isOnRamp = true;
// }
// }

// For each potentially colliding tile,
for (int y = topTile; y <= bottomTile; ++y)
{
for (int x = leftTile; x <= rightTile; ++x)
{
// If this tile is collidable,
TileCollision collision = Level.GetCollision(x, y);

/*
if (collision == TileCollision.SlantedUp)
{
//create a rectangle collision box
Rectangle tileCol = Level.GetBounds(x, y); //new Rectangle(x * Tile.Size, (y * Tile.Size), Tile.Size, Tile.Size + 1);

//if player collision "col" collides with "tileCol" and you haven't done this before this itteration (only happens once per full double loop)
if (bounds.Intersects(tileCol) && !isOnRamp)
{

//get the angle of the tile
float angle = level.angle;  // [level.tile[x, y]];

//get the x distance of how far away the player's right is inside the tile
float dist = (bounds.X + bounds.Width) - tileCol.X;

//constructs the opposite of a right triangle
float opposite = (float)(Math.Tan(MathHelper.ToRadians(angle)) * (dist));

if (angle < 90)
{
//if player's right is less then or equal to tile's right
if (bounds.X + bounds.Width <= tileCol.X + tileCol.Width)
{
//place player on slope. this works properly
position.Y = tileCol.Y - opposite;
//tell the program we don't wanna go through this again until the next full loop starts.
isOnRamp = true;
}
}

else if (angle > 90)
{
if ((bounds.X + bounds.Width) >= tileCol.X)
{
//this is where the error is. the player goes "up" a slope that's 315 degrees, instead of down it.
//how do I make the player go down the slope that's 315 degrees!?
position.Y = tileCol.Y + opposite;
isOnRamp = true;
}
}
}

}

*/

//If we touched a checkpoint tile, set our new checkpoint here
if (collision == TileCollision.Checkpoint)
level.checkpoint = new Vector2(x, y) * Tile.Size;

if (collision != TileCollision.Passable)
{

// Determine collision depth (with direction) and magnitude.
Rectangle tileBounds = Level.GetBounds(x, y);
Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
if (depth != Vector2.Zero)
{
float absDepthX = Math.Abs(depth.X);
float absDepthY = Math.Abs(depth.Y);

// List<TileEdge> collidableEdges = Level.GetCollidableEdges(x, y);

// Resolve the collision along the shallow axis.
if (absDepthY < absDepthX || collision == TileCollision.Platform)
{
// If we crossed the top of a tile, we are on the ground.
if (previousBottom <= tileBounds.Top)
isOnGround = true;

// Ignore platforms, unless we are on the ground.
if (collision == TileCollision.Impassable || IsOnGround)
{
// Resolve the collision along the Y axis.
Position = new Vector2(Position.X, Position.Y + depth.Y);

// Perform further collisions with the new bounds.
bounds = BoundingRectangle;
}
}
else if (collision == TileCollision.Impassable) // Ignore platforms.
{
// Resolve the collision along the X axis.
Position = new Vector2(Position.X + depth.X, Position.Y);

// Perform further collisions with the new bounds.
bounds = BoundingRectangle;
}

else if (collision == TileCollision.SlantedUp)
{
// float dist = (bounds.X + bounds.Width) - depth.X;
// float opposite = (float)(Math.Tan(MathHelper.ToRadians(45)) * (dist));

//  float a = Position.X - bottomTile;

//bounds = m * a + absDepth.Y;

//  Position = new Vector2(Position.X + depth.X, Position.Y - opposite);

// bounds = BoundingRectangle;

// Resolve the collision along the Y axis.
Position = new Vector2
(Position.X, slopeTopLeft);
Position = new Vector2
((float)Math.Round(Position.X),
(float)Math.Round(Position.Y));

// Perform further collisions with the new bounds.
bounds = BoundingRectangle;
}

}
}
}
}

// Save the new bounds bottom.
previousBottom = bounds.Bottom;
}

private Rectangle HandleCollision(Rectangle bounds, TileCollision collision, Rectangle tileBounds)
{
Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
if (depth != Vector2.Zero)
{
float absDepthX = Math.Abs(depth.X);
float absDepthY = Math.Abs(depth.Y);

// Resolve the collision along the shallow axis.
if (absDepthY < absDepthX || collision == TileCollision.Platform)
{
// If we crossed the top of a tile, we are on the ground.
if (previousBottom <= tileBounds.Top)
isOnGround = true;

// Ignore platforms, unless we are on the ground.
if (collision == TileCollision.Impassable || IsOnGround)
{
// Resolve the collision along the Y axis.
Position = new Vector2(Position.X, Position.Y + depth.Y);

// Perform further collisions with the new bounds.
bounds = BoundingRectangle;
}
}
else if (collision == TileCollision.Impassable) // Ignore platforms.
{
// Resolve the collision along the X axis.
Position = new Vector2(Position.X + depth.X, Position.Y);

// Perform further collisions with the new bounds.
bounds = BoundingRectangle;
}

}
return bounds;
}

• This question is pretty similar to the one here: gamedev.stackexchange.com/questions/17922/… You may want to read my answer there. – ChrisC Dec 21 '11 at 21:41
• hey thanks for that i actually saw that earlier in the day and I couldn't quite understand that post I tried to use it but the I can't figure out how to implement it for example: Does lv = level Does col = tileBounds Does pos = Position Does tileCol = Collision ETC. (sorry to be such a noob) – Nexus1216 Dec 22 '11 at 0:23

Sorry codeless answer here, though hopefully you will find quite useful if you can bothered writing the simple code to accompany the technique.

This is a technique i use quite a bit in platformers to handle slopes. I call it the "5 Point Collision" technique. Basically, your character has 5 main points associated with them, as shown in the image above.

2 head collision points (green) 2 wall collision points (red) 1 ground collision point (yellow)

Every step of the game you check collision on each of these 5 points when moving. If either of the head collision points are hit, you have jumped to high! If either of the wall collision points are hit, you have hit a wall, and if your ground collision point is hit, you have hit the ground, and should move your character up a pixel (until it no longer detects ground).

So how does this handle slopes?

Well if you look at the image above, you'll notice that some things may be low enough to pass under the wall collision points, and then hit the ground collision point, forcing the character to move upwards. This means if you had a slope, if the slope is too great, it will hit the wall collision point before the ground collision point; no access. However if the slope is low enough, it shall hit the ground collision point before the wall collision point, moving the character up, and therefore making the character walk up the slope.

Extra points can be added for collision detection to improve mechanics, though remember every point you add means more calculations...

Adding two ground collision points, and spreading them evenly along the bottom sometimes helps. The further the ground collision points are from the middle, the steeper slopes are that the player can handle. Alternatively you could just make the wall collision points higher up, but this may not end well (your character may end up "Jumping" onto obstacles. So my advice is keep the wall collisions reasonably low, (remember if the head collision points are in the corner as shown in my image, then they can also act as wall collision points).

Hope it helps you, and if not, someone else who glances by :)

• This is a neat technique! I'd like to add that having 3 ground collision points (two at the sides) might be useful too. For instance, it could be used to detect when the player is on the edge of a platform (i.e. when only one of the side ground points collides), and play a different animation, for instance, like he's about to fall. – David Gouveia Dec 22 '11 at 14:38
• But I also find this is better for a pixel perfect environment. If the world is entirely tiled and without pixel perfect collisions, and the slopes have a fixed angle (as is usually the case), calculating it like annonymously mentioned will be easier and more efficient. – David Gouveia Dec 22 '11 at 14:42
• i made a pixel based game, each pixel was actually 4x4. The player was 8 pixels high and i put the wall collisions at 5 pixels high, meaning he could go up one pixel in height, but not 2. So this works well with non-sloped tiles (that give the effect of a slope), but yes, ananonymously's example is better for tiled slopes. Also, yes, the two extra corner tiles is a good idea. :D – Joel Dec 22 '11 at 14:50

If you know what the angle of the tile is then you can just find the gradient of the slope:

// angle is the angle of the sloped tile
// m is the gradient = rise/run
// c is the offset up of the slope, you may want to make it negative as the computer
// screen coordinates are upside down.

float m = Math.Sin(angleInRadians)  // Sin gives us the height, and Cos gives us the width
/  Math.Cos(angleInRadians); // It's best to only work this out once (like in LoadContent)

float x = playerX - tileX; // playerX is the x coordinate of the player
// tileX is the x coordinate of the tile's bottom left corner

// y = mx + c
playerY = m * x + tileY; // In this case, c will be the y coordinate of the bottom left corner of the sloped tile.

• in your code is angle a float or a double – Nexus1216 Dec 22 '11 at 20:46
• whatever is easier for you :) but if you use double you need to cast it to float in the method 'ToRadians' – annonymously Dec 22 '11 at 21:30
• Id also like to note that at certain angles (90 and 270) calculating m will give a divide by zero error because of the way that Sin and Cos work, so you can check for it if you want. Of course you probably wont use a sloped tile to go straight up and down, so it most likely won't happen – annonymously Dec 22 '11 at 21:42
• i have set angle as a double but i'm getting an error that says i can't implicitly convert a float to a double – Nexus1216 Dec 22 '11 at 22:19