Introducción al Desarrollo de Videojuegos con GameMaker · Module 3: Programming Fundamentals with GML Code
Loops and Basic Built-in Functions
Lesson 13 of 60 · 55 min
Also available in: ES
Introduction to Iterative Flow and Built-in Functions in GameMaker
Up to this point in the course, we have learned how to store information using variables and make decisions with conditional structures such as if, else, and switch. However, game development involves a constant demand: executing the exact same operation dozens, hundreds, or thousands of times automatically, cleanly, and without manually writing duplicate code. To solve this challenge, programming languages provide loops (or iteration structures), which work hand in hand with the engine's built-in functions.
One of the most frequent misconceptions among GameMaker beginners is confusing the engine's natural update cycle—the Step Event, which runs continuously at the game's room speed (e.g., 60 times per second)—with a code loop such as for or while. Understanding this distinction is vital: while the Step Event distributes its ticks across real time, allowing the screen to render between one frame and the next, a code loop inside an event executes completely within a single frame, halting visual rendering until all its iterations finish. If a loop takes too long or enters an infinite loop, the entire game will freeze.
In this lesson, we will explore the inner workings of GameMaker's built-in functions (GML Code), examining how they accept parameters and return values to handle trigonometry, general math, or instance management. Then, we will thoroughly dissect the three primary iteration structures in GML: the repeat statement, the for loop, and the while loop, along with the break and continue flow control statements. Finally, we will apply these concepts directly to our 2D platformer project, automating tasks like generating rows of collectible coins and scattering visual particle effects.
What Is a Built-in Function and How Does It Work in GML?
In programming terms, a function is an encapsulated block of code designed to carry out a specific task. GameMaker includes hundreds of built-in functions designed to streamline development: from calculating the distance between two entities in a room to rounding decimal values or generating random numbers to make game mechanics feel dynamic. Calling or invoking a function simply means writing its name followed by a pair of parentheses, inside of which we pass input values known as arguments or parameters.
Many functions trigger an immediate action without returning a numeric result (for example, instance_destroy() destroys the current instance and returns nothing). Conversely, a wide variety of functions perform calculations and 'return' a resulting value that we can store in a variable or use directly inside a mathematical or conditional expression. The general syntax for calling a function with a return value assigns its output to a variable: my_result = function_name(arg0, arg1, ...);
| GML Function | Key Arguments | Return Value | Common 2D Game Purpose |
|---|---|---|---|
| clamp(val, min, max) | Value to evaluate, minimum bound, maximum bound | Number constrained within the allowed range | Capping falling speed or ensuring health does not exceed maximum. |
| point_distance(x1, y1, x2, y2) | Coordinates of Point 1 and Point 2 | Euclidean distance in pixels (Real) | Measuring proximity between the player and an enemy or collectible. |
| irandom_range(min, max) | Minimum integer, maximum integer | Inclusive random integer | Determining randomized loot, critical strike damage, or variable delays. |
| choose(val0, val1, val2...) | Comma-separated list of values | One of the provided arguments picked at random | Selecting a random sprite, an initial direction, or a sound effect. |
| show_debug_message(string) | Text or value converted to a string | None (prints to the Output Console) | Monitoring internal variables and tracking logic bugs. |
Let's see how these functions interact in an actual script. Suppose we want to cap our character's movement speed and check whether a collectible chest is within our hero's interaction range:
// Location: obj_player -> Step Event
// 1. Clamp horizontal speed between -velocidad_maxima and velocidad_maxima
velocidad_horizontal = clamp(velocidad_horizontal, -velocidad_maxima, velocidad_maxima);
// 2. Check the distance to a reference position (for example, the center of a chest)
var _distancia_al_objetivo = point_distance(x, y, 320, 240);
if (_distancia_al_objetivo < 64)
{
// Print to the Output Console for debugging
show_debug_message("The player is close to the interactive chest.");
}The 'var' keyword preceding _distancia_al_objetivo defines a temporary local variable. Local variables exist solely within the code block where they are declared and are discarded as soon as the block finishes executing. This saves memory and prevents accidental name collisions between different events or game objects. We will explore variable scope in detail in an upcoming lesson, but using local variables when working with loops and temporary calculations is an essential best practice.
The repeat Loop: Direct and Efficient Iteration
The simplest and fastest iteration structure in GameMaker is the repeat statement. Its purpose is straightforward: take an integer expression and execute the enclosed block of instructions exactly that many times. Unlike languages that lack this dedicated construct, GameMaker provides repeat as a highly optimized tool for scenarios where you do not need an index tracking which iteration is currently running, but simply want an action repeated N times.
The general syntax for repeat is: repeat (number_of_iterations) { // Code to execute on each cycle } If the value inside the parentheses is zero or negative, GameMaker skips the entire block without throwing an error. If the value has decimals, it is truncated down to the nearest integer.
// Location: obj_player -> Destroy Event (or when the player is defeated)
// Spawn 12 visual impact particles at the current position
repeat (12)
{
var _offset_x = irandom_range(-16, 16);
var _offset_y = irandom_range(-16, 16);
// Create a decorative instance on the 'Instances' layer
instance_create_layer(x + _offset_x, y + _offset_y, "Instances", obj_particula_muerte);
}In the example above, each of the 12 iterations calculates an independent random offset using irandom_range and creates an instance of obj_particula_muerte. This all happens instantly within the single frame in which obj_player is destroyed, producing a coordinated burst effect without requiring twelve duplicate lines of code.
The for Loop: Indexed Iteration and Structured Control
While repeat is ideal for blind bursts, most game mechanics require knowing exactly which iteration is currently executing. For instance, if you want to place a row of five floor tiles or position ten coins spaced exactly 32 pixels apart, you need the counter value on every pass. For this purpose, the for loop is the industry-standard structure.
A for loop organizes its control logic into three distinct sections separated by semicolons: for (initialization; condition; increment). First, a counter variable is initialized (conventionally named 'i'). Second, the condition is evaluated before each iteration; if true, the loop body runs; if false, the loop terminates immediately. Third, after running the body, the counter is incremented or decremented, and the loop checks the condition again.
// Location: obj_controlador_nivel -> Create Event
// Spawn a horizontal row of 6 collectible coins spaced 40 pixels apart
var _inicio_x = 120;
var _posicion_y = 300;
var _separacion = 40;
for (var i = 0; i < 6; i++)
{
var _spawn_x = _inicio_x + (i * _separacion);
instance_create_layer(_spawn_x, _posicion_y, "Instances", obj_moneda);
show_debug_message("Coin created at index: " + string(i) + " at X=" + string(_spawn_x));
}Let's trace the math step by step: on the first pass, i is 0, so _spawn_x evaluates to 120 + (0 * 40) = 120. On the second pass, i increments to 1, producing 120 + (1 * 40) = 160. This sequence continues up to i = 5 (X = 320). Once i hits 6 via the increment operator 'i++', the condition i < 6 evaluates to false, and GameMaker proceeds to the next line outside the loop. Calculating and instantiating all 6 coins takes just microseconds during the room's very first frame.

The while Loop: Conditional Repetition and Preventing Freezes
Unlike a for loop, where the number of iterations is known before looping begins, a while loop is used when the number of iterations depends on a dynamic condition that changes while the loop is actively running. A while statement evaluates a boolean expression: as long as that expression remains true, the body continues running uninterrupted.
The basic syntax for while is: while (condition) { // Statements that must eventually modify the condition to prevent an infinite loop } The greatest hazard with while loops is the dreaded infinite loop. If a logic error prevents the condition from ever becoming false, the CPU remains trapped executing that block forever, preventing GameMaker from finishing the current frame. The application stops responding, the operating system flags the process as frozen, and the game must be force-closed.
💡 Golden Safety Rule: Whenever you write a while loop, always ensure that code within the body progresses the condition toward false. In professional game programming, it is considered best practice to include a safety counter that caps the maximum allowable iterations (e.g., 500 passes) to prevent hard freezes caused by unexpected edge cases.
Let's examine a practical use case: finding a random position inside a room that is free from solid collision geometry before spawning an enemy or special item.
// Location: obj_spawner_enemigos -> Create Event
var _pos_valida = false;
var _intentos = 0;
var _max_intentos = 100;
var _coord_x = 0;
var _coord_y = 0;
while (!_pos_valida && _intentos < _max_intentos)
{
_intentos++;
// Pick a random position within the room bounds
_coord_x = irandom_range(64, room_width - 64);
_coord_y = irandom_range(64, room_height - 64);
// position_meeting checks if that coordinate collides with a solid wall
if (!position_meeting(_coord_x, _coord_y, obj_solid))
{
_pos_valida = true;
}
}
// If a free spot was found within the allowed attempts, spawn the entity
if (_pos_valida)
{
instance_create_layer(_coord_x, _coord_y, "Instances", obj_enemigo_patrulla);
}
else
{
show_debug_message("Warning: No open space found after " + string(_max_intentos) + " attempts.");
}Thanks to the _intentos safety counter, we guarantee that the engine will never hang indefinitely, even if the entire room is completely packed with solid blocks. The loop terminates as soon as either condition is met: a valid position is found, or the attempt limit is reached.
Flow Control Statements: break and continue
Inside any iterative structure (whether for, while, or repeat), two keywords allow us to manually alter the standard loop flow: break and continue. Using these statements effectively leads to cleaner, faster, and more readable code by eliminating unnecessary nested conditional blocks.
The 'break' keyword immediately terminates the entire loop on the spot. Program execution jumps directly to the first statement following the closing brace of the loop, skipping all remaining passes. In contrast, 'continue' does not terminate the whole loop: it only skips the remainder of the current iteration, immediately advancing to the counter increment and the next condition check.
// Location: obj_controlador_items -> Create Event
// Demonstration of break and continue in platform generation
for (var i = 0; i < 10; i++)
{
// Suppose we want to skip placing a block at the middle index (i = 5)
if (i == 5)
{
// Skip this specific iteration; block creation won't run for i = 5
continue;
}
// If we hit a critical threshold or boundary, abort the entire process
if (i > 8)
{
// Exit the for loop permanently
break;
}
var _pos_x = 64 + (i * 32);
instance_create_layer(_pos_x, 384, "Instances", obj_platform);
}In this example, indices 0, 1, 2, 3, and 4 generate 32-pixel platform blocks as expected. When i reaches 5, the continue statement skips ahead before reaching instance_create_layer, intentionally leaving a gap in the walkway without breaking the loop structure. When i reaches 9, the i > 8 condition triggers break, terminating the loop completely so nothing is placed at index 9. Mastering these two statements gives you fine-grained control over procedural generation algorithms.
Practical Integration: Basic Procedural Collectible Spawner in a Platformer
To solidify the concepts covered in this lesson, we will implement a practical spawner for our retro 2D platformer project. In earlier Module 2 rooms, we placed coins manually using the room editor. Now, we will create a controller object that combines for loops with mathematical functions (such as clamp) to spawn an elegant parabolic coin arc over floating platforms.
In the Asset Browser, right-click the 'Objects' folder and select 'Create -> Object'. Name it obj_generador_monedas. You don't need to assign a sprite, as this will serve as an invisible logic spawner. Open the object and add a Create Event with the following code:
// Location: obj_generador_monedas -> Create Event
// Define coin count for the pattern and horizontal spacing
var _total_monedas = 7;
var _espaciado_x = 32;
// Peak arc height in pixels
var _altura_arco_max = 48;
for (var i = 0; i < _total_monedas; i++)
{
// Calculate continuous horizontal offset
var _pos_x = x + (i * _espaciado_x);
// Calculate a simple symmetric parabolic curve:
// When i is at either end (0 and 6), vertical offset is 0.
// When i is at the center (3), offset reaches the maximum height.
var _progreso_normalizado = (i - (_total_monedas - 1) / 2);
var _factor_curva = 1 - sqr(_progreso_normalizado / ((_total_monedas - 1) / 2));
// Clamp the factor between 0 and 1 for absolute stability
_factor_curva = clamp(_factor_curva, 0, 1);
var _pos_y = y - (_factor_curva * _altura_arco_max);
// Create the coin on the 'Instances' layer
instance_create_layer(_pos_x, _pos_y, "Instances", obj_coin);
}
// Once spawned, destroy the spawner instance to free memory
instance_destroy();This example brings together the core pillars of programming: it uses an index variable in a for loop to iterate across 7 coins, computes a clean parabolic arc, protects boundary values using clamp(), and cleans up memory with instance_destroy() once the coins are placed. If you drag an instance of obj_generador_monedas into room_nivel_1 and press F5 (Run), you will see coins suspended in a clean arc, ready for your character to collect mid-jump.
Hands-on Exercises
The following three exercises are designed to test your understanding of iterative structures and built-in function calls in GML Code. Each exercise includes step-by-step instructions and objective verification criteria.
Summary
- Fundamental difference: The Step Event distributes its execution across frames over real time (FPS), whereas repeat, for, and while loops execute all iterations instantly within a single frame.
- Built-in GML functions: Integrated utilities that handle specialized tasks; some perform immediate actions without returning values, while others return calculated numbers or strings (e.g., clamp, point_distance, irandom_range).
- The repeat loop: GameMaker's simplest and fastest iteration statement; repeats a block an integer number of times without maintaining an index variable.
- The for loop: The standard structure for indexed iteration, defined by three expressions: variable initialization, loop condition, and increment/decrement.
- The while loop and freeze prevention: Runs code as long as a condition holds true. Always include a safety counter to prevent infinite loops from locking up the engine.
- Loop flow control with break and continue: break exits the entire loop immediately, whereas continue skips the rest of the current iteration and jumps to the next cycle.
Practise what you learned
Exercise 1 · 1 point
True or false? In GameMaker, a code loop (such as `for` or `repeat`) executes entirely within a single frame, halting visual rendering until all its iterations have finished.
Exercise 2 · 1 point
Which loop structure in GameMaker is specifically designed to run a block of code a fixed number of times when you do not need to track the current iteration index?
Exercise 3 · 1 point
Arrange the three components that make up a `for` loop in GameMaker in the order they appear inside its parentheses: `for (... ; ... ; ...)`.
- 1Evaluation of the continuation condition
- 2Increment or decrement of the counter
- 3Initialization of the counter variable
Sign in to save your progress — Sign in