GameMaker: On global variables

A little too often I see confusion about what's the difference between different ways of storing global variables in GameMaker, such as global, globalvar, storing in objects, and macros.

So I wrote a small post to show the technical details and clean that up.

`global` and `globalvar`

First things first, on globalvar. Some people seem to think that it's somehow different (or even more better) than using the global. prefix. Upon compilation, it's exactly the same.

Consider the following piece of code:

global.via_global = 1;
globalvar via_globalvar;
via_globalvar = 2;
show_debug_message(global.via_globalvar);

It produces the following bytecode (cleaned up and hinted for your convenience):

// global.via_global = 1:
 0  01000F84           ConstInt(1)
 4  FBFF2545 000000A0  Set("via_global", Global, Normal)
// via_globalvar = 2:
12  02000F84           ConstInt(2)
16  FBFF2545 010000A0  Set("via_globalvar", Global, Normal)
// show_debug_message(global.via_globalvar):
24  FBFF05C2 010000A0  Get("via_globalvar", Global, Normal)
32  010002D9 00000000  Call("show_debug_message", 1)
40  0000059E           Discard

As can be observed, bytecode produced for global and globalvar is exactly the same - the only thing differing is the variable ID (00 and 01 accordingly). Furthermore, this means that variables declared via globalvar can still be accessed via the global prefix (as shown).

So you evidently do not win any performance with globalvar, only a bit of time by not typing global. every time. But at what price?

Problems with `globalvar`

To understand why use of globalvar is discouraged, first you need to understand how it works to begin with. When you do variable access without prefix or declaring something as a local variable (via var), an instance' variable is accessed:

// some = 1:
 0  01000F84           ConstInt(1)
 4  FFFF2545 000000A0  Set("some", Self, Normal)

However, if you have a globalvar declaration for a variable anywhere, it overrides the default behaviour, and variable access will use a global variable instead:

// some = 1;
// globalvar some;
 0  01000F84           ConstInt(1)
 4  FBFF2545 000000A0  Set("some", Global, Normal)

Furthermore, globalvar' variables are not specifically highlighted in code editor so far, thus you can't easily tell if a variable is marked as globalvar or not (other than specifically looking through code).

The trouble becomes increasingly more apparent when working in a team or using extensions, where both original and added code may have their own globalvar declarations that can change the behaviour of the other.

Object' variables

Another question that is commonly brought up is whether it's practical to store your variables in objects and access them as obj_some.variable. In short, while it might be a little slower than using global variables, it's definitely nowhere on the list of worst things you can do in your code.

Consider the following code:

global.some = 3;
obj_test.some = 3;

Bytecode output for it is practically the same,

// global.some = 3:
 0  03000F84           ConstInt(3)
 4  FBFF2545 000000A0  Set("some", Global, Normal)
// obj_test.some = 3:
12  03000F84           ConstInt(3)
16  00002545 000000A0  Set("some", Stack, Normal)

The only difference is the 16-bit signed "context" in the "Set" action, being -5 for global (you can actually do (-5).some = 1;, to say), and 0 (object' index) for object access.

Of course, behind the scenes it's going to pick the singular special instance for global or look up the first object' instance for object access, so there would be a minor performance difference.

Generated C++ code for YYC follows the similar suit (same as with bytecode, cleaned up for clarity):

// global.some = 3:
YYRValue& global_some = g_pGlobal->yyvars[kVARID_global_some];
global_some = 3;
// obj_test.some = 3:
YYRValue o0_some = 3;
YYGML_Variable_SetValue(0, kVARID_self_some, (int)ARRAY_INDEX_NO_INDEX, &o0_some);

As can be seen, for global prefix the code pulls a variable out of a special global container, and assigns a new value for it, while for object prefix it calls a function to find an object' instance and assign a new value to it's variable.

Macros

For unknown reasons, macros remain underused for variables, despite being noticeably better for the task. The way macros work in GameMaker is simple - macro' value is put in place of it's uses. So, if you have a macro called object_name with value object_get_name(object_index), writing

draw_text(x, y, object_name);

Would be exactly the same as if you wrote

draw_text(x, y, object_get_name(object_index));

And you get auto-completion and syntax highlighting too, which is good.

Same can be applied to global variables - if you make a macro named via_macro with value global._via_macros (underscore prefix because macro may not mention it's own name in value),

via_macro = 4;

The result would be the same as if you used the global. prefix, combining the best of both methods:

 0  04000F84           ConstInt(4)
 4  FBFF2545 000000A0  Set("_via_macro", Global, Normal)

On a related note, if you find no excitement in filling out macros, and don't find the process of using global. prefix either, you can always make a "shortcut" - make a macro named g (for example), the value of which would be global. Then writing g.some would yield the same result as if you wrote global.some.

In conclusion

global is fine. globalvar should be avoided. Storing variables in objects is okay if you want to. Macros can be used to have global variables that are both easier to use and have auto-completion.

Have fun!

Related posts:

3 thoughts on “GameMaker: On global variables

  1. I’ve been using macros a lot more lately but I had no idea about the global shortcut, that’s incredibly handy! Thanks for the awesome info! 😀

  2. Thanks. Good stuff and the info on macros will definitely come in handy. I tend to avoid global variables in favor of object references but I am thinking macros will definitely be used in my future endeavors.

Leave a Reply

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