GameMaker Studio 2D Platformer Tutorial

A blank paper square standing on a sage sheet on an oak desk, with a pencil and a small clay pebble in window light.

Take the square that already slides left and right, add one floor under it, pull it down with gravity, and let it hop when you press Space.

This is the floor-and-jump slice: one floor object, one gravity value, one landing check, and one jump key, added on top of movement you already have, with no walls, no hazards, no camera, no level design, and no export. When your square drops onto the floor, walks along it, and hops only while it is standing on it, you are finished.

You need the project from page 81, where obj_box moves while you hold an arrow key. If you do not have it, build that first. This page does not repeat it.

GameMaker’s editors get updated regularly. The function names below match the current GameMaker manual. If a button sits somewhere else on your screen, search the manual for the feature name instead of guessing.

Step 1: Remove up and down

Once gravity controls the square’s height, the up and down arrows cannot also push it, or the two will fight. Open the Step event of obj_box and delete the two lines that check vk_down and vk_up. It should now hold only this:

var spd = 4;

if (keyboard_check(vk_right)) x += spd;
if (keyboard_check(vk_left))  x -= spd;

Run the game once. Left and right should still work, and up and down should do nothing.

Step 2: Make a floor

Create a new sprite and name it spr_floor. Fill it with a color that stands out from your square. Keep it the same size as your square’s sprite, so a row of them lines up neatly.

Then create a new object, name it obj_floor, and give it spr_floor. It needs no events. A floor only has to exist.

GameMaker checks collisions using each instance’s collision mask, not the colored pixels you see. A new sprite gets a default rectangular mask that covers the image, which is what you want here. The manual’s page on the Sprite Editor explains the collision mask settings if you ever need to change them.

Step 3: Put the floor in the room

Open your room. Select the instance layer, the same one that holds your square.

Drag obj_floor into the room several times to make a row near the bottom of the screen. Line the pieces up edge to edge with no gaps. Turning on the room grid makes this easier.

Then move your square up so it starts well above the floor. Leave clear air between them. If the square starts overlapping the floor, the landing code will not have a clean starting point and the square may stick.

Step 4: Add a Create event

In obj_box, press Add Event and choose Create. The manual describes the Create event as running once, when the instance first appears. That makes it the right home for values the Step event will change every frame.

Type:

vsp = 0;
grav = 0.5;
jump_speed = 9;

vsp is the square’s vertical speed: how many pixels it will move up or down this step. It starts at zero.

grav is how much downward speed gets added every step.

jump_speed is how hard the square pushes off the floor.

Notice the names. The manual lists gravity and vspeed as built-in variables that every instance already has. If you set them, GameMaker applies its own motion on top of yours. Using your own names like grav and vsp keeps you in charge, and it keeps the two systems from stacking.

Step 5: Add gravity and landing

Go back to the Step event. Under your left and right lines, add:

// Gravity
vsp += grav;

// Land on the floor
if (place_meeting(x, y + vsp, obj_floor)) {
    while (!place_meeting(x, y + sign(vsp), obj_floor)) {
        y += sign(vsp);
    }
    vsp = 0;
}
y += vsp;

Here is what each piece does.

vsp += grav; makes the square fall a little faster every step. That growing speed is what makes a fall look like a fall instead of a slow, even drift.

place_meeting is the collision check. The manual describes it as checking a position for a collision using the collision mask of the instance running the code. In plain words, it asks: if the square were moved here, would it overlap a floor? It answers true or false, and it does not actually move anything.

The if line looks ahead to where the square is about to go. If that spot is clear, the square moves the full vsp at the bottom.

If that spot hits the floor, the while loop moves the square one pixel at a time toward it. sign(vsp) turns the speed into 1 or -1, so each nudge is a single pixel in the right direction. The loop stops when one more pixel would overlap. The square ends up resting on the floor’s top edge instead of sinking in or stopping short.

Then vsp = 0; kills the fall. Without that line, speed would keep piling up while the square sits still.

Run it. The square should drop, speed up as it falls, and stop cleanly on the floor. Left and right should still work.

Step 6: Add the jump

Now add the jump. Put these lines above the gravity line, right after your left and right lines:

// Jump
var on_floor = place_meeting(x, y + 1, obj_floor);
if (on_floor && keyboard_check_pressed(vk_space)) {
    vsp = -jump_speed;
}

on_floor looks one pixel below the square. If a floor is there, the square is standing on something.

keyboard_check_pressed fires only on the step the key goes down. You met it on page 81 when you swapped it in and watched the square stop. Here that one-time behavior is the point. One press gives one jump.

vsp = -jump_speed; sets the vertical speed upward. In GameMaker, y grows downward, so a negative value means up. Gravity then slows the rise, flips it, and brings the square back down to the floor, where the landing code from Step 5 catches it.

Run the game and press Space. The square should hop and land.

Step 7: Test four things

Click inside the game window so it has keyboard focus, then check:

  1. The square falls at the start and lands on the floor without sinking into it.
  2. Holding left or right walks it along the floor.
  3. Pressing Space while it stands on the floor makes it hop.
  4. Pressing Space again while it is in the air does nothing.

Then walk off the end of your floor row. The square should drop out of the room. That is correct. There is nothing under it, and catching a fall is a lesson for another day.

Tune the feel

The three Create event values control everything about the hop. Change one at a time, run, and feel the difference.

Raise grav and the jump gets shorter and snappier. Lower it and the jump gets floaty.

Raise jump_speed and the square goes higher. Lower it and the hop gets small.

There is no correct pair. Write down the numbers you like.

If it does not work

The square falls through the floor. Check the spelling of obj_floor in every place_meeting call. Then check that the floor sprite has a collision mask.

The square stops in midair above the floor. The floor’s mask may be bigger than its visible image. Look at the mask settings in the Sprite Editor.

The square sticks or freezes at the start. It probably begins overlapping the floor. Move it higher in the room.

The square floats up and never comes back. Look for a missing vsp += grav; line, or a Create event value typed as gravity instead of grav.

Stop here

You took a square that only slid sideways and gave it weight, a floor, and a hop. That is the base of every platformer: speed that changes every step, a look-ahead collision check, and a jump that only works from the ground.

Save the project. Tomorrow, delete the floor and jump code and rebuild it from memory before you add anything new.