March 2, 2012

Game Maker: Walking Up/Down Slopes


As an avid Game Maker user for 1.5 years now, I've run into quite a few problems that are rarely if ever addressed by tutorials or documentation. This post will hopefully be the first of many to address some of the problems I've come across and how I've solved them. If you're using Game Maker to craft your own game, or even if you are using another engine, I hope you find this helpful. Now, on to today's problem...

Problem:
I want my character to walk up and down angled slopes, but my character just collides with things and then stops, even if it's only a 1 degree incline! Help!


Solution:
This problem can be handled in many ways, but the easiest I've found is to use what I've dubbed an "angle sweep". In simple terms, your character will, every step, check a number of points at various angles in front of it to determine where it can walk.


How do we do that? Luckily for you, I've written a script to take care of it! I've included two versions. One has a ton of comments to explain exactly what the script is doing, and the other doesn't.


I've written out the script below so you can see what you're getting yourself into before you download, and I've included a video so you can see the script in action. Have fun!



/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////

// Script: Walking up/down slopes.
// Written by Seth Coster of Stoz Studios (www.stozstudios.com)
// Paste this into a script named something like "scr_Walk".
// Call this script in the key presses of your character which are for movement.
// When calling the script, use scr_Walk("left") to move left, and scr_Walk("right") to move right.
// Note: This script assumes the character has a variable called "movespeed" which determines how many pixels the character moves at a time. Use 10 as your base movespeed and tweak it from there.

// First we check which direction to walk.
if argument0 = "left" {
   dir = -1
   }
else if argument0 = "right" {
     dir = 1
     }

// Now that we have the direction, it's time to do the angle sweep. Since this will occur in a loop, it sweeps over a crapload of angles every step.

anglesweep = -90+40*dir; // This is the starting point of the sweep, in degrees.
anglerem = 100; // We're going to use a 100-degree sweep.
pathclear = 0; // "pathclear" is a toggle switch. If we find a path to walk, it gets set to "1" and we break the loop.

while (anglerem >= 0 && pathclear = 0){
      anglesweep += 5*dir; // We're doing 5-degree jumps in our sweep.
      anglerem -= 5; // Decrease "anglerem" by the degrees in each angle interval.

// Using the lengthdir function, we grab the x,y coordinates of the target location found for the current angle.
      groundspotx = x+lengthdir_x(movespeed,anglesweep)
      groundspoty = y+lengthdir_y(movespeed,anglesweep)

// Then, we check if the spot is free and if there's ground beneath it.
      if place_free(groundspotx,groundspoty) && !place_free(groundspotx,groundspoty+5)
             pathclear = 1 // If so, HOORAY! The path is clear for walking. If not, "pathclear" will stay at 0 and the loop will end after checking its 100 degree sweep.
      }

// The loop is now finished. Time to check if we have clear ground to walk on, and if we are already on the ground.
if (pathclear == 1 && !place_free(x,y+1)){
  // First, change the character's X and Y coordinates to the destination.
   x+=lengthdir_x(movespeed,anglesweep)
   y+=lengthdir_y(movespeed,anglesweep)
   move_contact_solid(270,15) // This will drop the character to contact with the ground if he ended up a few pixels above it.
   vspeed = 0 // Not required, but useful at times.
   hspeed = 0 // Not  required , but useful at times.
}
// If we failed the "path clear" test, we check to see if we're airborne. If so, we just move right/left in midair.
else if place_free(x+lengthdir_x(movespeed, 90-90*dir), y)
     x += dir*max(movespeed-abs(hspeed), 0)
// Last if we aren't airborne and have no clear path to walk, we'll just move to contact the closest solid obstacle.
else{
     move_contact_solid(90-90*dir, max(movespeed-abs(hspeed), 0))
     movingleft = 0
     movingright = 0
}



/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
// COMMENT-FREE VERSION

// Script: Walking up/down slopes.
// Written by Seth Coster of Stoz Studios (www.stozstudios.com)
// Paste this into a script named something like "scr_Walk".
// Call this script in the key presses of your character which are for movement.
// When calling the script, use scr_Walk("left") to move left, and scr_Walk("right") to move right.
// Note: This script assumes the character has a variable called "movespeed" which determines how many pixels the character moves at a time. Use 10 as your base movespeed and tweak it from there.

if argument0 = "left" {
   dir = -1
   }
else if argument0 = "right" {
     dir = 1
     }

anglesweep = -90+40*dir;
anglerem = 100;
pathclear = 0;

while (anglerem >= 0 && pathclear = 0){
      anglesweep += 5*dir;
      anglerem -= 5;
      groundspotx = x+lengthdir_x(movespeed,anglesweep)
      groundspoty = y+lengthdir_y(movespeed,anglesweep)
      if place_free(groundspotx,groundspoty) && !place_free(groundspotx,groundspoty+5)
             pathclear = 1 
      }

if (pathclear == 1 && !place_free(x,y+1)){
   x+=lengthdir_x(movespeed,anglesweep)
   y+=lengthdir_y(movespeed,anglesweep)
   move_contact_solid(270,15)
   vspeed = 0
   hspeed = 0
}

else if place_free(x+lengthdir_x(movespeed, 90-90*dir), y)
     x += dir*max(movespeed-abs(hspeed), 0)

else{
     move_contact_solid(90-90*dir, max(movespeed-abs(hspeed), 0))
     movingleft = 0
     movingright = 0
}



18 comments:

  1. Shouldn't the line if (anglerem >= 0 && pathclear = 0) break because you used the assignment (=) instead of the "is equal?" (==) operator? Or does '=' do both in gamemaker?

    ReplyDelete
    Replies
    1. gml (game maker language) is like a really dummed down prog. language as in you dont have to do the '==' while testing stuff and u dont have to add a ';' after every freaken line. it was made for begginers, but u can do the'==' and use semi-colons if u want to, but it doesnt affect it, its just good practice for the real languages.

      Delete
  2. Game maker doesn't distinguish between the two; I've started working with C# so my brain must be on the fence between the two conventions. Doesn't affect the code in Game Maker, luckily!

    ReplyDelete
  3. Thank you very much! this helped me a lot! I had some trouble implementing gravity with this, but found an easy way. If someone is having the same issues as I had, create an object called obj_solidparent and make it parent for all solid objects. Next, just add this to your player's step event:

    //calculates gravity and vspeed
    if collision_rectangle(bbox_left,bbox_bottom,bbox_right,bbox_bottom+vspeed+1,obj_solidparent,true,true)
    {
    gravity=0
    vspeed=0
    }
    if !collision_rectangle(bbox_left,bbox_bottom,bbox_right,bbox_bottom+vspeed+1,obj_solidparent,true,true)
    {
    gravity=1
    }

    ReplyDelete
  4. Hi, how do i implement jumping in both directions? Cause when i jump, i cant turn left or right in the air. i put hspeed=4 for right and hspeed=-4 for left in my controls movement. In the air its ok, but on the ground it messes up with the var movespeed.

    ReplyDelete
  5. Your use of "hspeed" is probably messing with the above script. Try cutting out the use of hspeed entirely, and instead just use the movespeed variable to control the character movement. Using hspeed is tricky because it sets your object in motion until you tell it to stop, so your character is probably getting stuck in/on walls and the ground.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
    2. The problem is that i dont know where do i put the command to jump left/right. i have tried to put it in my step event like this:
      if keyboard_check(vk_right){
      scrpt_slopes("right");
      movespeed=4;
      }
      }
      but its worse than using the hspeed.

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Best script I have come across yet! Nice work, Seth! :)

    ReplyDelete
  8. Thanks for shared Seth Coster. this will be useful in a future to me =)

    ReplyDelete
  9. How do I set the movespeed variable?

    ReplyDelete
  10. hey man
    i used your script and i gotta admit i had to tweak it a lot to make it work and basically it changed my whole way of programming, but in a good way, i really learned a thing or two about gm (things i was doing terribly wrong) so i rewrote my whole character code in a more "modular" way
    ive been trying to do this kind of thing for years and always gave up for getting frustrated with one bug after the other and with your help i got my engine nice and smooth in a single day, i would have already been happy to have just 45º slopes and now i can basically make my guy walk anywhere!!!
    so yeah basically im just writing to give you a huge THANK YOU!!! :D

    ReplyDelete
  11. what if you dont want to use a script and the player object doesn't move left or right. just up and down, and the incline moves horizontally

    ReplyDelete
  12. This comment has been removed by the author.

    ReplyDelete
  13. some times my player stuck in something (wind maybe?!), anyone know why this? is the player mask or something like that? Only when i go up.

    ReplyDelete
  14. I have found another solution of this problem, which might be a little bit easier than this one.
    But thx anyway, this script inspired me.

    ReplyDelete