[GMS-S] Variable Scoping In GameMaker

Level: BeginnerIntermediate

Author: Mark Alexander

Learning Outcomes:

  • Understand local, instance and global variables
  • Understand variable scope
  • Learn about some GML keywords

Description:

This article briefly explains the dos and don'ts of scoping in GameMaker: Studio, however, before we start getting into details about how scoping works, let's just go over exactly what we mean by the term. Scoping is all about the visibility of variables, as GML or DnD code is executed the variable names that are available to be read or written to change - what is available now is said to be in scope. So when a create event is executed any variables that are referenced are from the scope of the instance that is being created at that time.

Various GML or DnD constructs can change the scope of variables and this article will discuss these and some of the issues with them, but before continuing it would be useful to set out some definitions that we use when discussing scope and hopefully clear up some confusion that has unfortunately existed within GML (the GameMaker Language) over the years. These definitions will be used from now on with GML:

Local Scope - if a variable is declared using the var keyword then it is only visible within this script or event and it is at local scope.

Global Scope - if a variable is declared using globalvar or is referred using the global prefix then it is at global scope and is visible from all code areas

Instance Scope - if a variable has NOT been declared with var or globalvar and is NOT referred to using global or any other prefix then it is an instance variable and is in the instance scope.

Explicit Scope -If a variable is referred to with a prefix using the DOT syntax i.e. obj.varname, then that variable is using explicit scope, the prefix itself is the context that the variable is referenced from.

When a variable is referenced in GML or DnD the scope is used to work out where to read or write the variable and ensure that variables are consistently named.

 NOTE: You can find more detailed explanations of each of the above variable types here - Variables and Variable Scope

Caveats with Studio scoping

Local scope is local across the whole event, even if the variables are within different code actions - this can be confusing coming from earlier versions of GameMaker where variables are local to actions (not the whole event). This is due to optimisations within Studio that concatenates all the actions together and turns DnD into code, so the whole event becomes a single script that is executed. 

The Globalvar keyword can hide local and instance variables, if the same name is used for a globalvar variable as one used for local and instance variables then strange things can occur! The strangeness will be different between HTML5 and YYC as well as the JavaScript and C++ languages since they all have different ideas on how scoping works so there can be some undefined results. We will be introducing an error to ensure that this is disallowed in future versions, but it is a bad idea and something you should be wary of. (The whole globalvar keyword is a bad idea (tm) and will be removed in a future version of GML, but for now we have left it in for backward compatibility and bad habits).

Using an object name or id as a prefix will work differently across different platforms too and also work differently if the variable is being read or written to, so it is recommended that you do not do thisIt is better (and more sensible) to use the "with " syntax rather than an object prefix). So generally do not do this:

objPlayer.x = 5;

or

objPlayer.x = enemy.x;

 As it will do different things on different platforms. In reality it is much better to do this:

with( objPlayer )
{
x = enemy.x;
}

which will work consistently across all target platforms.

DnD scope changers

On DnD actions changing the "Applies To" entries will modify the scope of any variable references within that action.

 picpicpic.png

GML Keywords That Change Scope

Some GML language elements change the scope and how variables are looked up the most notable one of these is the "with" keyword. This can be used in many ways but the essence of it is that the statement body is executed in the context of the argument of the "with".

With (argument) <statement>

So any instance variables referenced by the statement are now using the argument as the instance, i.e:

with( <instance id> )
{
++i;
}

will refer to the variable i within the instance referred to in the argument. An object name or id can also be used:

with( <object id> )
{
++i;
}

This will actually loop through every instance of the object and increment the instance variable i in each one. In this way it can be treated as a cheap "for" loop replacement, and using this with object parenting is a good way to iterate through all the instances of a particular object type.

An Example

In this example, "Mike" has a specific problem with scoping that has left him scratching his head. Coming from a C++ background he has become used to the scoping rules in that. Now one of the major differences in C++ is that variables are scoped within blocks of code (denoted by curly brackets), if a variable declaration is within one of these blocks then each time that block is entered the variable will be initialised as new as the block is entered. This is not true in GML as local scope is the whole event (or script) and not the code block that it is in, and can lead to confusion.

So the code that caused confusion was like this: 

var m = ds_map_create();
for( var i = 0; i<5; ++i)
{
var a;
a[0] = 0;
a[1] = 1;
ds_map_add_value( m, “key” + string(i), a );

Coming from a C++ background Mike expected the variable "a" to be reset within the for loop and so a different array would be added to each entry of the map, BUT because of the GML scoping rules the variable a is NOT reset within the for statement but is only scoped within the full event, this meant that it was the same array that was being added to each of the map entries.

Fortunately there was an easy fix for Mikes problem, by a simple change when the variable is defined: 

var m = ds_map_create();
for(var i = 0; i<5; ++i;)
{
var a=0;
a[0] = 0;
a[1] = 1;
ds_map_add_value( m, “key”+string(i), a );
}

By adding the "a = 0" to the declaration of the local variable "a" we ensure that it is initialised (at the location of the declaration) then each run through of the for loop the variable a is set to the real value 0 which breaks any association with the array (which would now be in the map so the association is broken). Which was the behaviour that Mike intended and a C++ programmer would expect.

Conclusion

When using GameMaker:Studio it is a good idea to be acquainted with the scoping rules as it can avoid a lot of pain. The simplicity of GML is both a strength and a weakness - leverage that strength and avoid the weakness in your own code.

 

 

 

Have more questions? Submit a request

0 Comments

Article is closed for comments.