I moved to a new URL! Check it out!

Colliding with Slopes?!

Colliding with Slopes?!
In my last blog post I talked a little bit about how I've implemented slopes in my latest project, but I only talked about how I was actually importing them from Ogmo Editor into Flashpunk and not about how I'm actually using them for platforming. In this post I'll attempt to explain how I actually use slopes in my movement system, which means my platformer characters can walk up and down them without any problems.

The first thing to keep in mind is that all of my slope code only really works with slopes that increase or decrease by 1 pixel. I could rework some of it to make it work with a step of any size that the programmer could define, but for now 1 step is all I really need.

Pixel Sweepin'


The first thing to know is how I actually go about moving my platformer characters, and other moving objects around my game world. I use a method that I refer to as pixel sweeping. Basically whenever an object moves in my games, I move it one pixel at a time and check for collisions at each step! This might sound a little crazy to some folk, but this is the most reliable way I've been able to do stuff like platforming and other moving objects and still collide with even the tiniest pixel of a floor or wall. I've been using this technique since the very beginning of Bonesaw: The Game.

Flashpunk actually has some functions built in that allow you to move things in this manner! You should check out the moveBy() function on the "Entity" class. This function will move the entity by an integer value, and has the option to sweep which will check each pixel for a collision. When there is a collision, it will call the functions moveCollideX() and moveCollideY() which you can override to figure out how you want to respond to collisions in each axis.

The Slope Code


In order to do slopes, I needed to basically make my own moveBy() function so that I could inject my slope code into it at the right spot. First, here's my logic behind it. We'll start with just moving horizontally, and in this example we'll say that I'm only moving to the right.

If the pixel to my right is occupied (there's a collision) then check to see if the pixel to the right, and up one pixel is free. If that's the case, then move one pixel to the right, and one pixel up. We've just moved up a slope by one pixel!

If the pixel to my right is open, then we need to check for a downward slope below! So check to see if the pixel to the right, and down one pixel is open AND check to see if one pixel to the right and two pixels down is occupied. If that is the case, then move one pixel to the right and down one pixel. That's moving down a slope! If we checked one pixel to the right, and down one pixel, and that pixel was occupied, then there is no slope, so just move one pixel to the right.

Actual Source Code


Sorry if that's a little difficult to follow. If it helps any, here is my complete function for moving in the X direction. Note that these functions are still very much a work in progress, so some stuff might not make sense... (like I don't even use that sweep boolean at all? haha!)
public function moveX(collideAgainst:Array, speed:int, sweep:Boolean = true ):void {			
var t:String;
var e:Entity;

_move.x += speed;

var movement:uint = Math.abs(_move.x);
var step:int = FP.sign(_move.x);

while (movement >= speedScale) {
var clear:Boolean = true;

e = collideTypes(collideAgainst, x + step, y);
if (e) {
if (!collideTypes(collideAgainst, x + step, y - 1)) {
y -= 1;
e = null;
}
if (!collideTypes(collideAgainst, x + step, y + 1)) {
y += 1;
e = null;
}
}
else {
if (!collideTypes(collideAgainst, x + step, y + 1)) {
if (collideTypes(collideAgainst, x + step, y + 2)) {
y += 1;
}
}
}

if (e) {
collisionX(e);
movement = 0;
_move.x = 0;
}
else {
x += step;
movement -= speedScale;
_move.x = FP.approach(_move.x, 0, speedScale);
}

}

}

And since I do some checks for slopes in the y direction as well, here's how that looks:
public function moveY(collideAgainst:Array, speed:int, sweep:Boolean = true ):void {
_move.y += speed;

var movement:uint = Math.abs(_move.y);
var step:int = FP.sign(_move.y);

while (movement >= speedScale) {
var clear:Boolean = true;


var e:Entity = collideTypes(collideAgainst, x, y + step);
if (step < 0) {
if (e) {
if (!collideTypes(collideAgainst, x + 1, y + step)) {
x += 1;
e = null;
}
if (!collideTypes(collideAgainst, x - 1, y + step)) {
x -= 1;
e = null;
}
}
}


if (e) {
collisionY(e);
movement = 0;
_move.y = 0;
}
else {
y += step;
movement -= speedScale;
_move.y = FP.approach(_move.y, 0, speedScale);
}
}
}

Both of these functions go on my "Actor" class, which extends "Entity" for some extra functionality.

As far as slopes go in the y direction, it can be a little weird. Right now the way that I use slopes in the Y direction is if you're moving upwards and hit into a slope, you'll slide along it to maintain your upward speed. This feels a little bit more natural to me, but not nearly as necessary as the horizontal movement slope code.

Don't Neglect Downward Slopes!


One thing I want to note is the IMPORTANCE of having code that sticks your objects to slopes that move downward. You might think you can get away with just code that moves your characters up slopes and let gravity do the work for the downward slopes but you are WRONG!

When you try and let gravity do the work for downward slopes you end up with an awkward bouncy walk down every slope. The worst part about this is that your character will actually be leaving the ground every time they walk off the slope to let gravity take them downward. That means whenever the player attempts to jump during this time that they are temporarily off the ground, they will fail. (That is unless you have some sort of jump forgiveness frames, but even with that there is still the issue of the awkward bouncing down the slope.) So that is why I put in the code for checking downward slopes and sticking to those as well.

That's All


Alright I think that's all I got for slopes! If you want to know more feel free to post a comment or whatever. I'll try to answer any question you might have, or if you want to see more code or more examples I can try that too.

Comments

Shawn McCool
Shawn McCool
Great article, I like this method and I like the concept of pixel sweeping. I've been using a more traditional collision detection algorithm and I wasn't extremely happy with its reliability.

Do you notice any negatives to extending the capability to check for slopes more than 1 pixel in difference?
Posted May 31st 2013 2:57 AM
new comment!

Post your comment!

Name
Email
Comment