Introducción al Desarrollo de Videojuegos con GameMaker · Module 4: Controls, Movement, and Player Handling

Implementing a Basic Jump

Lesson 19 of 60 · 60 min

Also available in: ES

Introduction: The Physics of Jumping in 2D Video Games

In two-dimensional platformer design, the jump is almost always the core mechanic around which the entire player experience revolves. A jump that feels imprecise, excessively floaty, or stiff undermines gameplay satisfaction, no matter how polished the visual art or deep the narrative. To make a jump feel natural and rewarding, you cannot simply move the character upward at a constant speed and then return them to the ground through uniform translation. Instead, you need to simulate the foundational principles of classical kinematics in a simplified, controlled manner: an instantaneous upward impulse followed by continuous gravitational acceleration.

In the previous lessons of this module, we configured keyboard input reading and horizontal character movement using a custom horizontal speed variable (`hsp`), along with flipping the sprite visually based on facing direction using `image_xscale`. In this lesson, we take the logical next step: introducing vertical displacement along the Y-axis. We will calculate vertical speed (`vsp`) step by step, apply an instantaneous upward impulse when the player presses the jump key, and subject that impulse to progressive deceleration caused by a constant downward force of gravity.

GameMaker's Coordinate System and the Vertical Axis

Before writing a single line of jump code, it is essential to understand how GameMaker interprets two-dimensional space inside a Room. In standard Cartesian geometry taught in school, the origin (0, 0) typically sits at the bottom-left corner or the center of a grid, with positive Y values pointing upward. In video game engines and digital 2D screen rendering, this convention is inverted.

In GameMaker, the origin point (0, 0) of any Room is located at the top-left corner. The axis orientations operate as follows:

AxisPositive Direction (+)Negative Direction (-)In-Game Effect
Horizontal Axis (X)To the rightTo the leftMoving laterally forward or backward
Vertical Axis (Y)DownwardUpwardFalling/descending (+) or ascending/jumping (-)

This inverted Y-axis is one of the most common stumbling blocks for beginners. Moving upward on the screen requires subtracting pixels from the instance's `y` coordinate, whereas descending or falling requires adding pixels. Therefore, when making a character jump upward, you must assign an initial negative vertical speed. As gravity acts over time, you add a positive value to that speed on every frame, transitioning the velocity from a high negative number (rapid ascent), to zero (the apex of the jump), and finally to positive values (freefall back toward the ground).

Jump Kinematics: Impulse, Gravity, and the Parabola

The trajectory traced by a body moving upward against constant gravity is a parabola. This jump arc consists of three distinct phases that must be modeled within the per-frame Step Event:

1. Initial Impulse: At the exact moment the player presses the jump key, an instantaneous negative vertical speed of substantial magnitude is applied to the character (for example, `vsp = -9`). At this precise moment, the character moves upward at maximum vertical velocity. 2. Deceleration and the Apex: On each successive frame, gravity adds a small positive increment to vertical speed (for example, `vsp += 0.4`). As frames tick by, vertical velocity approaches zero: -9, -8.6, -8.2, ..., -0.6, -0.2, 0. When `vsp` reaches exactly zero, the character reaches the apex—the peak of the trajectory—remaining suspended in the air for an infinitesimal fraction of a second. 3. Accelerated Fall: Immediately after the apex, gravity continues to accumulate. Now `vsp` becomes positive (+0.4, +0.8, +1.2...), and the character descends, gaining downward speed until colliding with a solid surface or reaching a predefined terminal velocity cap.

Schematic diagram of a 2D jump parabola showing upward impulse force, downward gravity, the jump apex at zero speed, and the coordinate system where negative Y points upward.
Figure 1: Parabolic trajectory of a jump. The negative initial impulse decays under constant gravitational acceleration until reaching the apex (vsp = 0) and beginning downward descent. · AI-generated image

Variables Needed in the Create Event

To build this behavior in a clean, scalable way, avoid hardcoded numeric literals ("magic numbers") scattered across your logic. Instead, declare descriptive variables in the Create Event of your player object (`obj_player`). We will keep our previously declared horizontal variables (`hsp` and `walk_speed`) and add dedicated variables for vertical motion.

/// obj_player Create Event

// --- Horizontal movement variables (from previous lessons) ---
hsp = 0;
walk_speed = 4;

// --- Vertical movement and jumping variables ---
vsp = 0;              // Current vertical speed (pixels per step)
grav = 0.4;           // Constant gravitational acceleration
jump_speed = 9;       // Upward jump impulse magnitude (pixels upward)
vsp_max = 12;         // Maximum terminal fall velocity (prevents falling too fast)

// --- Temporary ground reference variable ---
// Note: Module 5 covers formal solid tile and object collisions.
// For now, we use a fixed Y baseline as a temporary test floor.
ground_y = 500;

Let's break down the role of each newly declared vertical variable:

• `vsp`: Represents the vertical offset the object will experience during the current frame. Initialized to 0, meaning the character starts vertically at rest. • `grav`: The per-step increment added to `vsp`. A value between 0.3 and 0.6 typically yields a classic platformer feel that is neither too floaty nor too abrupt. • `jump_speed`: Determines jump strength. Defined as a positive number (9); when applied as an upward impulse, we negate it (`-jump_speed`). • `vsp_max`: Terminal velocity. In an uncapped physics simulation, falling from a great height accumulates so much downward velocity that the character could tunnel through thin platforms in a single step or become uncontrollable. Capping `vsp` guarantees stability. • `ground_y`: A temporary floor altitude within the Room (500 pixels from the top edge) so our character has a surface to stand on before we implement full collision systems with solid instances.

Input Handling: keyboard_check vs. keyboard_check_pressed

In the first lesson of this module, we explored GameMaker's various input functions. For jumping, the distinction between `keyboard_check` and `keyboard_check_pressed` is technically critical:

• `keyboard_check(key)` returns `true` continuously on every frame that the physical key remains held down. This is ideal for walking or running laterally, where movement should sustain as long as the key is pressed. • `keyboard_check_pressed(key)` returns `true` exclusively on the single frame in which the key transitions from released to pressed. If the player continues holding the key down for several seconds, the function returns `false` on every subsequent frame until the key is released and pressed again.

💡 Golden Rule: Always use keyboard_check_pressed to trigger a jump impulse. If keyboard_check were used instead, the code would reapply the jump impulse on every single frame while the spacebar is held, rocketing the character upward like a missile without being affected by gravity until the key is released.

Structuring Physics in the Step Event

The Step Event runs continuously at the game's room speed (typically 60 frames per second). Code inside this event should follow a strict, logical pipeline of gathering input, running calculations, and applying motion:

1. Input Gathering: Read the keyboard or gamepad state into local boolean variables. 2. Horizontal Processing: Compute `hsp` based on directional inputs. 3. Ground Detection: Check whether the character is currently standing on solid ground. 4. Jump Processing: Apply the upward impulse only if the jump input fired and the character is grounded. 5. Gravity Application: Increase `vsp` by `grav`, clamping to `vsp_max`. 6. Coordinate Updates: Update the instance's built-in `x` and `y` coordinates by adding `hsp` and `vsp`.

Here is the complete, annotated Step Event implementation for `obj_player`:

/// obj_player Step Event

// --------------------------------------------------------
// 1. INPUT GATHERING
// --------------------------------------------------------
var _key_right = keyboard_check(vk_right) || keyboard_check(ord("D"));
var _key_left  = keyboard_check(vk_left)  || keyboard_check(ord("A"));
var _key_jump  = keyboard_check_pressed(vk_space) || keyboard_check_pressed(ord("W"));

// --------------------------------------------------------
// 2. HORIZONTAL MOVEMENT
// --------------------------------------------------------
var _move_dir = _key_right - _key_left;
hsp = _move_dir * walk_speed;

// Flip sprite horizontally based on movement direction
if (_move_dir != 0) {
    image_xscale = _move_dir;
}

// --------------------------------------------------------
// 3. TEMPORARY GROUND CHECK AND JUMPING
// --------------------------------------------------------
// Check if the character is at or below the floor baseline
var _is_on_ground = (y >= ground_y);

if (_is_on_ground) {
    // Grounded: lock vertical position to floor and zero out residual speed
    y = ground_y;
    vsp = 0;
    
    // Check for jump input while grounded
    if (_key_jump) {
        vsp = -jump_speed; // Apply negative upward impulse
    }
} else {
    // Airborne: apply downward gravitational acceleration
    vsp += grav;
    
    // Cap fall speed at terminal velocity
    if (vsp > vsp_max) {
        vsp = vsp_max;
    }
}

// --------------------------------------------------------
// 4. POSITION UPDATES
// --------------------------------------------------------
x += hsp;
y += vsp;

Let's examine how each logic block operates. The condition `var _is_on_ground = (y >= ground_y);` evaluates whether the player has touched or passed the baseline floor height. When true, we eliminate any lingering downward velocity (`vsp = 0`) and prevent the character from sinking by snapping `y = ground_y`.

Inside that grounded condition, we evaluate `if (_key_jump)`. Because jumping is gated strictly behind `_is_on_ground`, we solve the classic "infinite jump" exploit: the player cannot jump while already airborne. Once the impulse triggers (`vsp = -jump_speed`), on the very next frame `y` updates via `y += vsp`, making `y` smaller than `ground_y`. Instantly, `_is_on_ground` evaluates to false, directing execution to the `else` branch where gravity acts until the parabolic arc brings the character back to earth.

The Mathematical Relationship Between Jump Height and Gravity

Beginner developers often tune jump feel by guessing numbers at random until it "feels okay." However, understanding the underlying math behind vertical kinematics allows you to calibrate maximum jump height and airborne duration with pinpoint precision.

In the simplified discrete physics used by platformers, the maximum height ($h$) in pixels reached by a jump can be approximated using the standard kinematic formula:

$$h = \frac{v_{jump}^2}{2 \cdot g}$$

Similarly, the estimated time ($t$) in frames required to reach the jump apex is given by:

$$t = \frac{v_{jump}}{g}$$

If our game sets `jump_speed = 9` and `grav = 0.4`, the time required to reach the apex is $9 / 0.4 = 22.5$ steps (roughly one-third of a second at 60 FPS). The maximum height reached above the starting baseline will be $9^2 / (2 \cdot 0.4) = 81 / 0.8 \approx 101.25$ pixels.

This mathematical relationship is invaluable for level design: if a level designer places a platform 120 pixels above the floor, you know mathematically that with your current values, the player will never clear that ledge, regardless of skill. You must either raise `jump_speed` or lower `grav` to make that platform reachable.

Target Game StyleSuggested jump_speedSuggested gravGameplay Feel
Classic / Retro Platformer (Mario, Mega Man)8.5 to 10.50.40 to 0.55Snappy, responsive, clear arc, controlled height.
Cinematic / Heavy Platformer6.0 to 7.50.45 to 0.60Lower jump, pronounced sense of body weight.
Low-Gravity / Sci-Fi / Floaty5.0 to 7.00.12 to 0.20Very gentle descent, long airtime and hang time.
Frenetic / Precision Platformer (Celeste)11.0 to 13.00.70 to 0.90Fast arc, instant feedback, high reflex demand.

Practical Example 1: Implementing a Jump with Fall Clamping

Let's look at how GameMaker's built-in `clamp` function can streamline vertical speed limits into a single expressive line of code, simplifying terminal velocity management and removing unnecessary nested conditions.

Practical Example 2: On-Screen Debug Overlay

When fine-tuning jump arcs, watching physics variables update in real time on screen as your character moves and jumps is immensely helpful. The Draw GUI Event is built for this purpose.

Testing and Common Troubleshooting

During initial implementation, you may encounter unexpected physics quirks. Here are the most frequent issues and how to fix them:

1. Infinite airborne jumping: Occurs when `_is_on_ground` is not strictly evaluated before applying the jump impulse, or when the player's `y` position is reassigned to the floor height on every frame unconditionally. Ensure `vsp = -jump_speed;` is nested inside a true ground check. 2. Character jitters or vibrates on the floor: Happens when gravity is still applied while grounded. The character drops a fraction of a pixel, passes the floor line, gets snapped back up to `y = ground_y`, and repeats every frame. The fix is to set `vsp = 0` and skip gravity entirely whenever the character is grounded. 3. The jump feels floaty or like "walking on the moon": `grav` is set too low relative to `jump_speed`. Increase `grav` to the 0.4–0.55 range and calibrate `jump_speed` accordingly to produce a sharper, punchier arc.

Hands-On Exercises

Complete these three progressive exercises to reinforce and test your understanding of vertical movement logic:

Exercise 1: Intentional Physics Calibration Modify `grav` and `jump_speed` in the Create Event of `obj_player` to create two contrasting jump profiles. First, configure a floaty "Moon / Underwater" jump that reaches 150 pixels of height but takes at least 40 frames to hit its apex. Next, adjust values for a snappy, retro-style jump that reaches a maximum of 75 pixels in only 14 frames. Document your mathematical derivations in code comments. Verification: Use the on-screen debug monitor from Practical Example 2 to check the peak `y` coordinate reached right as `vsp` flips from negative to positive.

Exercise 2: Double Jump with an Integer Counter Using an integer variable declared in the Create Event (such as `jumps_remaining = 2;`), update your Step Event logic so the character can trigger a second jump impulse in mid-air after leaving the ground. When the character touches the floor again (`y >= ground_y`), reset the variable back to its maximum (`jumps_remaining = 2`). Verification: The player should be able to jump from the ground and execute exactly one additional impulse while airborne. Pressing jump a third time in mid-air should do nothing until the character lands.

Exercise 3: Hybrid Input Handling (Keyboard and Gamepad) Expand your local `_key_jump` input variable to accept input from both the spacebar (`vk_space`) or W key (`ord("W")`) on the keyboard, and the bottom face button of a standard gamepad in slot 0 (using GameMaker's native function `gamepad_button_check_pressed(0, gp_face1)`). Verification: Connect a compatible gamepad and verify that pressing the A button (Xbox layout) or Cross button (PlayStation layout) triggers the jump with the exact same responsive parabolic trajectory as the keyboard spacebar.

Summary

  • In GameMaker, the Y-axis is inverted relative to standard Cartesian coordinates: negative values indicate upward movement, while positive values indicate downward movement.
  • A basic platformer jump combines two kinematic forces: an instantaneous upward impulse (negative vertical speed) and constant downward deceleration from gravity (continuous positive speed additions).
  • Always use keyboard_check_pressed for jumping to ensure the impulse triggers once per physical keypress rather than repeating on consecutive frames.
  • The peak or apex of a jump occurs at the precise moment vertical velocity (vsp) reaches 0, after which positive velocity causes the character to fall.
  • Using variables like vsp_max or the clamp function protects against runaway fall speeds that could cause collision clipping or tunneling issues.
  • Understanding kinematic formulas like h = (v^2) / (2 * g) lets designers calculate exact jump clearances before placing platforms in a room.

Practise what you learned

Exercise 1 · 1 point

Why must a negative value be assigned to vertical speed (`vsp`) for a character to jump upwards in GameMaker?

You are practising without an account: nothing is saved. Sign up to keep your progress.

Exercise 2 · 1 point

Order the logical sequence of steps executed in the Step Event to manage a character's jump and vertical physics:

  1. 1Update spatial coordinates
  2. 2Apply gravity
  3. 3Vertical jump processing
  4. 4Capture inputs
  5. 5Ground detection
You are practising without an account: nothing is saved. Sign up to keep your progress.

Exercise 3 · 1 point

To trigger a jump, the function should always be used, as it returns `true` only on the exact frame the key is pressed. If were used instead, the impulse would trigger continuously as long as the key is held down, causing the character to fly upward uncontrollably.

You are practising without an account: nothing is saved. Sign up to keep your progress.

Sign in to save your progress — Sign in