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

Introduction to Tilemaps

Lesson 29 of 60 · 60 min

Also available in: ES

From Individual Objects to Tile-Based Architecture

Throughout previous modules, we built the core mechanics for our 2D platformer. In Module 2 and Module 5, we placed individual instances of a solid object (such as `obj_bloque` or `obj_suelo`) across the room so our character had solid ground to run and jump on. While this instance-based approach is intuitive and works well for small prototypes spanning just a few screens, it introduces critical bottlenecks as projects scale up into larger, more detailed levels.

Every object instance in GameMaker consumes system memory and CPU cycles. An instance is not merely an image drawn on the screen: it is a complex data structure that contains dozens of built-in variables (such as `x`, `y`, `speed`, `direction`, `image_index`, `image_alpha`, and `mask_index`), pointers to event lists, and logic checks evaluated by the engine on every Step cycle. If a medium-sized level requires 4,000 ground blocks to build tunnels, slopes, ceilings, and floating platforms, the engine must manage 4,000 separate instances. This drastically reduces the game's framerate (FPS) and makes editing in the Room Editor sluggish and awkward.

To solve this problem, the 2D game industry developed tilemaps decades ago. A Tilemap is a lightweight 2D grid optimized to render massive environments using a single batched draw call. Instead of processing thousands of active entities, GameMaker simply references a 2D array of integers, where each number represents a specific graphic slice taken from a single master image called a Tile Set. The performance impact drops from a major bottleneck to virtually zero.

FeatureInstance-Based Environment (Objects)Tilemap-Based Environment (Tiles)
Memory usageHigh: every block allocates memory for variables and events.Minimal: a flat 2D array of integers in memory.
CPU overheadHigh: the CPU iterates through events and checks for every object.Nearly zero: logical processing per cell only occurs when explicitly queried.
GPU rendering performanceMultiple draw calls unless texture batching is heavily optimized.Extremely efficient: the graphics card renders the entire map in a single batch (vertex buffer).
Level design toolsManual placement and alignment of individual instances.Fast brushes, bucket fill, multi-tile selection, and autotiling.
Recommended use caseDynamic entities, doors, moving traps, enemies, and switches.Solid terrain, walls, ceilings, static decorations, and background structures.

Core Structure: Sprite, Tile Set, and Tile Layer

To master this workflow in GameMaker, you must understand three interconnected concepts that often confuse beginner developers:

1. The Source Sprite: The bitmap asset (typically a PNG file) imported into the Asset Browser. It contains all the environmental art arranged across a uniform grid, such as 16x16 or 32x32 pixel cells. 2. The Tile Set Asset: A dedicated GameMaker resource created in the Asset Browser by right-clicking and selecting 'Create -> Tile Set'. This resource takes the source sprite and mathematically slices it into a regular grid, assigning an index number to every sub-tile. 3. The Tile Layer: A specialized layer created inside the Room Editor. Unlike Instance Layers, a Tile Layer does not store objects. Instead, it stores room coordinates where graphic indices from the Tile Set are 'stamped'.

Schematic diagram illustrating the conversion of a sprite sheet into an indexed tile set and its placement onto a level grid.
Figure 1: Modular workflow: from the original sprite sheet to the assembled environment in the Room Editor. · AI-generated image

The Golden Rule of GameMaker: Tile 0 (Empty Cell)

There is a crucial technical detail in GameMaker that every developer should memorize from day one: the cell at the top-left corner of any tile set (column 0, row 0) is reserved by the engine to represent 'empty space' or no tile. Internally, a value of 0 in a tilemap array means 'do not draw anything here'.

💡 Watch out! If you import a tileset sprite where the first usable artwork (such as your main grass tile) sits directly in the top-left cell (coordinates x:0, y:0 of the sprite), GameMaker will ignore that graphic when painting. Always leave the top-left 16x16 or 32x32 pixel cell completely transparent in your image file, or configure an appropriate offset in the Tile Set properties.

If you are using an existing tileset asset that lacks this initial blank tile, GameMaker's Tile Set Editor provides automated offset options in its properties panel, such as 'Add Separation' or adjusting the 'Offset' (X and Y offsets). However, the cleanest standard in 2D technical art is to prepare your sprite sheets in your drawing tool (such as Aseprite, Photoshop, or GameMaker's built-in image editor) ensuring upfront that cell (0, 0) remains blank.

Step-by-Step: Creating a Tile Set Asset

Keeping with our platformer project's retro aesthetic, we will build a classic 16x16 pixel terrain set featuring grass surfaces, inner dirt layers, and solid stone blocks for underground areas. Follow these steps carefully:

Step 1: Preparing the Sprite. In the Asset Browser, right-click your sprites folder and select 'Create -> Sprite'. Name it `spr_tileset_terreno`. Open the image editor and resize the canvas to 128 pixels wide by 128 pixels tall. Turn on GameMaker's visual grid and set it to 16x16 pixels. Leave the top-left 16x16 cell empty and fully transparent. In the second cell (from x=16 to x=31), draw the flat top surface of a grass-covered dirt block. Next to it, draw the top-left corner, top-right corner, vertical dirt edges, solid interior dirt, and bottom edges.

Step 2: Generating the Tile Set. Right-click in the Asset Browser and choose 'Create -> Tile Set'. Name this new asset `tls_terreno` (using the standard `tls_` naming prefix for Tile Sets). In the asset's properties inspector, locate the 'Sprite' field and assign the asset created in the previous step: `spr_tileset_terreno`.

Step 3: Configuring Tile Dimensions. Once the sprite is linked, the editor displays the image divided by grid guidelines. In the Tile Set properties panel, verify the following core settings:

PropertyProject ValueFunctional Description
Tile Width16Width of each individual tile cell in pixels.
Tile Height16Height of each individual tile cell in pixels.
Separation X0Horizontal spacing between adjacent cells.
Separation Y0Vertical spacing between adjacent rows.
Offset X0Initial offset margin from the left edge of the canvas.
Offset Y0Initial offset margin from the top edge of the canvas.

Once configured, you will see GameMaker automatically number every cell in the interactive preview: the empty cell is 0, the first grass tile is 1, and numbering continues left-to-right, top-to-bottom. This asset is now ready to be used in any room across your game.

Working with Tile Layers in the Room Editor

With your `tls_terreno` resource ready, open your primary room, such as `rm_nivel_1`. Look at the Layers panel, usually located on the left or top of the Room Editor workspace.

By default, GameMaker creates a `Background` layer and an `Instances` layer. To paint environments using tiles, we must add a dedicated layer. Click the 'Create New Tile Layer' icon (represented by a small four-square grid). Name this layer `Tiles_Terreno`.

💡 Layer hierarchy tip: Layer order in the Room Editor determines render order. Layers higher up in the list draw on top of layers below them. Therefore, your `Tiles_Terreno` layer should sit below the `Instances` layer (so your character and enemies appear in front of the terrain rather than behind it), but above any `Background` layers.

Selecting the `Tiles_Terreno` layer will automatically open the 'Tile Editing' panel in GameMaker. This panel asks you to select a Tile Set: choose `tls_terreno`. Your palette of available tiles drawn in the sprite will appear immediately.

Painting Tools and Productivity Shortcuts

The Room Editor provides raster-style painting tools tailored for working on a grid:

• Pencil Tool: Select an individual tile or a rectangular block of tiles in the Tile Editing palette, then click or drag with the left mouse button across the room canvas to place them. • Eraser Tool: Resets any clicked cell to index 0 (empty), clearing the graphic on that layer without affecting other layers. • Rectangle Tool: Paints large continuous areas by repeating the selected pattern. Great for filling the solid interior of underground caverns in seconds. • Bucket Fill Tool: Fills continuous bounded regions sharing the same tile index. • Quick Erase Shortcut: While using the Pencil tool, you can hold Shift or right-click (depending on your editor preferences) to quickly erase without manually switching tools in the toolbar.

In addition, you can select a 3x3 block of tiles directly from the palette and paint full platform structures in a single stroke. This speeds up floating platform construction: select the left edge, center, and right edge together, and place them into your scene with a single click.

Layering Strategies: Structure, Decoration, and Depth

A common beginner mistake in level design is attempting to paint the entire environment onto a single Tile Layer. This creates immediate visual conflicts: if you want to place hanging vines under a stone ledge, or small flowers and rocks on top of the grass, placing the flower in that cell overwrites and erases the underlying grass tile, since each cell in a Tile Layer can only store one index at a time.

The professional solution is to structure levels using multiple overlapping Tile Layers, each with a distinct role and consistent depth order:

1. `Tiles_Fondo_Estructural` (Background Structure): Sits behind characters and solid platforms. Use this layer to paint dim cave interiors, exterior windows, support beams, or distant ruins. These tiles are often drawn with lower saturation or darker tones so they do not compete visually with interactive gameplay elements. 2. `Tiles_Terreno` (Main Terrain): The primary layer holding the solid platforms, floors, ceilings, and walls that the gameplay relies upon. 3. `Tiles_Decoracion` (Decoration): Placed directly above `Tiles_Terreno`. Use this layer for grass tufts, flowers, loose stones, signs, and props that enrich the visuals without breaking the underlying platform structures. 4. `Tiles_Primer_Plano` (Foreground): Positioned above the `Instances` layer. Used sparingly for large foreground tree branches, shadowy cavern overhangs, or pillars that the player walks behind, adding a noticeable sense of depth to a 2D scene.

Manipulating Tilemaps via GML Code

While most level design takes place visually in the Room Editor, GameMaker Tilemaps are dynamic data structures that can be read, queried, and modified in real time during gameplay using GameMaker Language (GML). For developers who prefer GML Visual, equivalent action blocks exist under the 'Tiles' category (such as 'Get Tile' or 'Set Tile'); however, understanding the code functions provides precision and flexibility that are hard to beat.

To interact with a tile layer via code, we first retrieve the layer's ID using `layer_get_id()`, and then get the specific tilemap element ID hosted on that layer using `layer_tilemap_get_id()`. With that element ID in hand, we can inspect or alter any cell across the level.

Let's review the core functions used for reading and modifying terrain programmatically:

Now let's build a practical, contextualized example. We will create a controller object named `obj_terreno_controller` to manage tilemap access and implement a common platformer feature: destroying cracked ground blocks when clicked with the mouse (or upon impact), modifying the map in memory instantly.

In the Asset Browser, create an object named `obj_terreno_controller`. In its **Create** event, initialize variables and store the layer references:

// --------------------------------------------------------
// Object: obj_terreno_controller
// Event: Create
// Description: Locates the terrain layer and stores the tilemap ID
// --------------------------------------------------------

// 1. Get the ID of the layer created in the Room Editor
var _layer_id = layer_get_id("Tiles_Terreno");

// 2. Retrieve the ID of the tilemap element on that layer
mapa_terreno = layer_tilemap_get_id(_layer_id);

// Debug console safety check
if (mapa_terreno == -1) {
    show_debug_message("ERROR: Tiles_Terreno layer not found or contains no valid Tilemap.");
} else {
    show_debug_message("Tilemap successfully connected. ID: " + string(mapa_terreno));
}

Next, in the **Step** event of this controller object, implement an interactive routine: if the player clicks the left mouse button (`mb_left`), the system reads the tile under the cursor. If a tile is present (index greater than 0), it clears it immediately by setting its index to 0 (the empty cell):

// --------------------------------------------------------
// Object: obj_terreno_controller
// Event: Step
// Description: Detects and clears tiles under the mouse cursor
// --------------------------------------------------------

// Only execute if the tilemap was initialized properly
if (mapa_terreno != -1) {
    // Check if the player pressed the left mouse button
    if (mouse_check_button_pressed(mb_left)) {
        // Read the tile index at the cursor's exact room coordinates
        var _tile_actual = tilemap_get_at_pixel(mapa_terreno, mouse_x, mouse_y);
        
        // An index greater than 0 confirms a visible, solid tile is present
        if (_tile_actual > 0) {
            show_debug_message("Tile removed. Previous index: " + string(_tile_actual));
            
            // Set the index to 0 to erase the selected cell
            tilemap_set_at_pixel(mapa_terreno, 0, mouse_x, mouse_y);
        }
    }
}

Place a single instance of `obj_terreno_controller` onto the `Instances` layer of your room and run the game (by pressing F5). Clicking on any section of ground painted with your Tile Set will instantly erase that block. This mechanic illustrates how tilemap functions work under the hood and forms the basis for breakable walls, diggable tunnels, and real-time environment modification.

Logical Data Mapping and Collision Preparation

Keep in mind that at this stage, the tilemap is purely visual. If you run your game with the player character from Module 5 (`obj_jugador`) standing on a platform built purely with tiles, the character will fall straight through the ground due to gravity.

This happens because our collision system from Module 5 was built to check physical instances (`place_meeting(x + vel_x, y, obj_bloque)`). By replacing thousands of `obj_bloque` instances with a tilemap, there are no longer any solid objects in the room for that method to detect.

In the next lesson, we will cover 'Tilemap Collisions' in depth, updating our player movement code to query the mathematical cells of the tilemap directly using `tilemap_get_at_pixel()`. This will provide responsive, rock-solid platformer physics without needing a single collision object instance in the room.

Best Practices for Tile Set Design

As your game environments become larger and more visually detailed, keeping your art assets structured is essential for avoiding costly rework. Follow these industry best practices:

1. Maintain a consistent grid size throughout the project: If your characters and movement mechanics are tuned for a 16x16 grid, design all tilesets at 16x16. Mixing 16x16 tiles with 24x24 or 32x32 tiles within the same gameplay plane creates visual inconsistency and breaks collision math.

2. Always design standard modular connecting tiles (9-Slice): For any biome (dirt, stone, metal), be sure to draw at least the standard 9-piece set: 4 outer corners, 4 outer edges/walls, and 1 solid center fill. This allows you to build arbitrary rectangular platforms and shapes without leaving awkward unfinished edges.

3. Enforce strict layer naming conventions: Use identical layer names across all rooms (e.g., `Tiles_Fondo`, `Tiles_Terreno`, `Tiles_Decoracion`). When writing GML scripts that locate layers by string (`layer_get_id("Tiles_Terreno")`), any typo or variation between rooms (`Tiles_Piso`, `Terreno_1`) will cause the code to fail or miss solid ground entirely.

Hands-On Exercises

Complete these three exercises to master creating, designing, and inspecting tilemaps:

**Exercise 1: Creating a Tile Set Asset and Respecting Tile 0** Create a sprite named `spr_tileset_castillo` sized at 64x64 pixels and divided into 16x16 cells (a 4-column by 4-row matrix). Leave the top-left cell (column 0, row 0) completely transparent. In the neighboring cells, draw stone brick tiles: a brick with a top edge, a brick with a side edge, and an interior filler brick. Then create a Tile Set asset named `tls_castillo`, assign your sprite to it, and confirm the cell sizes are set to exactly 16x16 pixels. *Verification criteria:* When opening `tls_castillo`, the first cell in the top-left corner must display index 0 with a transparent background, and all other tiles must align perfectly within the grid lines without cutoffs or offset issues.

**Exercise 2: Building a Multi-Layered Level** Open `rm_nivel_1`. Remove or disable the massive rows of `obj_bloque` instances used in earlier prototypes. Create three tile layers named `Tiles_Fondo`, `Tiles_Terreno`, and `Tiles_Decoracion`. Arrange them to respect proper layer hierarchy relative to `Instances`. Paint a level containing at least two elevated platforms, a set of stepped terrain ledges, and an underground cavern with a solid structural background. *Verification criteria:* In the Room Editor, toggling the visibility of `Tiles_Decoracion` or `Tiles_Fondo` (via the eye icon in the layer list) should leave the main terrain structure intact without textures erasing or overlapping each other destructively.

**Exercise 3: Debugging Cell Data via GML** Extend the Step event in `obj_terreno_controller` so that, in addition to erasing tiles on left-click, pressing the Spacebar (`keyboard_check_pressed(vk_space)`) reads the player's position (`obj_jugador.x`, `obj_jugador.y`) and prints to the Output console via `show_debug_message()` the tile index and grid coordinates (column and row) directly beneath the player's feet. *Verification criteria:* Run the game, navigate the character across different terrain sections, and press Space. The GameMaker Output window should display clear debug messages stating the detected tile index, column, and row under the player.

Summary

  • Replacing thousands of ground object instances with Tilemaps dramatically reduces RAM usage and CPU overhead by consolidating rendering into a single batched draw call.
  • A tilemap system relies on three linked components: the source Sprite containing the textures, the Tile Set asset defining the grid, and the Tile Layer in the room where tiles are placed.
  • The top-left cell (column 0, row 0) of every tile set is reserved by GameMaker as index 0 (empty/transparent); placing usable art there renders it unpaintable.
  • Organizing levels across multiple tile layers (background, terrain, decoration, and foreground) prevents tile overwrites and provides visual depth.
  • Built-in GML functions such as layer_tilemap_get_id(), tilemap_get_at_pixel(), and tilemap_set_at_pixel() allow developers to inspect, modify, and destroy tiles dynamically during gameplay.

Practise what you learned

Exercise 1 · 1 point

What is the technical reason why the top-left cell (column 0, row 0) must be left blank or transparent when preparing a Tile Set in GameMaker?

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

Exercise 2 · 1 point

Match each core element of GameMaker's tile system with its corresponding definition:

  • Source Sprite
  • Tile Set Resource
  • Tile Layer (Tilemap)
  • Eraser Tool
You are practising without an account: nothing is saved. Sign up to keep your progress.

Exercise 3 · 1 point

Following the layer depth hierarchy rules, order these layers in the Room Editor panel from TOP to BOTTOM (so that elements render correctly from front to back):

  1. 1Instances layer (Instances)
  2. 2Background layer (Background)
  3. 3Tile layer (Tiles_Terrain)
You are practising without an account: nothing is saved. Sign up to keep your progress.

Sign in to save your progress — Sign in