Structs, Methods & Constructors in GML

Elevate your GameMaker architecture with lightweight objects.

The Post-2.3 Revolution

Since the monumental 2.3 update, GameMaker Language (GML) evolved from a purely script-and-object-based system into a much more flexible, modern language. At the heart of this shift are Structs, Methods, and Constructors.

Instead of creating an entirely new Object in the asset browser for simple data containers (like inventory items, weapons, or dialogue nodes), you can now define lightweight data structures directly in code.

1. Structs: Lightweight Data Containers

A struct is essentially a variable that holds a collection of other variables. If you are familiar with JSON or JavaScript objects, GML structs will feel right at home.

Creating a Basic Struct

You can create a struct using curly braces {} and separate properties with commas.

var my_weapon = {
    name: "Rusty Sword",
    damage: 15,
    durability: 100
};

Accessing Data

You access the data inside a struct using dot notation, exactly like you would access variables inside an instance.

show_debug_message(my_weapon.name); // Prints "Rusty Sword"
my_weapon.damage += 5; // Upgrades the damage

2. Methods: Functions with Scope

A method is simply a function that is bound to a specific scope (like an instance or a struct). When a method runs, the self scope refers to the struct that owns it.

Adding Behavior to Structs

You can define functions directly inside your struct definition. This encapsulates your data and the logic that modifies it together.

var my_player = {
    hp: 100,
    max_hp: 100,
    
    take_damage: function(_amount) {
        hp -= _amount;
        if (hp < 0) hp = 0;
    },
    
    heal: function(_amount) {
        hp += _amount;
        if (hp > max_hp) hp = max_hp;
    }
};

// Using the methods:
my_player.take_damage(20);

3. Constructors: Stamping out Structs

While making structs manually with curly braces is great for one-off data, it becomes tedious if you need 50 different weapons or enemies. This is where Constructors come in. They act as "blueprints" for creating standardized structs.

Defining a Constructor

A constructor is defined like a normal function, but you add the constructor keyword at the end of the declaration. Inside, you define the variables and methods that every struct made from this blueprint should have.

function Weapon(_name, _damage) constructor {
    name = _name;
    damage = _damage;
    
    static attack = function() {
        show_debug_message("Swung " + name + " for " + string(damage) + " damage!");
    }
}

Note the use of the static keyword for the method. This ensures that the function is created only once in memory and shared across all instances of this struct, saving resources!

The new Keyword

To create a struct from your constructor, you use the new keyword.

var sword = new Weapon("Iron Sword", 25);
var axe = new Weapon("Battle Axe", 40);

sword.attack(); // Swung Iron Sword for 25 damage!

Inheritance (The : Operator)

Constructors can inherit from other constructors. This allows you to create base classes (like an Entity) and specialized classes (like a Player or Enemy) without rewriting code.

function MagicWeapon(_name, _damage, _mana_cost) : Weapon(_name, _damage) constructor {
    mana_cost = _mana_cost;
    
    static cast_spell = function() {
        show_debug_message("Spent " + string(mana_cost) + " MP!");
    }
}

var staff = new MagicWeapon("Fire Staff", 10, 5);
staff.attack(); // Inherited from Weapon
staff.cast_spell(); // Unique to MagicWeapon