GameMaker Sonic-Like 360° Slope Movements?

Status
Not open for further replies.
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
 
M

mhouckod

Guest
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
Any luck on success with this? I’m new to coding and game development, but having been working on it for a few months now and done some tutorials. So my knowledge is limited. I’ve been following along with some YouTube videos but haven’t had much success with getting the player object to scale the half pipes correctly. I’m also using a physics/trigonometry based approach.

I would appreciate any help!
 
Any luck on success with this? I’m new to coding and game development, but having been working on it for a few months now and done some tutorials. So my knowledge is limited. I’ve been following along with some YouTube videos but haven’t had much success with getting the player object to scale the half pipes correctly. I’m also using a physics/trigonometry based approach.

I would appreciate any help!
Hi.

I found this forum again after looking for help on this very subject just now, so here I am. Sadly, I haven't really made much progress (yes I'm still trying nearly 2 years later 😭).

I understand the concepts from the Sonic Retro Physics Guide, and I've seen the recently updated sections, but the code structure/ordering needed is just making me feel hopeless. I got it working (kind of) using a technique I learned from the Clickteam Fusion Sonic Worlds Engine, but the slope sensors are very shaky, and that makes me feel like I shouldn't bother using that method because if the sensors are shaking, it's probably going to be really problematic once I implement other features such as jumping or rolling. Not to mention the technique is different from what's outlined on the Sonic Retro Physics Guide, so because of that, I feel I won't be able to follow other sections of that guide for implementing various features to my engine since my 360 degree physics are done differently.

What I'm saying is, I wish I could help, but sadly I don't have much to share. When I eventually figure it out and get a working example, I plan on posting the project file here and documenting as much as I can just to provide a very rough starting point so people will have a project file they can look at and compare the slope physics code to what's explained on the Sonic Retro Physics Guide. It will be very bare bones, but it will be exactly what I've been looking to find for the past few years.
 
I guess I may as well ask for more help since I haven't really found the solution yet (or at least cannot implement it for the life of me). I come back to this every few months and it's so frustrating always coming close to a solution but always having at least one thing make what I have unusable.

I've had a look through more engines made in other game creation tools. Some of them are either too complex for me to understand with massive amounts of code, or simply don't do what I'm looking for.


Here is the problem with my latest attempt:

When moving down a slope, the player switches from mode 1 (on the floor) to mode 4 (on the left wall). However, when the game changes the mode of the player, they're constantly switching between being in mode 1 (on the floor) to mode 4 (on the left wall, aka the slope the player is on in this clip). If the player continues down the slope, eventually this will be corrected and they will be in mode 4 without constantly swapping between mode 1 and 4.

Here is a video demonstration of the issue.
(please note the value of the variables, and look at the sensor bar behaviour)

I think I see the problem, but I don't know the solution. I believe the problem has to do with the code for changing the player's mode based on their current angle. It's probably something to do with the player constantly meeting the criteria for changing to mode 1 and to mode 4 every other frame.
GML:
if (angle >= 45 && angle < 135)
    mode = 2; // right wall
else if (angle >= 135 && angle < 225)
    mode = 3 // ceiling
else if (angle >= 225 && angle < 315)
    mode = 4 // left wall
else
    mode = 1; // floor

// a is the left floor sensor, b is the right floor sensor
// with _x2 and _y2 being the end point of each floor sensor
angle = point_direction(a_x2, a_y2, b_x2, b_y2);
If you think you can help, please feel free to let me know.
I've left the values of the variables in the top left of the video to help diagnose the issue. Also, I'm willing to share the entire project if it will help you understand the problem.

Thank you!
 
im not an expert but i think that ur problem is that the player is checking the angle even if he is not moving so prob you should do a condition that the angle should be point_direction(a_x2, a_y2, b_x2, b_y2) when the player is moving?
 

Yal

🐧 *penguin noises*
GMC Elder
I sure love when a topic returns from the grave repeatedly lol

After having been a software engineer for a while I'd probably recommend a thing we use called "hysteresis".
  • First of all, use a "state machine" approach and only run checks for the neighboring orientations - if you are in ground mode, only check for "start running up a wall" and "start running down a slope". This should prevent the player from going through unexpected transitions.
  • Make the swap thresholds a bit more extreme - instead of 45 and 135 degrees to start running on a right wall, use 35 and 145. But the thresholds for ending this mode should be more extreme in the other direction, e.g. 55 and 125 degrees. (Draw this out on paper, I'm too sleepy to double-check if the maths is correct)
Basically, instead of swapping exactly at the breakpoint, you swap 10 degrees past it, in both directions - so there's a 20 degree deadzone where no swapping happens and within which you could be in either state, depending on which state you were in when reaching this rotation. This is what hysteresis is, having a little buffer zone so that small noisy changes won't trigger constant state changes.
 
Status
Not open for further replies.
Top