Introducción al Desarrollo de Videojuegos con GameMaker · Module 10: Level Design, Cameras, and Game Progression
Configuring a Follow Camera in GameMaker
Lesson 46 of 60 · 50 min
Also available in: ES
Introduction to 2D Game Camera Systems
Up to this point in the course, most of our testing has taken place in single-screen setups where the entire action fits within a single fixed display. However, in classic 2D platformers (such as Super Mario World, Sonic the Hedgehog, or Celeste), the game world is substantially larger than what the player can see at any given moment. To allow players to explore underground caverns, mountain peaks, or long obstacle courses, the engine must decide with pinpoint accuracy which portion of the world to project onto the screen at each tick.
The system responsible for this task is the virtual camera. Far from being a mere aesthetic detail, the camera is a critical narrative and gameplay tool: it dictates pacing, provides anticipation for upcoming hazards, prevents blind jumps, and conveys physical impact through screen shakes, acceleration, and dynamic framing. In GameMaker, rendering what the player sees relies on a three-tier architecture: the Room, the Camera, and the Viewport.
Core Concepts: Rooms, Cameras, and Viewports
To avoid common beginner confusion, it is essential to clarify the distinction between these three fundamental GameMaker concepts:
| Concept | Definition | Primary Role in Game | Practical Example Dimensions |
|---|---|---|---|
| Room | The global physical space of the level where instances, layers, and collisions exist. | Holds the entire world explorable by the player from start to finish. | Width: 3840 px, Height: 1080 px |
| Camera | A mathematical data structure defining an (X, Y) position and a rectangular framing size within the Room. | Determines exactly which portion of the world is being captured on each frame. | Width: 480 px, Height: 270 px |
| Viewport | The physical region of the OS window or display screen where the camera's captured view is drawn. | Scales the camera's frame up to the user's native monitor or window resolution. | Width: 1920 px, Height: 1080 px (Uniform 4x scale) |
Think of the Room as a miniature diorama city built on a large table. The Camera is a film camera mounted on a tripod or dolly hovering over the table, framing an area of 480 by 270 millimeters. The Viewport is the television set in your living room displaying the feed sent by that film camera. If the camera pans to the right, the picture on the TV changes, even though the diorama itself never moves an inch.

Native Setup via the Room Editor
GameMaker provides a built-in, no-code mechanism to assign a follow camera directly from the Room Properties panel. While this approach has limitations in advanced commercial projects, it is the fastest way to understand how the underlying view subsystem operates.
To enable this system step by step in your platformer room:
1. In the Asset Browser, open your level room (for example, `rm_level_1`). 2. In the room properties panel on the left, expand the section titled **Viewports and Cameras**. 3. Check the **Enable Viewports** box. If this is unchecked, GameMaker will stretch or crop the entire room to fit the main window without using any cameras. 4. Expand the **Viewport 0** tab and check **Visible**. 5. Under **Camera Properties**, set the framing width and height. To preserve our retro aesthetic with crisp pixels and a 16:9 widescreen aspect ratio, set `Width: 480` and `Height: 270`. 6. Under **Viewport Properties**, set the display output size. You can use `Width: 1920` and `Height: 1080` (an exact 4x integer scale of the internal camera resolution). 7. Under **Object Following**, click the object selector and choose our playable character: `obj_player`. 8. Adjust the **Horizontal Border** and **Vertical Border** values. These represent the minimum distance (in pixels) the player can get to the edge of the frame before the camera starts moving. Set `Horizontal Border: 200` and `Vertical Border: 100`. 9. The **Horizontal Speed** and **Vertical Speed** fields determine the maximum speed at which the camera tracks the player. Setting these to `-1` forces the camera to lock onto the player instantly and rigidly.
💡 Important: Setting speed to -1 means every micro-movement and jump from the player is mirrored instantly on screen without dampening, which can cause visual fatigue and feel mechanically rigid. However, if you set a fixed tracking speed such as 4 or 6 pixels per frame, any movement faster than that (such as sprinting or falling) will cause the character to outrun the camera and leave the screen.
Because of these limitations in basic object following (lack of spring dampening, inability to lead the player's view, lack of programmatic screen shakes or dynamic zooming), professional 2D games generally bypass the Room Editor's built-in following in favor of a dedicated camera controller object written in GML Code.
The Camera Controller: obj_camera
To gain full control over the visual presentation, we will create a dedicated controller object called `obj_camera`. This object will be solely responsible for calculating optimal view coordinates, preventing the camera from revealing empty space outside the level, and applying smooth mathematical interpolation.
Create a new object in your Asset Browser and name it `obj_camera`. It does not require a sprite, as it acts purely as an invisible logic manager in the scene.
Initialization in the Create Event
In the **Create** event of `obj_camera`, we initialize the active virtual camera, declare our base resolution, and configure the viewport using GameMaker's camera functions. Add the following code to the Create event:
/// Create Event of obj_camera
// 1. Define base frame resolution (retro 16:9 aspect ratio)
ancho_camara = 480;
alto_camara = 270;
escala_ventana = 3; // Window desktop scale (480*3 = 1440, 270*3 = 810)
// 2. Create the internal GameMaker camera
camara = camera_create_view(0, 0, ancho_camara, alto_camara, 0, -1, -1, -1, 0, 0);
// 3. Enable viewports and bind the camera to Viewport 0
view_enabled = true;
view_visible[0] = true;
view_set_camera(0, camara);
// 4. Configure OS window size and drawing buffer (Application Surface)
window_set_size(ancho_camara * escala_ventana, alto_camara * escala_ventana);
surface_resize(application_surface, ancho_camara * escala_ventana, alto_camara * escala_ventana);
// Center the desktop window after resizing
alarm[0] = 1;
// 5. Tracking and interpolation variables
objetivo = obj_jugador;
suavizado = 0.1; // Interpolation factor (between 0.01 and 1.0)
// Center camera on target initially if it exists
if (instance_exists(objetivo)) {
x = objetivo.x - (ancho_camara / 2);
y = objetivo.y - (alto_camara / 2);
} else {
x = 0;
y = 0;
}
// Apply initial position directly to camera
camera_set_view_pos(camara, x, y);To ensure the game window centers properly on the player's desktop after calling `window_set_size()`, add the **Alarm 0** event to `obj_camera` with a single function call:
/// Alarm 0 Event of obj_camera
window_center();The Critical Event Choice: Step vs. End Step
One of the most common pitfalls in GameMaker development is placing camera follow logic in the standard **Step** event. Updating the camera position in Step introduces a temporal race condition determined by the internal instance execution order.
If `obj_camera` runs its Step event before `obj_player` processes its input, speeds, and collisions in its own Step event, the camera will position itself based on the player's previous frame coordinates. When the player moves an instant later, the final render will draw the character offset by one frame relative to the background. This produces an unpleasant shuddering effect known as camera jitter, noticeably degrading visual fluidity.
For this reason, camera positioning must ALWAYS run in the **End Step** event. In this phase, GameMaker guarantees that all gameplay entities have already resolved their velocities, physics collisions, and position integrations, allowing the camera to track the final, authoritative position of the player before any **Draw** events fire.
Smooth Tracking with Linear Interpolation (lerp)
Cinematic and responsive camera tracking avoids clamping the character rigidly to the center of the screen, opting instead for elastic dampening. In game development mathematics, this is accomplished using the `lerp()` function (short for *Linear Interpolation*).
The function `lerp(a, b, t)` takes a starting point `a`, a target destination `b`, and a normalized percentage or blend factor `t` (where 0 represents 0% and 1 represents 100%). Each frame, the function calculates an intermediate position by traveling a percentage of the remaining distance. As the camera gets closer to the target, the absolute distance traveled shrinks, producing an organic ease-out deceleration.
Room Clamping with clamp()
If the player walks toward the far left edge of a level (x = 0), centering the camera directly on their coordinates would cause the left half of the screen to reveal an empty black void outside the level. To prevent the camera from revealing areas beyond the room bounds, we use the mathematical function `clamp(val, min, max)`.
The minimum horizontal bound for the top-left corner of the camera is `0`. The maximum horizontal bound is not the full width of the room (`room_width`), but the width of the room minus the width of the camera (`room_width - ancho_camara`). Similarly, the vertical bounds range between `0` and `room_height - alto_camara`.
Implementing the End Step Event in obj_camera
Open `obj_camera`, add the **Step -> End Step** event, and enter the following thoroughly commented code:
/// End Step Event of obj_camera
// 1. Defensively check that the target instance exists
if (!instance_exists(objetivo)) exit;
// 2. Calculate ideal top-left coordinates to center the camera on the target
// We subtract half the camera width and height from the player's position
var _pos_x_destino = objetivo.x - (ancho_camara / 2);
var _pos_y_destino = objetivo.y - (alto_camara / 2);
// 3. Apply linear interpolation to glide camera smoothly toward target
x = lerp(x, _pos_x_destino, suavizado);
y = lerp(y, _pos_y_destino, suavizado);
// 4. Constrain position within current room boundaries
x = clamp(x, 0, room_width - ancho_camara);
y = clamp(y, 0, room_height - alto_camara);
// 5. Sub-pixel correction for retro pixel art (prevents texture shimmering and tearing)
// Passing floating-point numbers to view coordinates can cause visual artifacts
var _pos_x_final = floor(x);
var _pos_y_final = floor(y);
// 6. Update the active GameMaker camera position
camera_set_view_pos(camara, _pos_x_final, _pos_y_final);💡 Pixel rounding warning: Using floor() is essential for retro pixel art games. If you pass fractional floating-point values (like x = 124.382) to camera_set_view_pos(), single-pixel lines may shimmer, distort, or intermittently disappear depending on your display scaling.
The Clean Up Event and Memory Management
In GameMaker, cameras created dynamically in code via `camera_create_view()` allocate native memory outside the automatic garbage collector. If the player restarts the level or changes rooms and `obj_camera` is destroyed and recreated, orphaned cameras will persist in memory, leading to a memory leak.
To prevent memory leaks, add the **Clean Up** event to `obj_camera` and explicitly free the camera data structure when the instance is removed:
/// Clean Up Event of obj_camera
// Destroy dynamically created camera to free engine memory
if (camera_exists(camara)) {
camera_destroy(camara);
}Advanced 2D Platformer Camera Techniques
Once standard tracking and boundary clamping are established, you can enhance gameplay feel by implementing additional techniques that give the camera dynamic personality and better responsiveness.
Look-Ahead Tracking
In fast-paced side-scrollers, when the character runs to the right, what matters most to the player is not the empty space they just cleared behind them, but the upcoming terrain ahead. This technique is known as 'Look-Ahead' or predictive tracking.
We can introduce a horizontal offset based on the sign of the player's horizontal speed (`hsp`) or their facing direction (`image_xscale`). This shifts the camera target forward in the direction of movement:
/// Look-Ahead integration example in End Step of obj_camera
var _distancia_adelanto = 48; // Pixels camera leads ahead
var _offset_x = 0;
// Check player movement direction or facing side
if (objetivo.hsp != 0) {
_offset_x = sign(objetivo.hsp) * _distancia_adelanto;
} else {
_offset_x = objetivo.image_xscale * (_distancia_adelanto * 0.5);
}
// Add lead offset to target calculation
var _pos_x_destino = (objetivo.x + _offset_x) - (ancho_camara / 2);
var _pos_y_destino = objetivo.y - (alto_camara / 2);
// Apply interpolation and bounds clamping as normal...Screen Shake
A screen shake effect adds powerful visual feedback when the player takes damage, defeats an enemy, or triggers an explosion. It works by introducing a randomized offset to the camera position that gradually decays back to zero.
You can add this behavior to `obj_camera` with minimal setup. In the **Create** event, initialize the shake variables:
// Screen Shake variables
fuerza_sacudida = 0;
amortiguacion_sacudida = 0.9;Then, in the **End Step** event, after calculating the smoothed position and clamping bounds, add the random offset before rounding:
// Apply screen shake if active
if (fuerza_sacudida > 0.2) {
_pos_x_final += random_range(-fuerza_sacudida, fuerza_sacudida);
_pos_y_final += random_range(-fuerza_sacudida, fuerza_sacudida);
// Gradually reduce shake intensity each frame
fuerza_sacudida *= amortiguacion_sacudida;
} else {
fuerza_sacudida = 0;
}
// Finally set camera position with shake applied
camera_set_view_pos(camara, floor(_pos_x_final), floor(_pos_y_final));Any object in the game (such as an enemy projectile or `obj_player` when landing on a hazard) can trigger the effect with a single line of code: `obj_camera.fuerza_sacudida = 8;`.
Practical Exercises
Put your knowledge of cameras and viewports into practice by completing these exercises in your platformer project:
1. **Wide Panoramic Room and Boundary Validation:** Create a new room called `rm_camera_test` sized 2400 pixels wide by 540 pixels high. Place solid tiles to form a continuous floor and various stepped platforms at different elevations. Place only `obj_player` and `obj_camera` on the instances layer. Verify that walking to the far left (x = 0) or far right (x = 2400) stops the camera naturally at the room boundaries without exposing empty space outside the level.
2. **Dynamic Smoothing Based on Player State:** Modify `obj_camera` so that the `suavizado` factor adjusts dynamically based on the character's movement state. When running along flat ground, keep smoothing at `0.1`. However, if the player is in free fall at high velocity (`vsp > 3` on `obj_player`), increase smoothing to `0.25` so the camera tracks faster and prevents the player from falling out of frame. Ensure the transition between smoothing values feels natural and does not cause sudden snapping.
3. **Vertical Deadzone for Small Hops:** In many classic platformers, minor jumps on flat ground do not move the camera vertically, preventing player disorientation from excessive up-and-down motion. Modify the **End Step** event of `obj_camera` to create a vertical deadzone: if the difference between the player's Y coordinate and the vertical center of the camera is under 32 pixels, skip updating the camera's Y position. Confirm that small jumps on flat ground leave the camera horizon completely stable.
Summary
- GameMaker's vision architecture consists of three elements: the Room (the full game world), the Camera (the mathematical lens with a position and frame size inside the room), and the Viewport (the area of the screen or display window where the view is rendered).
- The Room Editor provides a visual way to set up object following, but lacks the elastic interpolation and custom behaviors required for polished commercial games.
- Camera positioning must always run in the End Step event of a dedicated manager object (obj_camera) to ensure all movement and collision logic has finished before drawing.
- The lerp() function produces smooth, organic follow movement through percentage-based linear interpolation each frame.
- The clamp() function keeps the camera constrained within the room boundaries (between 0 and room_width - ancho_camara horizontally, and 0 and room_height - alto_camara vertically), preventing out-of-bounds voids from being visible.
- In pixel art projects, truncating camera coordinates with floor() before passing them to camera_set_view_pos() prevents sub-pixel jitter and texture distortion.
- Dynamically created cameras must be freed in the Clean Up event using camera_destroy() to prevent memory leaks when switching rooms or restarting the game.
Practise what you learned
Exercise 1 · 1 point
Match each vision architecture concept in GameMaker with its role and the analogy introduced in the lesson:
- Room
- Camera
- Viewport
Exercise 2 · 1 point
Why is it essential to position and update the camera in the End Step event rather than the Step event?
Exercise 3 · 1 point
In the Room Editor, setting Horizontal Speed or Vertical Speed to causes the camera to track the player instantly and rigidly. To achieve smooth, elastic camera tracking in code, developers use the function, which performs linear interpolation between the current and target positions.
Sign in to save your progress — Sign in