Variable references in GameMaker (2023 edition)

A familiar dinosaur pointing at a slightly out-of-focus word "variable" in the back

It's been 5 years since my "variable references in GameMaker" post and people are still interested in passing variables by reference, so this I'm giving the idea a little refresh with new tools that are now available.

The idea

The concepts didn't change from the last time, but GameMaker did - you can read more about this in another post of mine, but what matters for this is that we have structs and constructors now.

Getting it done

So we can make a little constructor with get/set functions that will call reflection functions to get/set the variable.

function VarRef(_inst, _varname) constructor {
    inst = _inst;
    varname = _varname;
    static get = function() {
        return variable_instance_get(inst, varname);
    }
    static set = function(_value) {
        variable_instance_set(inst, varname, _value);
    }
}

This will also work for structs (variable_instance_get and variable_struct_get are synonyms for the same function), and we'll be able to do

var my_x = new VarRef(self, "x");
x = 100;
show_debug_message(my_x.get());
my_x.set(200);
show_debug_message(x);

Other wrappers can be similarly accomplished:

function ArrayItemRef(_arr, _index) constructor {
    array = _arr;
    index = _index;
    static get = function() {
        return array[index];
    }
    static set = function(_value) {
        array[@ index] = _value;
    }
}

and used:

var arr = [1, 2, 3];
var item = new ArrayItemRef(arr, 1);
show_debug_message(item.get());
item.set(4);
show_debug_message(arr[1]);

Making it shorter

val = ref.get() and ref.set(val) are good, but what if you wanted to make this shorter?

Obviously you can't have val = ref and ref = val because that's not supported, but how about val = ref() and ref(val)?

Let's have a look at the code:

function variable_ref_create(_inst, _varname) {
    with ({
        __inst: _inst,
        __varname: _varname,
    }) return function() {
        if (argument_count > 0) {
            variable_instance_set(__inst, __varname, argument[0]);
        } else return variable_instance_get(__inst, __varname);
    }
}

When you create a function "value" in GameMaker using function(){}, it automatically binds to the current self, and code inside the function will execute in context of that instance/struct.

By doing with (struct) return function(){}, we can create a little struct (to hold information about what variable of which instance we need) and bind a function to it.

Calling this bound function with no arguments will then return the current value of the variable and calling it with one argument will change its value to a new one.

This can subsequently be used like so:

var my_x = variable_ref_create(self, "x");
x = 100;
show_debug_message(my_x());
my_x(200);
show_debug_message(x);

Similarly, this can be applied to other types of references:

function array_ref_create(_array, _index) {
    with ({
        __array: _array,
        __index: _index,
    }) return function() {
        if (argument_count > 0) {
            __array[@ __index] = argument0;
        } else return __array[__index];
    }
}

and used:

var arr = [1, 2, 3];
var item = array_ref_create(arr, 1);
show_debug_message(item());
item(4);
show_debug_message(arr[1]);

Pretty neat and a little cursed, isn't it?

Related posts:

2 thoughts on “Variable references in GameMaker (2023 edition)

  1. Something I also like to add to the mix, is that variables can now be hashed from either structs or instances. Which does matter if you’re looking up the same variable multiple of times. (This applies to 2023.4+)

    function VarRefHash(_struct, _name) constructor {
        inst = _struct;
        hash = variable_get_hash(_name);
    
        static get = function() {
             return struct_get_from_hash(inst, hash);
        }
    
        static set= function(_value) {
             struct_set_from_hash(inst, hash, _value);
        }
    }
    

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.