GameMaker: get/set variable value by name

This is an average-length post covering the aspects of reading/writing variables by their name (as string) in each version of GameMaker.

First things first

Before we start, consider the following: do you really need to access variables by names? Since the entire concept is part of reflection, it isn't the fastest, and neither something that you should do often. To highlight a few common cases:

  • If your variables of interest end in numbers (v1, v2, ...), you might want to use arrays instead. As in, seriously.
  • If you intend to add/manipulate entirely arbitrary variables (e.g. names input by user), you might want ot use ds_maps instead.
  • If you intend to make "a reference" to a variable, you could either wrap it into a 1-element array... or reconsider if and why do you need to do that at all.

Obviously, there are also valid use cases, which is why this post was written.

GameMaker ≤ 8.1

Older versions of GameMaker only ran in interpreted mode on Windows and had a set of variable_local_ and variable_global_ functions, which could be used to variously work with local (instance) and global variables accordingly.

Therefore you could do

some = 4;
val = variable_local_get("some"); // 4
variable_local_set("some", 5);
show_debug_message(some); // 5

and that would have worked fine.

GameMaker: Studio ≤ 1.4.1763

GM:S brought nearly countless additions to GameMaker, including compilation to JS for making HTML5 games with the program.

However, YoYo Games had made a fairly reasonable decision of not writing a GML->JavaScript compiler (or interpreter) in JS itself, which meant that the interpreter-related functions had to go, variable_ functions included.

And so it would have seemed like this functionality is now gone for good. But, not all is bad. Since GML is still a dynamic language, if you know that there is a variable of kind on an instance, you can read it from anywhere via direct .field access. Therefore, you could very well just have a little script that compares a variable name to each name that you could need and returns it (from the instance running the script) if that is so:

/// variable_self_get(name)
switch (argument0) {
    case "test": return test;
    case "some": return some;
    case "x": return x;
    // ...
    default: show_error("variable_self_get: " + argument0 + " is not a known variable.", true);
}

Of course, if you have many variables, this can get a bit slow(-ish), since a switch on strings can only compare them one by one. So there's a fancier trick: first you make a set of scripts with names matching variable names (prefixed) that can read/write the according variables:

/// vget_some()
return some;
/// vset_some(value)
some = argument0;

And you make a script that assembles a pair of ds_maps for quick lookup of them:

/// variable_self_init()
global.g_get = ds_map_create();
global.g_set = ds_map_create();
for (var i = 0; script_exists(i); i++) {
    var s = script_get_name(i);
    if (string_length(s) > 5) {
        if (string_copy(s, 1, 5) == "vget_") {
            global.g_get[?string_delete(s, 1, 5)] = i;
        } else if (string_copy(s, 1, 5) == "vset_") {
            global.g_set[?string_delete(s, 1, 5)] = i;
        }
    }
}

And make two scripts to wrap retrieval and execution of a script from the according map to get or set the variable by it's name:

/// variable_self_get(name)
return script_execute(global.g_get[?argument0]);
/// variable_self_set(name, value)
script_execute(global.g_set[?argument0], argument1);

Enabling you to do

some = 4;
val = variable_self_get("some"); // 4
variable_self_set("some", 5);
show_debug_message(some); // 5

Much like you would in older versions with variable_local_* functions.

A point for criticism for this approach is that you have to define those small helper scripts for each variable name of interest, but that can be helped with a small code generator:

(add variables to the left field and copy the generated code for scripts from the right field into a GML file. Importing the file into a project will unpack individual scripts from it automatically)

All in all, while it takes some workarounds, the end result is good enough for most purposes.

GameMaker Studio 2 and GameMaker: Studio ≥ 1.4.1772

The newer versions of GameMaker bring back the variable_ functions, albeit in a slightly different form - variable_instance_ functions take an instance to check the variable in, meaning that for the same code you would do

some = 4;
val = variable_instance_get(self.id, "some"); // 4
variable_instance_set(self.id, "some", val + 1);
show_debug_message(some); // 5

Conveniently, this was also backported to GMS 1.x among the various fixes.

In conclusion

While not always necessary for tasks, variables can be both read and written by their (string) name in all versions of GameMaker, permitting for curious things if used with care.

Have fun!

Related posts:

3 thoughts on “GameMaker: get/set variable value by name

Leave a Reply to fjg Cancel 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.