GMS 2 Sonic-Like 360° Slope Movements?

Hello!

I'm trying to get my player object to be able to walk up and around slopes similar to how Sonic is able to run through loops.
My player can currently walk left and right.

I've tried implementing 360° Slope Movements onto my player object for the past 4 days straight but to no avail. I'm aware that sin and cos are necessary, but I'm not sure how to use them, or why exactly I have to use them. I also know I need to calculate the angle of the floor relative to my player's rotation, but again I believe that requires sin and cos (which I'm not sure exactly how to apply).

I've tried the following:
The YouTube tutorial was great, but I messed up the code somewhere and it's hard for me to know the fix if I don't understand how everything is working to make the slopes possible.

If anybody has any ideas (even if it's not an entire solution) please share it. I'm really at a loss here and as far as I know, this is the hardest obstacle my game is facing.

Thanks :)
 

chamaeleon

Member
You need sin() and cos() because you are working with a point location and a direction, and you place things in an x/y coordinate system. So in the general case, given some current position, sin() helps you find out the amount you move up or down in y when you you attempt to move in a certain direction/angle, while cos() gives you the amount you move left or right in x. lengthdir_x() and lengthdir_y() hides a tiny bit of calculation from you (but not a whole lot...) and gives you the necessary x and y offset you need to apply to the current position of your instance given a distance to travel and which direction to travel, but they do nothing different than the raw math you'd use using cos() (used behind the scenes in lengthdir_x()) and sin() (used behind the scenes in lengthdir_y()).

Because all the math is based on straight lines (a starting point and a direction/angle), I would imagine that some of the work the engine has to do is to possibly fudge the movement or the next angle/direction after it has been calculated to ensure the player stays on the ground and doesn't fly off due to accumulating differences between calculations and a perfect zero-error movement path (which is generally unattainable given inherent limitations in computer math computations).
 
Last edited:
You need sin() and cos() because you are working with a point location and a direction, and you place things in an x/y coordinate system. So in the general case, given some current position, sin() helps you find out the amount you move up or down in y when you you attempt to move in a certain direction/angle, while cos() gives you the amount you move left or right in x. lengthdir_x() and lengthdir_y() hides a tiny bit of calculation from you (but not a whole lot...) and gives you the necessary x and y offset you need to apply to the current position of your instance given a distance to travel and which direction to travel, but they do nothing different than the raw math you'd use using cos() (used behind the scenes in lengthdir_x()) and sin() (used behind the scenes in lengthdir_y()).

Because all the math is based on straight lines (a starting point and a direction/angle), I would imagine that some of the work the engine has to do is to possibly fudge the movement or the next angle/direction after it has been calculated to ensure the player stays on the ground and doesn't fly off due to accumulating differences between calculations and a perfect zero-error movement path (which is generally unattainable given inherent limitations in computer math computations).
I believe I understand it a bit more now.

If I'm understanding you correctly, cos will tell me how much to move the player left or right, on top of their horizontal movement.
So if my player is moving to the right and goes up a slope, cos will be a negative value that's added to their horizontal movement.
And sin is basically the same thing but for the y-axis.

If that's correct, then my next step is finding the angle. I'm thinking of having a script that has 2 lines scanning the ground under the player to find out the angle of the slope using the point_direction function. Though I'm not quite sure as I believe I need to make the 2 sensor lines rotate around with the player.

I'm not exactly sure how to put it all together though
 
It's been almost a year, but I'm back again and am trying to get this to work.
I have a simple demo where my player is able to curve up a wall, which just about works.

The problem is, my player slowly gets pushed into the wall they're curving onto every time their angle changes while they're moving.
I've included a .gif that displays the issue.

If anybody has any ideas, please let me know. I've included all (I believe) the necessary code below for people to understand how my player moves.
Also, I've tried looking into other projects where people got 360° Slope Movements, but there's so much in these project that I don't even know where to start.


Here is my code:

Player Step Event:
GML:
mask_index = sprite1x1;

p_move();
p_physics();
angle = find_angle();
The above calls the "p_move" script, which is just used to add speed to my player's horizontal velocity upon user input.
Then, the "p_physics" script is called to move my character based on their horizontal velocity.
Then, the angle of the floor beneath them is retrieved using the "find_angle" function.

p_physics Script:
GML:
// limits
if hsp > hspl hsp = hspl;
if hsp < -hspl hsp = -hspl;
if vsp > vspl vsp = vspl;
if vsp < -vspl vsp = -vspl;

acos = cos(degtorad(angle));
asin = sin(degtorad(angle));

// h movement
if hsp > 0
{
    x += acos * hsp;
    y -= asin * hsp; 
}
if hsp < 0
{
    x += acos * hsp;
    y -= asin * hsp; 
}
The above is the code that moves my player, using the angle they're currently facing.

find_angle Script:
GML:
// left point start
x1 = round(x - (acos * radius));
y1 = round(y + (asin * radius));

// right point start
x2 = round(x + (acos * radius));
y2 = round(y - (asin * radius));

done1 = 0;
done2 = 0;

for (i = 0; i < 16; i++)
{
    if !done1
    {
        x1 += round(asin);
        y1 += round(acos);
        if collision_point(x1, y1, oSolid, true, true)
            done1 = true;
    }
    if !done2
    {
        x2 += round(asin);
        y2 += round(acos);
        if collision_point(x2, y2, oSolid, true, true)
            done2 = true;
    }
    if done1 && done2 break;
}
return round(point_direction(x1, y1, x2, y2));
The above is the code to get the currently angle located at the player's feet.
The "radius" variable is equal to 10 (created in the player's create event).

Thank you for reading. Any ideas are very much appreciated :)
 

Attachments

chamaeleon

Member
When your character is following an arc, it is not a hsp or vsp variable that modifies x or y that should increase or decrease to go faster or slower, it is the angle, while the distance from the "center" of the arc remains constant. When you multiply acos and asin by hsp you are simply pushing the character that much further in the direction from the "center" of the arc instead of following the arc. You need to know where the center is for any given arc the character is on, use a constant length (the radius), and determine what angle change per step makes for a good movement speed.
 
When your character is following an arc, it is not a hsp or vsp variable that modifies x or y that should increase or decrease to go faster or slower, it is the angle, while the distance from the "center" of the arc remains constant. When you multiply acos and asin by hsp you are simply pushing the character that much further in the direction from the "center" of the arc instead of following the arc. You need to know where the center is for any given arc the character is on, use a constant length (the radius), and determine what angle change per step makes for a good movement speed.
Thank you for your reply.

When you say that my character should use their angle to follow an arc instead of modifying hsp and vsp, what do you mean? How would I use the angle to change what value to add to the x and y coordinates?

Also, what do you mean by "center of the arc"? Is that the halfway point in-between where the player would begin running on and end running on the arc?

And for when you say I need to know where the center of a given arc, is that something I calculate? Or would that be defined in a variable in the arc?
 

chamaeleon

Member
Thank you for your reply.

When you say that my character should use their angle to follow an arc instead of modifying hsp and vsp, what do you mean? How would I use the angle to change what value to add to the x and y coordinates?

Also, what do you mean by "center of the arc"? Is that the halfway point in-between where the player would begin running on and end running on the arc?

And for when you say I need to know where the center of a given arc, is that something I calculate? Or would that be defined in a variable in the arc?
In my hypothetical implementation there would be a flag that indicates whether I'm travelling along the arc or not. If I am, x and y are (given the arc's center point and radius, and an angle_change that makes sense for the game).
Code:
x = arc_center_x + lengthdir_x(arc_radius, angle);
y = arc_center_y + lengthdir_y(arc_radius, angle);
angle += angle_change;
hsp/vsp only makes sense to use when x and y change independently of each other. This is not the case for a circular motion where x and y are directly tied to each other through trigonometry.

The center is located not on the halfway point between the entry and exit. In your image, the center point should be the intersection of the two lines formed by drawing a line up from the ground where the arc starts, and a line from wall where the arc ends. Essentially, your arc is a quarter of a circle. I can't tell you what data you have to work with, but if the arc is truly a proper arc of a circle, and if the arc is exactly the size of a square sprite, the upper left corner of the sprite would be the arc center.

I'm just going by an implementation like this because you're using sin and cos which should only make sense when trying to do an exact path. If you're trying to do a physics based approach, you don't need to pay any attention to what I'm saying.
 

Yal

🐧 *penguin noises*
GMC Elder
I don't know how useful it would be to me though as reading through other's engines to understand what is going on is difficult for me (even with comments). Though thanks for the suggestion
You don't need to understand it to use it in your games, though... that's my job :p

I remember seeing similar issues during development of Fast Furry Framework, though... I think it's a mix of pixels' boundaries when rotating being ill-defined and rounding errors. Genesis Sonic solved this by having a normal "uphills and downhills only" approach and then rotating Sonic's axis of reference at 45-degree transitions (so you could be in normal mode, right wall mode, ceiling mode and left wall mode) with full-360 rotation as visual candy that didn't affect gameplay. I don't remember if I went this route in FFF or just used precise collisions to move you out of terrain if rotation put you inside it, but it probably doesn't hurt to do a check to see if you're currently inside terrain and if so move outside it after moving.
 
In my hypothetical implementation there would be a flag that indicates whether I'm travelling along the arc or not. If I am, x and y are (given the arc's center point and radius, and an angle_change that makes sense for the game).
Code:
x = arc_center_x + lengthdir_x(arc_radius, angle);
y = arc_center_y + lengthdir_y(arc_radius, angle);
angle += angle_change;
hsp/vsp only makes sense to use when x and y change independently of each other. This is not the case for a circular motion where x and y are directly tied to each other through trigonometry.

The center is located not on the halfway point between the entry and exit. In your image, the center point should be the intersection of the two lines formed by drawing a line up from the ground where the arc starts, and a line from wall where the arc ends. Essentially, your arc is a quarter of a circle. I can't tell you what data you have to work with, but if the arc is truly a proper arc of a circle, and if the arc is exactly the size of a square sprite, the upper left corner of the sprite would be the arc center.

I'm just going by an implementation like this because you're using sin and cos which should only make sense when trying to do an exact path. If you're trying to do a physics based approach, you don't need to pay any attention to what I'm saying.
Ah, I understand now what you mean by the center of the arc. Though I do very much appreciate the suggestion, I am going for a physics based approach (sorry if that was unclear). Mainly because I don't want to be restricted to just half pipes, but instead can have the character able to scale any type of shape (as long as the edges aren't too steep).

You don't need to understand it to use it in your games, though... that's my job :p

I remember seeing similar issues during development of Fast Furry Framework, though... I think it's a mix of pixels' boundaries when rotating being ill-defined and rounding errors. Genesis Sonic solved this by having a normal "uphills and downhills only" approach and then rotating Sonic's axis of reference at 45-degree transitions (so you could be in normal mode, right wall mode, ceiling mode and left wall mode) with full-360 rotation as visual candy that didn't affect gameplay. I don't remember if I went this route in FFF or just used precise collisions to move you out of terrain if rotation put you inside it, but it probably doesn't hurt to do a check to see if you're currently inside terrain and if so move outside it after moving.
Oh neat! I think this 4 axis idea might be the right approach for me considering that's what was done in the Genesis Sonic games. That being said, how would I get from one mode to the other?
I believe that once the player goes up a slope and reaches an angle of 45 degree while on the normal mode, they would shift to the right mode.
I guess what I'm asking is how would I get my character to go up the slope in the first place (say they're in the normal mode, moving to the right while on a flat floor, and eventually going up a slope)?

Sorry if that's confusing. I'm trying to wrap my head around all these ideas and circular math is new to me
 

Yal

🐧 *penguin noises*
GMC Elder
That being said, how would I get from one mode to the other?
I believe that once the player goes up a slope and reaches an angle of 45 degree while on the normal mode, they would shift to the right mode.
I guess what I'm asking is how would I get my character to go up the slope in the first place (say they're in the normal mode, moving to the right while on a flat floor, and eventually going up a slope)?
Normal slopes is easy... the simplest solution is to check forwards and down (x + xspeed, y + SLOPESTEPMAX) to see if it's free of collisions, and if so move there. If not, gradually move the vertical position you check at upwards until you either find a valid spot, or abort the loop because you've reached SLOPESTEPMAX distance upwards (this state means you're trying to move into a wall).
(Try to get this to work flawlessly before going to the next step).

Also note that moving off a ledge will trigger the maximal downhill movement with this code; you might want to treat this case differently to avoid having the player Falcon Kick downwards at 500 miles per hour in the off chance they move off a ledge while holding down the "move forwards" button.

To get the Sonic approach, you just need 4 versions of this code, which treats "forwards" and "upwards/downwards" differently. For instance, when running up a wall whose surface faces left, "forwards" is up, "down" is to the right, and "up" is to the left.

The last thing you need is a way to change between the different states when you're running up a slope... which is pretty easy, the hard part is detecting when you're on such a slope. You want to rotate one step when trying to move up a slope steeper than 45 degrees uphill, and rotate one step in the other direction if trying to move down a slope steeper than 45 degrees downhill. The simplest way to detect this is if your vertical movement (as allowed by the slope code) is greater than your horizontal speed (remember that 45 degrees, horizontal and vertical components are equal), FFF and 16-bit Sonic both uses raycasts for greater precision but for simple cases the component check might be enough.


circular math is new to me
It's called "trigonometry", and it's the branch of maths that's the most useful in game development, so you'd better read up on the basics pronto :p
 
Normal slopes is easy... the simplest solution is to check forwards and down (x + xspeed, y + SLOPESTEPMAX) to see if it's free of collisions, and if so move there. If not, gradually move the vertical position you check at upwards until you either find a valid spot, or abort the loop because you've reached SLOPESTEPMAX distance upwards (this state means you're trying to move into a wall).
(Try to get this to work flawlessly before going to the next step).

Also note that moving off a ledge will trigger the maximal downhill movement with this code; you might want to treat this case differently to avoid having the player Falcon Kick downwards at 500 miles per hour in the off chance they move off a ledge while holding down the "move forwards" button.

To get the Sonic approach, you just need 4 versions of this code, which treats "forwards" and "upwards/downwards" differently. For instance, when running up a wall whose surface faces left, "forwards" is up, "down" is to the right, and "up" is to the left.

The last thing you need is a way to change between the different states when you're running up a slope... which is pretty easy, the hard part is detecting when you're on such a slope. You want to rotate one step when trying to move up a slope steeper than 45 degrees uphill, and rotate one step in the other direction if trying to move down a slope steeper than 45 degrees downhill. The simplest way to detect this is if your vertical movement (as allowed by the slope code) is greater than your horizontal speed (remember that 45 degrees, horizontal and vertical components are equal), FFF and 16-bit Sonic both uses raycasts for greater precision but for simple cases the component check might be enough.



It's called "trigonometry", and it's the branch of maths that's the most useful in game development, so you'd better read up on the basics pronto :p
Sorry for the late reply. I've been trying to go this route of using 4 modes like you've suggested, and I'm actually seeing results! I still got a lot of work to do but I think it's manageable.
My character is actually able to wrap around walls now. I need to work out the bugs and make it so the player can jump and land onto a slope properly. I'll return if I run into any major issue that stumps me.

Thanks! :D
 
Top