Introducción al Desarrollo de Videojuegos con GameMaker · Module 6: Animations, Sprites, Backgrounds, and Tilemaps

Configuring Static and Parallax Backgrounds

Lesson 28 of 60 · 50 min

Also available in: ES

Visual Space and Depth in Two Dimensions

In two-dimensional platformers, the screen acts as a cross-section of your game world. In previous modules, we built a playable character (obj_player) featuring horizontal movement physics, jumping, and precise collision detection against solid terrain blocks. However, if your stage displays platforms against a flat solid color or a completely static image, the environment tends to feel rigid, artificial, and lifeless. Immersing players in a believable atmosphere requires adding spatial depth.

The human brain calculates distance based on how elements shift across the relative visual field. When walking through an open field, blades of grass right beneath your feet seem to rush past quickly, trees at a medium distance drift by at a moderate pace, while distant mountain ranges and the sky appear virtually stationary. This natural optical phenomenon is called parallax. By simulating this mechanic, we can transform a flat two-dimensional plane into a layered diorama with perceived volume.

In this lesson, you will learn how to work with Background Layers inside GameMaker's Room Editor, configure seamless looping patterns (horizontal and vertical tiling), and control layers both passively through room properties and programmatically using GML Code synchronized with the game camera. This architecture also sets the stage for introducing Tilemaps in upcoming lessons.

Background Layers in the GameMaker Room Editor

In modern versions of GameMaker, level design inside the Room Editor is structured around a robust stack of layers found in the Layer Panel. The vertical order in which these layers are stacked strictly defines their render order: layers at the bottom of the visual list are drawn first (in the far background), whereas layers placed higher in the list render on top of everything beneath them.

GameMaker provides several dedicated layer types: Instance Layers (where obj_player and solid collision blocks live), Asset Layers, Tilemap Layers, and Background Layers. A Background Layer is specifically optimized to render either a solid flat color or a repeating sprite texture with minimal GPU overhead.

Inspector PropertyValue TypeEngine Functionality
ColourRGB / Hex ColorThe base tint over which the scene is drawn. If no sprite is selected, this color fills the entire visible view.
SpriteSprite AssetThe graphical resource assigned as a repeating background texture across the level.
Horizontal TileBoolean (Checkbox)Repeats the background sprite infinitely along the horizontal axis (X-axis).
Vertical TileBoolean (Checkbox)Repeats the background sprite infinitely along the vertical axis (Y-axis).
StretchBoolean (Checkbox)Distorts the sprite asset to force it to match the exact dimensions of the Room.
Horizontal Speed (hspeed)Real NumberApplies an automatic, continuous pixel-per-frame shift along the horizontal axis.
Vertical Speed (vspeed)Real NumberApplies an automatic, continuous pixel-per-frame shift along the vertical axis.

For our retro platformer project, avoid enabling 'Stretch'. Stretching pixel art distorts its aspect ratio and destroys consistent pixel scale (causing pixel distortion or "mixels"). Instead, design modular sprites whose left and right edges blend seamlessly into each other, then enable 'Horizontal Tile'.

The Physics and Mathematics of the Parallax Effect

The core principle behind parallax scrolling is moving distinct background planes at fractions of the main camera's velocity. Imagine a camera tracking the player as they run to the right at 4 pixels per frame. If a background image shifts at that exact same speed, it appears to sit on the exact same depth plane as the character. If the background remains completely stationary relative to the camera viewport, it appears infinitely far away (like the sun, moon, or distant stars).

Mathematically, the screen position of any given background layer is defined by a relative projection formula:\n\nLayerPosition = CameraPosition * ParallaxFactor\n\nHere, 'ParallaxFactor' is a normalized coefficient that typically ranges between 0.0 and 1.0. A factor of 0.0 means the layer tracks camera movement entirely, simulating infinite distance. A factor of 0.8 means the layer shifts almost at the same rate as the foreground terrain, representing physical elements directly behind the player's platforms.

Diagram explaining the 2D parallax effect showing layers of depth (foreground, gameplay plane, hills, and distant mountains) with velocity arrows that decrease with distance.
Depth plane hierarchy: the further a plane sits from the player's camera, the smaller its relative displacement per frame. · AI-generated image

Additionally, games often incorporate Foreground Parallax. By placing overhanging vines, rusted beams, or low-lying mist in front of the active Instance Layer (with a factor greater than 1.0, such as 1.2 or 1.3), those visual elements appear to brush past the camera lens rapidly, establishing convincing three-dimensional volume both behind and in front of the gameplay action.

Method 1: Continuous Ambient Parallax Without Code

GameMaker allows you to implement simple kinetic background movement directly within the visual Room Editor without writing a single line of GML. This is especially useful for self-propelled atmospheric elements such as wind-swept cloud banks, flowing underground water, or scrolling industrial conveyor belts.

To set this up directly in your level:

1. Open your main game room (e.g., rm_level_1) in the Workspace.\n2. In the top-left 'Layers' panel, click 'Create Background Layer'.\n3. Name this new layer 'Background_Clouds' and drag it so it sits directly above 'Background_Sky'.\n4. In the Layer Inspector, assign your spr_bg_clouds sprite.\n5. Check the 'Horizontal Tile' box so the cloud strip repeats indefinitely across the room.\n6. In the 'Horizontal Speed' field, enter -0.25.

💡 A negative Horizontal Speed value shifts the texture toward the left, simulating wind blowing westward. Keep these values modest (between 0.1 and 0.5); high scroll speeds generate visual noise that distracts players from obstacles and incoming hazards.

When you launch the game with F5, the clouds will drift continuously, regardless of whether the character is sprinting or standing still. However, this editor-only method has an architectural limit: the layer does not respond to camera movement through an expansive world. To create backgrounds that travel dynamically with the player, we must control layer offsets programmatically using GML.

Method 2: Camera-Synchronized Parallax Using GML

To make background layers react dynamically as the player moves across the stage, we must update each layer's offset during every step of the game loop. In GameMaker, every layer has an internal Layer ID that can be modified using the built-in functions layer_x() and layer_y().

To keep our codebase clean and decoupled, avoid embedding this logic inside obj_player or collision objects. Instead, create a dedicated controller object: obj_parallax_manager. This object serves as an invisible atmospheric controller inside the room.

Step 1: Organizing Room Layers

Inside rm_level_1, structure your Layer Panel from bottom to top in this exact sequence:

• Background_0_Sky (Background Layer: spr_bg_sky, Horizontal Tile enabled)\n• Background_1_Mountains (Background Layer: spr_bg_mountains, Horizontal Tile enabled)\n• Background_2_Forest (Background Layer: spr_bg_forest, Horizontal Tile enabled)\n• Instances (Instance Layer: where obj_player and obj_block are placed)\n• Foreground_Leaves (Optional Background or Asset layer rendering in front of instances)

Step 2: Creating and Initializing the Parallax Manager

Create a new object inside your manager folder and name it obj_parallax_manager. It does not require an assigned sprite. In its Create Event, cache references to our background layers using layer_get_id(). This function takes the layer's string name and returns its internal memory ID, avoiding expensive string searches every frame and keeping performance optimal.

// ====================================================================
// OBJECT: obj_parallax_manager
// EVENT: Create
// PURPOSE: Cache background layer IDs and define depth scroll factors
//          for the parallax effect.
// ====================================================================

// Retrieve internal IDs for the layers created in the Room Editor
capa_cielo     = layer_get_id("Background_0_Sky");
capa_montanas  = layer_get_id("Background_1_Mountains");
capa_bosque    = layer_get_id("Background_2_Forest");

// Horizontal relative motion factors (0.0 = static with camera, 1.0 = moves with room ground)
// Lower values push layers further toward the visual horizon.
factor_cielo    = 0.95; // Moves at 5% relative speed (nearly stationary on the horizon)
factor_montanas = 0.65; // Mid-to-distant mountain range
factor_bosque   = 0.30; // Foothills and trees sitting right behind the platforms

// Initial reference values to prevent startup position snaps
cam_x_previa = 0;
cam_y_previa = 0;

Notice how the factors are structured relative to the layer's placement formula: when calling layer_x(layer, cam_x * factor), a factor of 0.95 keeps the layer anchored closely to the moving camera, producing very little relative shift (the sky appears infinitely distant). Conversely, a factor of 0.30 creates a much larger shift relative to the camera viewport, making those elements appear closer to the foreground.

Step 3: Updating Layer Positions in the Step Event

To eliminate visual stutter or tearing (commonly referred to as camera jitter), background layer updates must happen either in the Step Event (after camera calculations are complete) or in the End Step Event. Assuming your viewport tracks the player using view_camera[0], retrieve its real-time coordinates:

// ====================================================================
// OBJECT: obj_parallax_manager
// EVENT: Step (or End Step)
// PURPOSE: Synchronize background layer coordinates with the active
//          room camera's current position.
// ====================================================================

// 1. Retrieve current camera coordinates for the primary view (View 0)
var _cam_x = camera_get_view_x(view_camera[0]);
var _cam_y = camera_get_view_y(view_camera[0]);

// 2. Apply updated X positions to each registered background layer
// Verify the layer exists in the active room first to prevent runtime errors
if (layer_exists(capa_cielo)) {
    layer_x(capa_cielo, _cam_x * factor_cielo);
}

if (layer_exists(capa_montanas)) {
    layer_x(capa_montanas, _cam_x * factor_montanas);
}

if (layer_exists(capa_bosque)) {
    layer_x(capa_bosque, _cam_x * factor_bosque);
    // If the stage features prominent vertical platforming, apply Y parallax as well:
    layer_y(capa_bosque, _cam_y * 0.15);
}

In GameMaker Visual (formerly Drag and Drop), this same mechanic is accomplished by dragging 'Function Call' blocks targeting layer_x with layer variables and camera math. In GML Code, however, the implementation is concise, easy to read, and quick to fine-tune during polishing passes.

Combining Relative Parallax with Continuous Drift

One hallmark visual technique found in classic 16-bit games is combining camera-reactive parallax with autonomous atmospheric drift. For instance, having distant cloud formations drift slowly across the sky while simultaneously reacting with subtle perspective shifts as the player sprints across the level.

To achieve this mathematical blend without modifying editor properties, introduce an independent time accumulator directly inside our GML controller:

// ====================================================================
// OBJECT: obj_parallax_manager
// EVENT: Create (Addition snippet for continuous drift)
// ====================================================================

deriva_nubes = 0;        // Wind offset accumulator
velocidad_viento = 0.2;  // Pixels per frame wind moves toward the left


// ====================================================================
// OBJECT: obj_parallax_manager
// EVENT: Step (Updated snippet for drifting layers)
// ====================================================================

var _cam_x = camera_get_view_x(view_camera[0]);

// Accumulate constant wind drift over time
deriva_nubes += velocidad_viento;

// Calculate final layer position by combining camera parallax with wind offset
if (layer_exists(capa_cielo)) {
    // Subtracting the accumulator shifts clouds continuously from right to left
    layer_x(capa_cielo, (_cam_x * factor_cielo) - deriva_nubes);
}

By adding a custom accumulator (deriva_nubes), the layer retains independent motion while still responding predictably whenever the camera pans across the world.

Performance Considerations and Graphics Optimization

While Background Layers are highly optimized, careless handling of oversized assets can quickly consume video memory (VRAM) and cause framerate drops, particularly on mobile hardware or low-power handhelds. To maintain a solid 60 FPS, follow these production standards:

1. Background Sprite Dimensions: Never create a single background image spanning the entire level dimensions (e.g., a massive 10000x1080 px graphic). Instead, design a seamless looping module at 320x180, 480x270, or 640x360 px and let GameMaker tile it automatically using 'Horizontal Tile'. This reduces texture memory usage from hundreds of megabytes down to a few kilobytes.

2. Texture Groups: Inside each background sprite's properties, assign them to dedicated Texture Groups. Placing all assets for 'Forest_Level' onto their own dedicated texture page prevents GameMaker from loading unused graphics from other stages into memory.

3. Empty or Unused Layers: If an indoor room (such as a dungeon or cave) has no visible sky, disable the layer's visibility or delete it from that room entirely. Never leave hidden layers running unnecessary calculations in the background.

Practical Consolidation Exercises

Reinforce and validate these concepts by completing the following practical exercises in your project:

Summary

  • Background Layers in GameMaker render solid fill colors or repeating texture patterns (tiling) with minimal performance overhead.
  • The parallax effect relies on the optical rule that objects further from an observer move slower across the field of view than foreground objects.
  • You can configure automated, constant layer scrolling directly inside the Room Inspector using hspeed and vspeed—ideal for air currents, clouds, and ambient motion.
  • GML functions layer_get_id(), layer_x(), and layer_y() allow precise manual synchronization of layer offsets based on active camera coordinates (view_camera[0]).
  • Multiplying horizontal camera positions by normalized decimal factors (between 0.0 and 1.0) creates distinct, natural-looking depth planes.
  • Modular, seamless looping textures keep texture memory (VRAM) lean compared to using oversized, stretched background images.

Practise what you learned

Exercise 1 · 1 point

Why is using the 'Stretch' property discouraged on background layers for a retro pixel art platformer project?

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

Exercise 2 · 1 point

Order the Room layers from bottom to top in the layer panel, from the layer drawn first in the background to the one drawn in front of everything else.

  1. 1Background_0_Sky
  2. 2Instances
  3. 3Foreground_Leaves
  4. 4Background_1_Mountains
  5. 5Background_2_Forest
You are practising without an account: nothing is saved. Sign up to keep your progress.

Exercise 3 · 1 point

In the parallax projection formula, assigning a factor of causes the layer to ignore camera movement, simulating infinite distance. In contrast, for a foreground effect that scrolls in front of the action, a factor to 1.0 is used. To update that layer's horizontal position using GML each frame, the function is used.

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

Sign in to save your progress — Sign in