Variable Scope
Home Site home Platforming Auto-sprites Vectors Path Finding quaternions.htm Variable Scope Water Panic Super Bouncy Ball Balls of Doom Contents k8055 GM Downloads

 

Understanding Variable Scope in Gamemaker

Or "Why can't I access a variable " here" when I can access it fine "there".

Gamemaker has some special rules about variables in objects and scripts. These can be tricky to get used to, as they differ from the rules of other more conventional programming languages.

A variable's scope is the range of circumstances under which that variable can be accessed. Many modern computer languages have tight rules for when a variable is accessible, particularly when using object oriented programming. Gamemaker has weaker scope rules, meaning that generally a variable is accessible if you know it exists, it isn't actively concealed

Global
Self
Other
Local

Global variables are never in scope, they must be accessed by prefixing the variable with "global.<variable>".

GM Studio introduces the globalvar directive, but it is slightly broken and must be used with caution

The statement "with (global)" is not permitted, so there is no "global scope".

Self variables are the variables of the current object. These are accessed by default unless a local variable with the same name exists.

"Self" variables can be accessed directly from within that object's events. They can also be accessed directly from within scripts called by the object's events. This is unusual in a computer language as it is more normal for object variables to go out of scope when a non-member procedure is called. Essentially scripts called from within events behave as though they belong to the object.

"Other" variables are the variables of a second object that is involved with the current object in some way. This can be caused by a collision or by the use of a "with" construct. One quirk of a "with" block is that the variables of what had previously been the current instance go out of scope (becoming "other") for the duration of the block.

Script variables (local variables or "var" variables) are specific to that script, they are not available to other scripts even when called from the script that owns the "var" and they are not available to code that is run using execute_string(). However they are not affected by "with" so copying important values to locals can avoid the need to keep prefixing everything.

The Dot (and how it really works).

In Gamemaker the dot is an operator, just like add, or subtract, or equals. It takes the following form:

<number> . <variable name>

and allows an object's events to manipulate variables in other objects, or access global variables. Objects do not hide their variables, so if you know a variable's name and the instance it is stored in you can access it.

Note that the reserved words true, false, self, other, all and noone all have fixed numeric values as follows:

Symbol Value
true 1 Also other positive numbers are treated as true
false 0 Also negative numbers are treated as false
self -1
other -2
all -3
noone -4 This value is returned by collision functions when there is no object

Object types are assigned low numbers starting from zero. Using the dot on an object type allows a variable to be accessed in all the instances of that type (this technique is heavily discouraged in GM Studio, use "with" instead). If the variable is written to it will write the same value to all the instances. If the variable is read it will probably return the value in the first instance.

Since object types are numbers it is likely that if you accidentally "dot" a number it will correspond to a valid type. This can be a problem when putting instance IDs in variables.

You can pass a whole instance to a script by passing its instance ID. This functions a lot like a "pointer" in other languages.

To assign a self-reference to a variable use variable=self.id; or simply variable=id; but NOT variable=self. In almost all the scopes where self is valid it is also implied. The only time I could see a need to use self is if there is already a local variable with the same name.

To assign a reference to the "other" object use variable=other.id and not variable=other; This might be used for a "remember what hit you" A.I. feature.

If you do want to store an instance ID in a variable of another instance you should think what will happen if the instance is destroyed. Interpreted GM will protect against referencing non-existent  instances but the bugs it creates can be hard to find.

Instances are assigned id beginning at 100000, so they are usually easily recognisable. Object types are assigned low numbers starting from zero so they are hard to distinguish from ordinary values, or from path IDs, sprite IDs etc.

Warning: Since there is no difference between an object type and a number the dot operator will work on numbers so if you accidentally use the dot on the wrong variable and it just happens to contain a valid object type then the operation will not throw an error but will affect all objects of that type. This could cause some very strange results, especially as object types are represented by low integers.

What is wrong with globalvar

There's a large potential problem with globalvar depending on how it is interpreted and I believe the interpretation may vary between targets.

The interpretation that I believe fits the description and the one that appears to apply to interpreted targets is that prior to the globalvar being executed a variable will have its default scope, usually an object variable. After globalvar any occurances of that name will be converted to global. This could have odd side effects if a variable of the same name has been used elsewhere as before being made global it will refer to an object but after the globalvar command the same code could now be accessing a global instead.

The interpretation that is safest and the one that I believe will apply to compiled targets is that using globalvar in a script will convert a variable name to global for the rest of that script only.

It is worth noting that global variables can be a source of annoying problems as something in the program must be responsible for initializing them.