Introducción al Desarrollo de Videojuegos con GameMaker · Module 3: Programming Fundamentals with GML Code

Basic Syntax and Variables in GML

Lesson 11 of 60 · 60 min

Also available in: ES

Introduction to GML Code and Syntactic Structure

In previous modules, you explored how GameMaker organizes the visual and structural assets of a project: sprites, objects, events, and rooms. Up to this point, your entities' properties have largely relied on engine defaults. However, delivering true interactivity, combat mechanics, custom physics, and complex gameplay systems requires communicating directly with the engine through code. GameMaker Language (GML) is the native language tailored specifically for this purpose. While GML Visual provides an action-block alternative, GML Code remains the industry standard for indie developers and professional studios alike due to its writing speed, readability, clean version-control integration, and technical flexibility.

GML syntax closely resembles C-family languages such as JavaScript, C#, and C++. It consists of statements that instruct the compiler which action to perform during each frame of the game. Each statement should ideally conclude with a semicolon (;). Although GameMaker's compiler is forgiving and frequently tolerates missing semicolons, omitting them is considered bad practice and often introduces logical ambiguity when dealing with complex expressions or advanced scripts. Furthermore, GML is case-sensitive: a variable named 'points' is distinct from 'Points' and 'POINTS'. Attempting to reference a misspelled variable will cause GameMaker to throw a fatal runtime error indicating that the variable is not defined.

Comments are an indispensable element of clean, professional code. Comments are programmer notes completely ignored by the compiler during execution. GML provides two standard comment formats: single-line comments, preceded by two forward slashes (//), and block or multiline comments, enclosed between a slash-asterisk and an asterisk-slash (/* ... */). Use comments to clarify the intent behind non-obvious mathematical formulas, delineate code sections, or temporarily disable code fragments during debugging sessions.

// This is a single-line comment
// Initializing player base speed
velocidad_caminar = 4;

/* 
   The following code block performs a complex
   calculation to determine the score multiplier
   based on coins collected without taking damage.
*/
multiplicador_activo = true;

Fundamental Data Types in GameMaker

Unlike strictly typed languages such as C++ or Java, GML is dynamically typed. This means you do not need to explicitly declare whether a variable will store an integer, a decimal, or a text string upon declaration; the data type is inferred automatically from the assigned value and can change dynamically if a different type is assigned later. Nevertheless, avoiding logic bugs requires a solid understanding of the primitive data types GameMaker manages in memory.

Real numbers represent all numeric values in GML. GameMaker makes no formal syntactic distinction between integers (such as 1, 0, -15) and floating-point decimal numbers (such as 3.1415, -0.5, 8.0). Both are handled internally with floating-point precision. This streamlines continuous math calculations, such as acceleration or gravity, though it warrants care when comparing exact fractional values. For exceptionally large numbers (such as 64-bit unique IDs), GameMaker provides the specialized int64 type, but standard real numbers handle the vast majority of traditional 2D game mechanics.

Strings are alphanumeric sequences enclosed in double quotation marks ("...") or single quotes ('...'), with double quotes serving as the standard convention in GML. They are used for character names, dialogue text, file paths, and UI messages. Strings can be concatenated using the addition operator (+). If you need to concatenate a number to a string, you must explicitly convert the number into text using the built-in string(value) function; failing to do so will result in a type-mismatch error.

Booleans represent binary logical states: true or false. Under the hood, GameMaker evaluates true as the number 1 (or any real number >= 0.5) and false as 0 (or any real number < 0.5). Despite this internal numeric mapping, you should always use the keywords true and false for syntactic clarity. Finally, GameMaker provides special engine constants, such as undefined (indicating the absence of an assigned value or a failed lookup) and noone (a constant evaluating to -4, used specifically to indicate that no instance or object is assigned to a reference variable).

Data TypeGML DescriptionSyntax ExampleCommon Game Use
Real (Number)Integers and floating-point decimals.vida = 100; gravedad = 0.45;Position (x, y), speed, damage, timers.
String (Text)Alphanumeric sequences wrapped in quotes.nombre = "Hero"; estado = "alert";Dialogue, profile names, UI messages.
Boolean (Logical)Truth values: true or false.en_suelo = true; derrotado = false;State toggles, collision flags, active skills.
Special IdentifiersEngine constants: undefined, noone.objetivo = noone; dato = undefined;Empty instance references, null search results.

Variables in GML: Instance Variables vs. Local Variables

A variable is an allocated space in computer memory that stores data capable of changing throughout program execution. Think of a variable as a labeled container: the label is the identifier you assign, and the content is the current data value. In GameMaker, a variable's scope determines where it can be read or modified, as well as how long it persists in RAM before being garbage-collected. To establish strong programming fundamentals, you must clearly distinguish between the two most commonly used scopes: instance variables and local variables.

An instance variable belongs exclusively to a specific active copy of an object within the room. It is declared simply by assigning it an identifier and a value inside an instance event, typically the Create event. Once declared, an instance variable persists for the entire lifetime of that instance in the room. Any event belonging to that same instance (such as Step, Draw, Destroy, or Keyboard events) can read and modify it freely. For example, a player's health, horizontal velocity, or collected gems are instance variables because they must persist across multiple frames and remain accessible across different events of that object.

Conversely, a local variable is declared by prefixing its identifier with the 'var' keyword (for example: var distance = 0;). A local variable does not belong to the object or the instance; it belongs exclusively to the specific code block or event in which it was declared. As soon as that event or script finishes executing for the current frame, the local variable is immediately discarded from memory. If you declare a variable using 'var' in the Create event, you will not be able to access it in the Step event. Local variables are ideal for temporary scratchpad calculations, intermediate mathematical values used for a fraction of a second, or loop counter indices, preventing unnecessary memory overhead and keeping the instance's permanent variable footprint clean.

Conceptual diagram comparing the scope of persistent instance variables versus temporary local variables declared with var.
Figure 1: Persistent storage of an instance variable compared to the transient lifecycle of a local variable declared with 'var'. · AI-generated image
// ---------------------------------------------------------
// OBJECT: obj_player
// EVENT: Create
// ---------------------------------------------------------
// Instance variables (persist throughout the player's lifetime)
vida_actual = 100;
vida_maxima = 100;
velocidad_movimiento = 3.5;
monedas_oro = 0;
estado_inmune = false;

// ---------------------------------------------------------
// OBJECT: obj_player
// EVENT: Step
// ---------------------------------------------------------
// Suppose the player picks up a healing potion.
// We use local variables (var) to compute the effect without cluttering the instance:
var _curacion_base = 25;
var _bonificacion = 5;
var _curacion_total = _curacion_base + _bonificacion;

// Modify the persistent instance variable using the temporary calculation:
vida_actual = vida_actual + _curacion_total;

// When the Step event finishes, _curacion_base, _bonificacion, and 
// _curacion_total are immediately discarded from memory.

💡 Professional Convention: Many GML developers prefix local variable names with an underscore (for example: var _distancia_x = 0;). This provides an immediate visual distinction between temporary scratchpad data and persistent instance variables.

Arithmetic and Compound Assignment Operators

Operators are mathematical symbols that direct the engine to perform specific operations on one or more values (operands). In game development, math is not abstract theory; it directly governs on-screen movement, health bar depletion, projectile arcs, and collision detection. GML supports all standard arithmetic operators, along with specialized operators for integer division and remainder calculations.

Addition (+), subtraction (-), multiplication (*), and division (/) function in standard fashion. Alongside these, two operators are especially vital in 2D game architecture: integer division (div) and modulo or remainder (mod or %). The 'div' operator divides two numbers and discards any fractional component, returning solely the integer quotient. For instance, 14 div 4 equals 3. This is useful for determining which tile coordinate or inventory slot corresponds to an exact pixel position. The modulo operator ('mod' or '%') returns the remaining remainder of integer division. For example, 14 mod 4 yields 2. Modulo is essential for cycling values (such as wrapping a weapon index back to 0 once it exceeds the selection limit).

OperatorNameSample ExpressionFinal Result
+Addition / Concatenation10 + 5 / "Hello " + "World"15 / "Hello World"
-Subtraction20 - 713
*Multiplication4 * 3.514
/Floating-point division10 / 42.5
divInteger division10 div 42
mod or %Modulo (Remainder)10 mod 42

In addition to basic arithmetic operators, GML includes compound assignment operators that combine a math operation with direct variable reassignment. Writing 'puntos += 10;' is semantically and functionally identical to writing 'puntos = puntos + 10;', but it is more concise and readable. Similarly, you can use -=, *=, and /=. To increment or decrement a value by exactly 1, use the increment (++) and decrement (--) operators, frequently utilized in counters (such as: municion--;).

GML follows standard mathematical order of operations: multiplication, division, and modulo operations evaluate prior to addition and subtraction. If you need to override this precedence or clarify that an addition must occur before a multiplication, you must use parentheses. For example, 5 + 2 * 10 evaluates to 25, whereas (5 + 2) * 10 evaluates to 70. In game programming, when in doubt, group complex operations within parentheses to avoid subtle logic errors.

Integrated Practical Case: Initial Setup of obj_player

To reinforce these principles, let us continue building the foundation for the retro platformer project used throughout this course. In Module 2, we imported the player sprite (spr_player), created the player object (obj_player), and placed an instance in the initial room (rm_nivel_1). At present, the instance remains static. We will now establish its core variables and see how local and instance variables interact across Create and Step events.

Open the 'obj_player' asset in your GameMaker Workspace. Ensure that 'spr_player' is assigned as its sprite. Click the 'Add Event' button and select the 'Create' event. In the code editor that opens, enter the initial variables that define the character's properties and movement attributes.

// ---------------------------------------------------------
// OBJECT: obj_player
// EVENT: Create
// PURPOSE: Initializing character statistics
// ---------------------------------------------------------

// Vital stats
puntos_vida_maximos = 100;
puntos_vida_actuales = puntos_vida_maximos;
estado_vivo = true;

// Base movement (in pixels per frame)
velocidad_caminar = 4;
velocidad_carrera = 6.5;
fuerza_salto = 9;
gravedad_base = 0.35;

// Economy and level progression
monedas_recolectadas = 0;
puntuacion_total = 0;

// Debug verification output
show_debug_message("obj_player initialized successfully with " + string(puntos_vida_actuales) + " HP.");

The 'show_debug_message()' function takes a string argument and prints it directly to GameMaker's bottom Output Console. Notice how we used string(puntos_vida_actuales) to convert the real number into text before concatenating it with the rest of the string using the addition (+) operator. This function is your quickest diagnostic tool for verifying variable states at runtime.

Next, add a 'Step' event to 'obj_player'. As covered in the previous module, the Step event executes continuously, once per game frame (by default, 60 times per second). In this event, we will execute a temporary calculation using local variables to simulate passive survival score accumulation using arithmetic operators.

// ---------------------------------------------------------
// OBJECT: obj_player
// EVENT: Step
// PURPOSE: Continuous frame logic and temporary calculations
// ---------------------------------------------------------

// Check if the character is alive
if (estado_vivo == true)
{
    // Declare local variables to calculate a mathematical bonus
    var _tasa_puntos_segundo = 10;
    var _fotogramas_por_segundo = 60;
    
    // Fractional score calculated for this specific frame:
    var _incremento_fotograma = _tasa_puntos_segundo / _fotogramas_por_segundo;
    
    // Accumulate into instance variable using compound assignment
    puntuacion_total += _incremento_fotograma;
}

Practical Exercises

Complete the following three hands-on exercises to solidify syntax declaration, scope handling, and arithmetic operations in GML Code before moving on to conditional control flow.

Summary

  • GML Code is GameMaker's standard professional development language, relying on sequential statements that should conclude with a semicolon (;).
  • The language is dynamically typed and case-sensitive, recognizing real numbers, strings, booleans, and special engine constants like noone and undefined.
  • Instance variables (declared in events like Create) persist throughout an object's lifecycle and remain accessible across all of its events.
  • Local variables (declared with 'var') are transient: they exist only while their parent block of code executes and are discarded immediately from RAM.
  • GML provides standard arithmetic operators (+, -, *, /) alongside integer division (div) and modulo (mod or %), which are crucial for grid layouts, inventories, and counters.
  • Compound assignment operators (+=, -=, *=, /=) and increment/decrement operators (++, --) streamline code readability and maintenance.
  • The 'show_debug_message()' function paired with 'string()' conversion provides the foundational tool for real-time console debugging.

Practise what you learned

Exercise 1 · 1 point

What happens to a variable declared with the 'var' keyword in GML once the event or code block where it was written finishes executing?

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

Exercise 2 · 1 point

To declare a temporary local variable that belongs only to the current event, you prefix it with the keyword . Similarly, if you want to concatenate a number with a text string without causing a type error, you must convert that number using the built-in function.

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

Exercise 3 · 1 point

Match each GML identifier or syntax element with its corresponding definition:

  • noone
  • undefined
  • //
  • /* ... */
You are practising without an account: nothing is saved. Sign up to keep your progress.

Sign in to save your progress — Sign in