> | | | | | | | | | | > Power Tools for the Slope

Power Tools for the Slope

Posted on Wednesday, August 15, 2012 | No Comments

I feel like the past three posts I've been trying to mold these slopes with primitive technology.  It's as if I found a small rock and stick with which I've been trying to chisel away at these blocks in order to make sloped tiles.  It's taken forever and the end result is messy!  Wouldn't it be great if there were already better tools out there that could be utilized to make this job a lot easier?  Well, in case if you haven't picked up on where I'm going with this yet, there just so happens to be a set of tools like this!

It's called the Flixel Power Tools library by Photon Storm and it does a lot of amazing things that really round out the generic Flixel capabilities.  We'll only be discussing one class from the library in this post, but feel free to check out their website because they have demos of every class included within the library.  As you may recall, the main issue with sloped tiles in a Flixel tile-based game is that collisions are only checked against bounding boxes, which is what led us to writing all that bloated slope code from before.  It would be much better if we had a pixel-perfect collision check to see whether or not the player and the slope tile are actually colliding!

Fortunately, the Power Tools contain a class called FlxCollision that does just that!  Now, we can't just plug it in with our tilemap and expect it to work.  Our tilemap is made of Tile classes, but FlxCollision only works with FlxSprite classes.  So what do we do?  Well, we need to use the same starting approach as last time by disabling collision detection for all sloped tiles in our tilemap.

level.setTileProperties(i, FlxObject.NONE);
Remember this?  We want to keep this, at least for now, so that the sloped tiles in our tilemap are ignored when checking for normal collisions.  The basic idea is to make copies of the sloped tiles as FlxSprites instead of Tiles so that we can run the FlxCollision check.  You could theoretically leave all the sloped tiles out of your main tilemap and then generate all your slopes on the fly as FlxSprites, but I haven't looked into this approach and I think it might be more taxing on the game than what I'm about to describe (although the difference may be negligible).

What I've done is created a generic FlxSprite named slopeTest that is going to load the graphic of the specific type of slope we're currently on.  When we're ready to run collision detection, we simply update the graphic to match that of the slope we're on and then set the coordinates accordingly.  This way, we're only dealing with a single object instead of loading up a bunch of different objects for each slope on the screen!  As an added bonus, once we've got our code working we can set the new slope's visible attribute to false so that it doesn't even appear on screen, but still works normally.

Now, I have to mention this code isn't working 100% yet.  I haven't had the time to tweak it to perfection.  What does work perfectly so far is jumping from and falling/landing onto sloped tiles (ironic, I know).  The code at this point does not always position slopeTest correctly, so you end up falling through the slope at times.

Anyway, the main variables we'll be working with should be familiar to you, namely pointTest, tileUnderneath, and tileBelow.  I've also added tileLeft and tileRight to replace tileAhead.  We also have a variable called dirX to keep track of which direction Otto is facing.

The first thing we want to do is determine when the slope code should run.  It's very similar to before.  We'll run it if tileUnderneath is a slope, or if tileLeft or tileRight is a slope AND onSlope is true AND we're currently not jumping on a slope (jumpingOnSlope = false), or if tileBelow is a slope and we ARE currently on a slope (onSlope = true).  My code looks like this:
(tileUnderneath > 15 || (((tileLeft > 15 || tileRight > 15) && onSlope) && !jumpingOnSlope) || (tileBelow > 15 && !onSlope))
The first thing we want to do is set onSlope to true.  Next, we want to set the x and y values of our pointTest variable (this is what we use to test our collisions).  Set pointTest.x = player.x + 5; and set pointTest.y = player.y + 29 or pointTest.y = player.y + 13, depending on whether Otto is big or small.

Then, we want to determine where we should place the slopeTest FlxSprite.  We'll declare a couple of variables for the new x and y values we want and we do so by stating: var x:uint; var y:uint;.  Start off by setting x to the same value we've been using for our collision point with Math.floor((player.x + 5) / 16) and then multiply it by 16 or whatever your tile width may be .  Do the same thing for y, but change the value based on whether or not Otto is small or large.

Now, this is currently where I'm working to fix the bug with slopeTest's positioning.  It seems to work pretty well if I'm moving left up the slope, but once I move right and head down the same slope it gets glitchy.  So I'll skip this part for now, but just know that the point of this code right here is to get the slopeTest variable into position.  Once we've calculated the correct coordinates, assign them to slopeTest's x and y values.

Now comes the fun part -- our pixel perfect collision test!  FlxCollision has a method called "pixelPerfectPointCheck", so we want to start out by using that.  First, check for the obvious collision, which is whether or not our collision point is currently colliding with slopeTest.  The code should look like this:
(FlxCollision.pixelPerfectPointCheck(pointTest.x, pointTest.y, slopeTest))
If that returns true, we want to set the player's maxVelocity.y to zero.  Now, we'll run a while loop that checks the same exact thing (meaning it will return true at least once) and each time through the loop we will move the player AND our pointTest variable up one pixel.  Also, I set stopJump to false here to make sure that jumping and falling from slopes work properly.

If our collision point is no longer colliding with the slopeTest, we need to set the maxVelocity.y back to the default value, so we'll add in another conditional.  In this one, we'll check to see if there is NOT a collision between our collision point + 1 and the slopeTest.  This essentially checks to see if the player is in the air above the slope.  Here's my code:
(!FlxCollision.pixelPerfectPointCheck(pointTest.x, pointTest.y + 1, slopeTest))
If this returns true, we set the player's maxVelocity.y back to the default value, which is 454 in my game.   We need to run one more check now.  If we're going up or down a slope, tileUnderneath is going to be zero.  So, this time we'll check to see if tileUnderneath is zero and also if either tileLeft or tileRight is a slope.  If so, this means we are currently on a slope either heading up or down and we simply set the player's maxVelocity.y to zero again.

If the slope code never ran, we reset the player's maxVelocity.y value and set onSlope and jumpingOnSlope to false, as well as run our normal collision code (FlxG.collide(player, level)).  As you can see, this code is much more concise than before!  The pixel perfect code should look something like this:

Actionscript 3 code for pixel-perfect slope collisions.
The jump code is pretty similar, but I will go over it next post as I have already covered a lot today.  I don't have a published version of this code for you to test since it doesn't work 100% correctly yet, but rest assured I will have one up as soon as it's fixed!  As always, feel free to comment if you have any questions or just want say hi!

Thanks for reading!

Leave a Reply

Powered by Blogger.