Moving Platforms – Horizontal and Vertical

Content's very old; It might be outdated or no longer valid.

Some adjustments required for GameMaker Studio 2.3

The following article is for pre 2.3 versions of GameMaker Studio 2. It doesn’t take advantage of functions, chained accessors, structs and other new features of GML/GMS2.3+ IDE. Unfortunately I’m unable to update the series for the foreseeable future, but downloading and importing the final project inside the new IDE should trigger the conversion.

It will run correctly. From there you could refactor and clean the code a bit to suit your taste, I guess.

MacOS Crash Update – 2020-04-19

A reader kindly reported this bug to YoYo Games (attaching the previous version of the project). You can read the bug report here: 0031657: macOS: Runner crashes during collision check in the attached project

It’s due to “bailing from a repeat within a with” and it’s marked as fixed for the upcoming version 2.3.0.

MacOS Crash Warning (see update above)

The following code will probably break on MacOS. Along with YoYo Games we are currently investigating the culprit. If I had to make a wild guess, I’d say that the MacOS runner has some weird bug about nested context and recursion that makes it lose track of who’s doing what, eventually crashing without any warning. I’m confident that YoYo will get back to me with some good news but in the meantime I’m publishing this article anyway… because this is how I make moving platforms 🤷‍♂️

I’m not saying this code is bug free. It might very well be a sneaky bug in my code. But to this date, I still haven’t found it. If you spot it, please point it out.

I’ve seen a lot of bad implementations of moving platforms in GameMaker Studio, especially the vertical moving platforms. Some rely on the fact that, due to gravity, the player will naturally follow a downward moving platform (which is odd, and wrong), whilst others don’t even try to explain the vertical moving platforms at all. Like they don’t exist.

I’m not sure what’s going on here. Maybe my code is super naive and I’m not doing it right either, but moving platforms don’t have to be difficult. They’re objects that live in the begin_step event; they do their collision checks, they can push or carry other objects (or squish them or whatever you decide) and… and that’s it, really.

The player will naturally collide with these platforms but only in the step event, when the platforms have already completed their movements so there won’t be any odd behavior. To entities, moving platforms are… not really moving at all. I’d say that entities are, to moving platforms, also not moving at all. Remember that they live in two separate events.

Basic platforms vs Advanced platforms

The following article will describe basic horizontal and vertical platforms which can push and carry the player. These are not advanced moving platforms, able to carry and push more than one item. Those kind of platforms need slightly more work in the collision detection area and the colliding instances must be handled differently. Don’t worry though, they will be covered in the next article. If you need moving platforms for the player though, the following article is what you need!

Fixes and cleanups

Some fixes before we begin

Before we begin I need to fix my movement code. Let’s create a platformer_init script and put these lines inside it. I introduce a couple of new variables here for clarity purposes. I like to know that xvel_int and yvel_int will always hold an integer number no matter what.

Also remember to call the platformer_init() script in the create event of the player.

///@func platformer_init()

xvel		= 0
yvel		= 0
xvel_int	= 0 // Integer x speed
yvel_int	= 0 // Integer y speed
xvel_fract      = 0
yvel_fract      = 0
This is the platformer_init script
From now on we use this platformer_init script on our moving objects

Also open the oPlayer object and slightly alter the movement lines in the step event to accommodate these changes.

// Use the xvel_int here
if !move_x(xvel_int, true)
    xvel       = 0
    xvel_fract = 0

// Use the yvel_int here
if !move_y(yvel_int)
    yvel       = 0
    yvel_fract = 0

There’s one last piece of code I need to alter: round_vel()

///@func round_vel()
///@desc Round the xvel/yvel while keeping track of fractions

xvel_fract += xvel;
xvel_int    = floor(xvel_fract);   // use xvel_int here
xvel_fract -= xvel_int;            // and here

yvel_fract += yvel;
yvel_int    = floor(yvel_fract);   // Use yvel_int here
yvel_fract -= yvel_int;            // and here

Other than using the new variables, I decided to go with flooring, instead of rounding. It should work either way but I prefer to know we always round down the velocities.

New collision scripts

coll_x and coll_y to simplify collision checks

I’ve introduced a couple of new scripts to avoid having to write collision checks over and over. Let’s see how they work. Passing a direction, will automatically select the correct side to check. Also the obj parameter is optional.

///@func coll_x(xdir, [obj])

var xdir = argument[0]
var obj = argument_count == 2 ? argument[1] : oWall

var side_to_check = xdir ? bbox_right + 1 : bbox_left - 1

return collision_rectangle(side_to_check, bbox_top, side_to_check, bbox_bottom, obj, false, true)
///@func coll_y(ydir, [obj])

var ydir = argument[0]
var obj = argument_count == 2 ? argument[1] : oWall

var side_to_check = ydir ? bbox_bottom + 1 : bbox_top - 1

return collision_rectangle(bbox_left, side_to_check, bbox_right, side_to_check, obj, false, true)

Ternary operator

The ternary operator is just a shorthand to write an if / else statement. It’s usually used in assignment statements and it works like this:

some_var = (condition) ? value_if_true : value_if_false

Moving Platforms

Let’s see how to implement uncomplicated moving platforms

oMoving object is a child of oWall. Object hierarchy is important for collisions.
A new object with an oWall parent

First of all create a new sprite sMoving (maybe duplicate the wall and give it another color) and assign it to a new object named oMoving. Let’s make this object a child of oWall. And voilà!

With this single action you’ve already taken care of every possible collision that might happen in the player’s step event. Indeed if you were to run the game now with a couple of these oMoving objects in the game, they’d be sitting still, of course, but the player cannot go through them. He can stand on them just like any other oWall. And from the player’s side of things, it’s going to stay this way.

“moving platforms” that the player cannot traverse. As solid as they can get…

Moving on

Let’s add some movement

Now let’s see what happens when we begin to move the moving platforms. Spoiler alert: they do not collide with the player.

Open the create event of the oMoving platforms and initialize the platformer by calling platformer_init(). Then open the begin step event and place the movement code inside it.

/// @desc move


// Let the instance decide what to do when it can't move
if !move_x(xvel_int, false)
    xvel       = -xvel    // Reverse the speed in case of collision
    xvel_fract = 0

if !move_y(yvel_int)
    yvel       = -yvel    // Reverse the speed in case of collision
    yvel_fract = 0

This is the very same code we have inside the oPlayer object slightly altered to let the moving platforms invert their speed when colliding with walls. Now, for the moving platforms to actually move we need to define an initial speed. We can do so in the room editor with the creation code.

Initial moving platforms speed is defined in the creation code of the room editor.
Double click the instance and open the creation code. Place the initial speed here (here we’ll set the vertical speed)

Creation code

Creation code is code that is run after the create event for each instance. Meaning that, as an instance is created, its create event runs, then its creation code runs and then GameMaker go on creating another instance and so on. This is a good place to override values initialized in the Create event.

If you run the game now, you end up with this. Disappointed? That’s exactly the result we expected though.

The moving platforms can go inside the player.

This is what might get some people confused. It’s true that the player cannot go inside the platforms but the platforms can go inside the player. While the player’s collisions are already set correctly (since the moving platforms are children of oWall), the oMoving platforms need some more work.

We can’t use the same movement and collision code that we use in the oPlayer object. Indeed those scripts don’t take into account collisions with the oPlayer itself (of course) so to the moving platforms, there is no collision at all. We need to define movement and collision detection specific for the moving platforms.


If those move_x and move_y scripts were complex enough to take into account each object’s own characteristics, they would work. For instance, if every object defined a list of what if could collide with and what it could carry or push, those script could read the list and then behave accordingly. But for the sake of this article, they’re not that complex. And I don’t want to complicate this too much. We’ll explore such generalized solutions in more advanced engines.

Platforms’ own movement scripts

move_platform_x and move_platform_y to the rescue

So let’s change the platform’s begin step event code into this, introducing move_platform_x and move_platform_y scripts.

In these scripts we take into account collisions with oPlayer from the sides and from above.

Let’s create them and let’s see how they work. First is the horizontal movement. The idea is to return false whenever the platform encounters and unmovable obstacle (such as walls or a stuck player).

/// @desc move


if !move_platform_x(xvel_int)
    xvel       = -xvel
    xvel_fract = 0

if !move_platform_y(yvel_int)
    yvel       = -yvel
    yvel_fract = 0

The collision from the sides may return false in case the player cannot move at all (e.g. squashed between a moving platform and a solid). Here is where you decide what to do in your game (I just return false and let the platform reverse its speed but you might as well kill the player). It’s also where the MacOS crashes, btw.

The collision with the player standing on the platform, on the other hand, does not return anything. This is because the platform doesn’t care about the player being unable to move. It will simply slide off its feet and continue its movement.

///@func move_platform_x(xvel_int)
///@arg xvel

var _xvel    = argument[0]
var _xdir    = sign(_xvel)

// Movement/Collision X
    // Colliding with solid
    if coll_x(_xdir)
        return false
    // Pushing the player
    var player_on_sides = coll_x(_xdir, oPlayer)
    if player_on_sides && false == move_x(_xdir, true, player_on_sides)
        return false // Squashed between solids
    // Carrying the player
    // Notice how we don't care if the player
    // can't move in this case. The underlying platform will
    // simply slide off its feet (hence we don't return false)
    var player_on_top = coll_y(-1, oPlayer)
    if player_on_top
        move_x(_xdir, false, player_on_top)

    // Finally move        
    x += _xdir

return true

Taming the vertical movement

With a trick

The following script is for vertical movement instead. If the platform is going upward, in case of player collision, we simply try to push it upward as well. No big deal. As usual we return false if the player is stuck (or you might kill it). Note: this is where the macOS crashes.

Here comes what might confuse someone. If we’re going downward, we move the player downward as well via its own move_y script. But if we’re carrying it on top of the platform, we need deactivate the platform during player’s collision checking.

If we don’t do this, the player would collide with the very platform that is trying to move it, before being able to move downward. This collision will leave the player behind in mid-air, making it bounce on the platform on its way down. That’s why we deactivate and reactivate the moving platform really quickly, just to remove the instance from the possible collisions.

It’s like saying “Not considering this very platform, try moving the player down 1px”.

///@func move_platform_y(yvel_int)
///@arg yvel

var _yvel    = argument[0]
var _ydir    = sign(_yvel)

    // Colliding with solid
    if coll_y(_ydir)
        return false

    // Going upward
    if !_ydir
        // Carry the player upward (lift it)
        var player_above = coll_y(_ydir, oPlayer)
        if player_above && false == move_y(_ydir, player_above)
            return false

    // Going downward
    if _ydir
        // Push the player downward if it's colliding from below
        var player_below = coll_y(_ydir, oPlayer)
        if player_below && false == move_y(_ydir, player_below)
            return false

        // Carry the player downward with the platform if it's standing on top of the platform
        var player_above = coll_y(-1, oPlayer)
        instance_deactivate_object(self)       // Dirty trick begins
        if player_above
            move_y(_ydir, player_above)
        instance_activate_object(self)         // Dirty trick ends

    // If everything went good, move
    y += _ydir

return true

And just like that, believe it or not, the moving platforms are done. There’s nothing else to do to have horizontal and vertical moving platforms capable of carrying and pushing the player around without odd, jerky movements.

If you are curious about more advanced platforms, able to carry more than one item, simply stay tuned for the next iteration about advanced moving platforms.

On returning false

As a reader pointed out, some of my scripts return 0 instead of return false. Most of the time it doesn’t make a difference but for clarity, I decided to replace all instances of return 0 with return false in scripts such as move_x, move_y and others. You can do a project-wide search and replace if you want to do so as well (or just download my project).

Buy Me a Coffee at

6 thoughts on “Moving Platforms – Horizontal and Vertical”

  1. Great tutorial! Instead of setting the moving platform’s x/y velocity in the instance’s creation code inside the room editor, you could set it in the “variables” menu.

    You can define an object’s “variables” in the object editor, then overwrite those values in the room editor per instance.

    • You’ll need to remove their declaration inside of platformer_init(), though, because it seems like the Create event runs after variables are assigned. Or, write the code so the velocity variables are only set if they don’t already exist, using something like:

      /// platformer_init()
      if !variable_instance_exists(id, “x_velocity”) {
      x_velocity = 0;
      if !variable_instance_exists(id, “y_velocity”) {
      y_velocity = 0;
      x_velocity_fraction = 0;
      y_velocity_fraction = 0;
      x_velocity_int = 0; // Integer x speed
      y_velocity_int = 0; // Integer y speed

      • Yep! I don’t use the variables GUI panel at all but I guess some might be more comfortable using it. I prefer to have init code in the create event and override vars in the creation code only because of habit, I guess, but it could be done 🙂

  2. Hi Nikles!

    Great tutorial, pretty neat ‘clean code’ vibes there 🙂
    Just one thing (of course) :3

    After I implemented your project to mine (because I had problems with moving platforms) and started using ‘normal’ sprites, I found out, that the movement in either directions are a little shaky. If I start to use whole numbers for velocities, the movement becomes smooth, so that was an easy fix to it, but I can’t use a whole number for gravity :/ So every time I jump with my character, there is a little bit of shakiness. Any thought about that? 🙂 Thank you, and for your cool tuts!

  3. Great tutorial, I confess that initially I don’t like to use objects as a collision source, I think it is better to leave this task for the tilemaps to do it, but I have to admit that their method seems more stable and less subject to small bugs that always happen.


    I’m trying to adapt to the standard. but I still have resistance

    • Thank you! Overall, tilemap collisions should indeed be faster (less overhead in running lots of objects). Of course moving platforms really are objects with their own behavior and they need their own events to run and react correctly. Maybe one could try and mix some tile collisions (e.g. for static walls and slopes) with some object collision (e.g. moving platforms). Personally, I tried and failed; I gave up and once I started using object collisions for everything, I found it to be pretty versatile. Using less, bigger objects can boost performance as well (or I guess out-of-view collision objects could be disabled).

      Having said that, there is no standard way of doing things. As long as it does what one intended it to do, it just works.

      Btw if you find any other interesting method you feel like sharing, I’m always willing to learn new things 🙂

      PS Sometimes I draw collisions with tiles in my room editor. Once the room starts, I spawn collision objects where collision tiles are, then simplify/merge collision objects with adjacent ones, then hide all the collision layers. So I still draw with tiles in the GMS room editor (it’s faster), and then use object collision at runtime.


Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.