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.
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.
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.
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.