> | | | | | | | | | | | > Traversing the Slope

Traversing the Slope

Posted on Tuesday, July 31, 2012 | 1 Comment

It can be challenging climbing up those sloped tiles.  Otto probably isn't used to working against an incline; maybe he should start hitting the gym in preparation.  Oh boy, that means I have to build him a tile-based game version of a gym!  If he works at it, maybe one day he could compete in the Olympics!  I think he has what it takes to win gold.  Go Otto!

Otto wearing an Olympic medal.
A winner is you.

You know, I just remembered... they don't let robots into the Olympics!  Sorry, Otto.  You can still get some exercise on my new sloped tiles, though.  Speaking of these sloped tiles, let's see how they work, shall we?

As you may recall, last time we left off having just figured out how to detect the tile underneath Otto.  Now we will get to see what code runs once we're on a sloped tile!  First, we need to think of the different situations that may lead to Otto being on a sloped tile (and remember, when I say "Otto" I'm really talking about the point represented by the single red pixel from last post).  The first situation is that tileUnderneath > 15, but what if we've entered a sloped tile from above?  The normal collision will run and the conditional won't be true until Otto falls from the square tile onto the sloped one.  We need another point to test!  We'll call this one tileBelow and it will be the pixel just below tileUnderneath.

Different point test locations on Otto.

As we talked about last time, the red point is tileUnderneath.  The blue point is our new tileBelow variable and the green points will be either point of the tileAhead variable which we have yet to create.  These comprise the main points we'll be looking at when we try to determine whether or not to run the slope code.  Our first test simply checks the conditional (tileUnderneath > 15 || tileBelow > 15) and runs slope code if it is true.  Later on when we add jumping to slopes we'll have to modify this conditional because of the need to disable all normal y axis movement for the player whenever slope code is running.  Don't worry about that for now though, we'll cross that bridge when we come to it.

So let's pretend our conditional returned true.  That means we're now running the slope code.  The first thing we want to do is suspend the player's y axis movement.  Normally, there is a value added to the player's y velocity every frame in an effort to simulate gravity.  Unfortunately, this will cause problems because we are calculating the player's y position a different way while on a slope; thus, we need to disable y axis movement entirely so that nothing interferes with our calculation for the player's y position.

Now we start checking for the different directions the player could be going.  The player can enter a slope from either moving left or right, falling down onto a slope, or jumping into a sloped tile on the ceiling.  We'll have to write code for each different scenario.  For now let's start with moving left.

We check whether or not the player is moving left by testing the conditional (player.velocity.x < 0).  If this returns true, we know the player is on a slope and heading left.  We now have two possibilities to check for.  The player could either be heading up a slope or down a slope.  Later, when we add 22.5° slopes, we'll have to also check for what degree of incline we're on, but for now we can just assume it's a 45° slope.

Let's assume we are moving down a slope.  So, Otto is heading left down a ramp.  The way we determine this is by checking the specific value of the tile we're on.  If (tileUnderneath == 16 || tileBelow == 16) is true, then run the code specific to that direction.  Tile 16 in my game is the slope that gets taller as you move right.  Later in development when I add variations of the same slope, I'll keep them grouped together so I can just check for a specific range instead of the value 16.

A slope that gets taller as you move right.
Tile 16.

Now we know specifically what direction we're moving and we can start adjusting the y position of the player accordingly.  Basically, we want to adjust Otto's y position based on where his x position on the tile is.  For each pixel that Otto moves on the x axis, he will move up or down one pixel as well.  Since we can't just use the players x and y attributes, I had to make new variables that use the same position as tileUnderneath.  The tileUnderneath variable basically acts as our collision point.  The variables I made are called slopeIncrementX, slopeIncrementY, and slopeTemp, although for now we will not worry about slopeIncrementY.  You can add this code in right after the conditional that determines if you're on a slope.  Mine looks like this:
slopeIncrementX = Math.floor((player.x + 5) % 16);
On each pass through the code, there are a few things that could have occurred for us to be heading down this sloped tile at the moment.  One, we could have just entered a slope tile from a standard tile; two, we could have entered from another slope tile; or three, we could have been moving along the same sloped tile.  First off, we'll check to see if we were not already on a slope.  I made a boolean variable called onSlope to keep track of this; it's main purpose is to help control jumping while on a slope.  Anyway, on the first pass, it will return false, which means that we just freshly entered a slope and we are on the right-most edge of the tile.

Normally you would think that since we just entered the slope and this is the first pass through the code, our slopeIncrementX value should be 15 because we are on the right-most part of the tile (keep in mind it goes 0-15 and my tiles are 16x16).  Something I noticed while writing and bug testing this code was that sometimes it actually skips to 14 and possibly even 13 and the only reason I can think why is because the player was moving so fast that he actually passed over that pixel from one pass through the code to the next.  If we don't account for this possibility, Otto will eventually glitch through the slope and then bad things will happen!

Anyway, what we do right now is pretty simple.  Since we are heading down, we want to add to Otto's y attribute.  How much?  My calculation ended up looking like this: player.y += (15 - slopeIncrementX);  15 is the right-most pixel on the tile, so we would want to subtract from that to get the difference between it and slopeIncrementX.  This will also account for the weird bug described above.  However, we only run this specific code once, because it doesn't work in the other situations.  After adjusting y, I set slopeTemp equal to the current value of slopeIncrementX.

We've determined we're on a slope, so onSlope is now true.  At this point in time, we want to only adjust the player's y attribute whenever slopeIncrementX changes.  We will keep track of this by storing the previous value of slopeIncrementX in another variable called slopeTemp.  My conditional simply looks like this: (slopeTemp != slopeIncrementX).  Any time this conditional is true, we check for the other two possibilities.  Otto is either entering a new slope tile from another sloped tile or he is moving along the same sloped tile.

If entering a new slope, then the conditional (slopeTemp <  slopeIncrementX) is true.  Normally, since we are moving left on the slope, the value of slopeTemp is higher than slopeIncrementX.  Therefore, we know that if that is no longer the case, we must have entered a new slope tile.  To compensate, we adjust the player's y value like so: player.y += ((16 + slopeTemp) - slopeIncrementX); and then again set slopeTemp equal to slopeIncrementX.

If moving along the same sloped tile, we do not need to compensate for anything and can simply declare: player.y += slopeTemp - slopeIncrementX;  Again, don't forget to set slopeTemp equal to slopeIncrementX afterward.  Here is what all of the code looks like together:

Code for moving left down a 45 degree slope.

There isn't much different about moving left and up a slope.  The main thing to remember is that you'll be subtracting from the player's current y value instead of adding to it.  Also, you'll be checking for a different value for tileBelow or tileUnderneath.  The value of my other 45° sloped tile is 17.  Another important difference is what you check for initially.  Because we're moving upward, we're going to have tiles that are directly in front of our player.

Instead of just checking to see if (tileBelow == 17 || tileUnderneath == 17), we must put our tileAhead variable to use!  It might be easier to use two variables for this one (for instance having a tileLeft and tileRight instead of tileAhead) because otherwise you constantly have to update the value of tileAhead.  Do whatever you find works best for you.  The conditional I use is (tileUnderneath == 17 || (tileBelow == 17 && tileAhead == 17)) because tileAhead is only important when we're checking tileBelow.  There is a situation where you get to the top of a ramp, so tileBelow may still be 17, but the tile ahead is 0 (no tile), so the slope code shouldn't run, which is why we only run it if tileAhead is 17 as well.  Here's my code for this direction:

Code for moving left up a 45 degree slope.

You can probably tell I worked on this direction first because of all the additional comment lines.  When starting out working on a new piece of code, descriptive comments like this can help you keep track of just what the heck is going on in your code.  Trust me when I say every bit of help counts.

Everything is pretty much the same for the right direction, except you're going to be checking for different values for the tiles and your calculations for the player's y position will be different because you're starting on the left side of the tile instead of the right.  You should be able to figure out how to write the code yourself based on what I've shown you so far.  Besides, just copying my code down isn't going to help you at all.  What's important is understanding how a chunk of code actually works, why it works, and its relation to the rest of the code around it.

The other two situations to check for are one where Otto is jumping into a sloped tile on the ceiling and one where he is falling onto a sloped tile.  If neither of these situations apply, then we run our normal collision code.  I won't discuss the jumping/falling situations today, but in addition to running the normal slope code we need to set onSlope to be false and reset our maximum velocity for y, like so: player.maximumVelocity.y = 454;

Now you should have a good idea on how to implement slopes in your own game!  Keep in mind though, this certainly isn't the only way to implement sloped tiles in your tile-based game.  I've mentioned before that I'm working on a different version of sloped tiles and it currently is a lot more concise than this version.  It's also been easier to implement the code for landing on a slope.  You'll see it eventually and then you'll understand why it's a better solution.  However, next post I am going to talk about how to implement jumping while on a slope and how to implement 22.5° slopes using the current technique, so you'll have to wait on the new version!

As always, thanks for reading!  See you next time!


Powered by Blogger.