Programming in GameMaker

The first programming language I wrote significant code in was a dinky little scripting language attached to a GUI-based game development program called Game Maker (since rebranded to GameMaker: Studio). The program provides graphical interfaces for creating objects and defining their behaviour by dragging, dropping, arranging and filling in fields, and you can quite happily use this and only this to make a large variety of games: GM works on abstract enough concepts that you’re not restricted to one or two genres of game like you might be if developing in AGS or RPGMaker.1

The interface

Here’s how the system works: Games are made up of rooms containing instances of objects, which in turn contain events, which in turn contain actions. An example: a room called level1 contains an instance of an object called player which, in response to the Left Arrow Key Pressed event, performs the action of Start moving in a direction (left).

In order to construct this, you’d need to create a room like this:

Example room

And an object like this:

Example object

The action shown on the left-hand side was created by dragging a “move in a direction” action from the palette on the right into the “left arrow key” event and defining the desired direction as left in the form that pops up. You can use multiple of these actions per event block, and a few special actions allow you to create if/else logic: so you can say, for example, “if the player is confused, move left, else move right”, like so:

DnD if-else logic

This is a fantastic system for novice users and non-programmers, who might be intimidated by the blank screen and blinking cursor of a traditional programming interface. The tabbed action palette on the side not only provides users with a way of creating logic that doesn’t feel like writing code, but also gives them a good idea of the possibilities available to them and provides discoverability in a way that text interfaces usually don’t.2

But it’s not a perfect system, and all the dragging and dropping quickly gets tedious and hard to read as soon as you have something more complex than a single if-else statement to express (plus there are no loops!) and so Game Maker comes with a special action called “execute some code”, which allows you to dispense with the drag-and-drop and just write your logic in GM’s C-like scripting language, GML.

Some GML

The language

And now we get to the main subject of this article: the strange beast that is GML, my first programming language.

On the surface, GML looks a lot like any other C-like, imperative language. Once you get into it, though, the differences between GML and pretty much everything else become readily apparent.

The parser is quite forgiving, as befits a language for novice programmers – you can put in or leave out semicolons after statements, wrap expressions in if statements in brackets or leave them off, and even substitute the curly braces around blocks for begin and end keywords. It will also figure out what you mean if you use a single = sign in an expression, so this:

if (a = 1)
	x += 2

is equivalent to this:

if (a == 1)
	x += 2

The common C-style operators ++ and -- (equivalent to += 1 and -= 1, respectively) were only introduced in GameMaker: Studio, and as a consequence I never got in the habit of using them. Which is not really such a bad thing, as I do most of my programming in Python and Ruby these days, neither of which supports the operators.

Data types

GML has two basic data types: real numbers and strings. The first stands in for most of the other data types: a real number can be an integer or a float and booleans are represented by 0 and 1. As a result, we have this neat parlour trick for toggling a boolean:

x = -x+1

I mean, it requires more typing and is less clear than x = !x (which you can also do in GML, but I didn’t know about till about CS102), but it does feel kinda clever after you stop to puzzle it out after encountering it for the first time. I know that’s what I like in my codebases!

Real numbers also co-opt objects and other game resources, which are referred to by their numeric IDs, determined by the order in which they were created. So the first object you make will have ID 0, the second ID 1, the third ID 3, and so on. This system of IDs is used for most things that might be types or objects in a more traditional language. In the case of objects, -4 is the value used for “no object”, helpfully mapped to the keyword noone.

So instead of passing around pointers to objects, or object types, you just pass around their numeric ids and use them in functions that call for an object parameter.

I sometimes miss this aggressive simplicity in other languages, where I feel like I spend an inordinate amount of time converting and coercing types into other types.

GML also has arrays, but they’re restricted in a number of ways that warped my understanding of arrays when I started learning a traditional language. Here’s what you can do with an array in GM:

arr[0] = 1
arr[1] = 2
arr[2] = 3

two = arr[1]

zero = 0
one = arr[zero]

Or if you have a special two-dimensional array:

arr[0,0] = 1
arr[1,1] = 2
arr[2,2] = 3

two = arr[1,1]

zero = 0
one = arr[zero,zero]

One-dimensional and two-dimensional arrays are treated differently from each other, and very little syntactic sugar is provided, so you can’t do any of this sort of thing:

arr = [1,2,3,4] //syntax error

But must instead do this (or something with a for loop):

arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4

You can nest arrays, but GM provides no syntactic support for working with them, so it never occurred to me to attempt it until I started learning Python, my second programming language. As arr[0][1] produces a syntax error, you need to do this instead:

nested_arr = arr[0]
nested_value = nested_arr[1]

And at that point, why bother?

A further quirk of GM’s arrays is that you can use strings as array indices.

arr['word'] = 1
one = arr['word']

I don’t know what this does in the background, and have always eyed it with deep suspicion. But it’s been used in some other people’s code to simulate dictionaries/associate arrays/hashmaps/key-value stores and seemed to work well enough there.

GameMaker: Studio introduced enums. These are all global in scope, and are terribly clever and useful, but I always forget they exist and so have barely used them.

enum months {
	January,
	February,
	March,
	April,
	May,
	June,
	July,
	August,
	September,
	October,
	November,
	December
	}

this_month = months.October

Behind the scenes, months.January is just the real number 0 – but you can also assign enum entries specific values if you want.

enum numbers {
	almostpi = 22/7
	}

This provides another way of simulating key-value stores.

enum a
{
	word,
	sentence,
	paragraph
}

arr[a.word] = 1

Oh, and there’s also the undefined type, which causes errors and sometimes unexpected behaviour. This is also new to GameMaker: Studio.

Before there was undefined, GM provided a checkbox in the Global Game Settings menu which allowed you to treat all uninitialised variables as 0. Obviously, this sounded very appealing to novice programmers, and prevented a lot of game-crashing errors, but you can imagine the chaos which ensued in a language where numbers were used for just about everything. Fourteen-year-old me was convinced to stop using it by a resultant bug in an inventory system he wrote that filled up every slot with a copy of the player object.

Objects

Just like GM’s arrays are not really arrays as we commonly think of them, GM’s objects are not really objects in the parlance of object-oriented programming (OOP). What GM calls an object is closer to what other languages call a class: one uses the UI to create objects with various behaviours and attributes, and then one places instances of these objects/templates in rooms, much like one might write a class definition and then instantiate objects of that class in a more traditional programming environment.

But even this analogy only holds until it becomes too abstract and inconvenient. Let’s say you have only one instance of a given object present – probably an instance of obj_player or something of that sort. How do you refer to this instance’s x position?

In most OOP languages, you’d something like this:

class obj_player
{
	// x is defined as a public variable or a private one with accessors
}

theplayer = new obj_player()

theplayer.x++ // or something more verbose if x is private and this is Java

You can do something similar in GM, namely:

theplayer = instance_create(x,y,obj_player)

theplayer.x += 1

Behind the scenes, theplayer is a number greater than 100 000. Instances are assigned numeric IDs according to the order in which they created within a given room. As far as I can tell, starting at 100 000 is a measure taken to prevent their IDs from clashing with those of objects.

And speaking of clashing with objects: if you know there’s only ever going to be a single instance of the player, you can do this instead of the code above:

obj_player.x += 1

Convenient, perhaps, but makes you draw a sharp breath through gritted teeth if you think about how silly what you’re doing is in terms of normal OOP: rather than involving any kind of class variable or method, this is just shorthand for “the first instance of obj_player in the room”.

And on that note, GM doesn’t care for your OOP design principles, and so has no concept of private variables – you can reach into an object and change its variables wherever and whenever you like. I think this was a good design choice for a language that was intended to be simple and easily learnable and useable by children.

Because GM objects are defined in terms of events and actions, GM also has no concept of either private or public methods. If you need to go further than changing variables and want to have an instance run some code, you use the with keyword to assume direct control over it.

with (obj_player) //first instance of obj_player
{
	// now running in obj_player's context
	x += 1
	y += 1
	instance_destroy()
}

If this really upsets you, you can simulate a method call with something like:

with (obj_player)
{
	event_user(0)
}

This will invoke the action in the 0th user defined event defined in obj_player. You only get 16 of these events, and there’s no way to give them friendlier names, or to pass them arguments. So a better idea might be calling a script:

with (obj_player)
{
	player_die(somearg)
}

…except that scripts are separate resources and cannot be linked to objects in any way, and so are even less like methods than user defined events.

There was, briefly, a better way of simulating method calls. More powerful user-defined events called triggers were introduced in Game Maker 8. For all intents and purposes, these were custom events: the editor allowed you to define a trigger with some expression. This trigger would then be available as an event in the object dialogue and could be assigned actions. These actions would execute when the trigger’s expression evaluated to true. You could also specifically invoke triggers in code, and in that way use them as an unlimited store of named user-defined events (i.e. better “method calls”).

Triggers were very short-lived – they disappeared in GameMaker: Studio. I’m pretty sure I was one of the only people who used them.

Data structures

Because GM’s arrays are limited and quite slow (see Datatypes above), a number of data structures are provided as faster, more dynamic alternatives. When initialising a data structure, you can choose between these exciting options:

  • ds_stack: The classic First-In, Last-Out stack.
  • ds_queue: The classic First-In, First-Out queue.
  • ds_priority_queue: Like a queue, but order is determined by the priority number associated to each element.
  • ds_map: A key-value store.
  • ds_grid: A 2D array with lots of helper functions for operating on data points arranged in a grid of some predetermined height and width.

The main annoyances of using these data structures instead of arrays is that all operations done with them were (until recently) only usable through function calls, and data structures all had to be manually initialised and destroyed.

So you’d have to write a lot of code like this:

my_list = ds_list_create()

ds_list_add(my_list,1)
ds_list_add(my_list,2)
ds_list_add(my_list,4)
ds_list_insert(my_list,2,3) // insert 3 at the third position (counting from 0)
first_value_in_list = ds_list_find_value(my_list, 0)

ds_list_destroy(my_list)

Java programmers may not see a problem with this, but anyone who’s used more expressive languages should already be feeling exhausted just looking at the above code. So in GameMaker: Studio, special syntax for accessing (reading and modifying) data structure entries was added. It looks like this:

my_grid[# 1,1] = 1
my_list[| 1] = 1
value = my_map[? "key"]

No accessor syntax exists for stacks or either of the types of queues because arbitrary access would rather defeat the point of those structures. Also, I’m not quite sure why they didn’t just reuse the standard array syntax, but I’m grateful for the syntactic sugar nonetheless.

As with arrays, the parser doesn’t let you chain these accessors, so this:

my_grid[# 1,1][| 3]

…will produce a syntax error. You’d have to work around this by going back to the old ways:

ds_list_find_value(my_grid[# 1,1], 3)

Scoping

The last odd GMLism I’d like to talk about is lexical scoping. In most languages I’ve used, the values you can access at any time depend on the current scope: if you’re in a function, you can access that function’s arguments and variables defined within that function; if you’re in a class, you can access variables defined within that class; and so, and so on. In general, you can access things defined on the same level, or explicitly passed down to that level.

GML’s scoping is rather more open. There are three levels:

  1. Global variables, declared via globalvar myglob;
  2. Instance variables, declared via a definition like myinstancevar = 1
  3. Local variables, declared via var myvar;

Code at each of these levels can access all variables in the level(s) above it.3 The implication of that is as follows: say you have a script (which is like a function), and you want to use it manipulate an array. Now, until recently, GM did not allow arrays to be passed as arguments to scripts, so your only option, unless you wanted to dispense with arrays entirely, was to take advantage of this open approach to scoping and write the script so that it would reach into its caller and directly manipulate the array.

This is, of course, hideous style, and places a constraint on the objects that can make use of your script without throwing an error, but is totally possible. It’s one of the many ways GM smooths over complexities and makes things easy for beginning programmers.

GM also provides a large number of default variables at both a global and instance level, most of which are associated with predetermined behaviour – these are helpfully indicated by syntax highlighting. For example, the global variable room contains the ID of the room the game is in at any point in time, and the instance variables x and y contain and determine the x and y coordinates of the instance within the current room.

Local variables like speed, gravity and direction (all of which directly affect an instance’s movements) can be a bit of a pain when writing your own systems that directly manipulate the x and y co-ordinates (as most intermediate to advanced GM users usually will) – you end up with stuff like move_speed, grav and dir.

Finally, GameMaker provides “constants”, which have recently been renamed to “macros”. This is basically a list of globally scoped variables that can’t be changed defined in a special menu. They’re not limited to static values, and can also be expressions and function calls. Thus this is a common method of defining new colours:4

c_chartreuse = make_colour_rgb(127, 255, 0)
GM's macro menu

Final words

GameMaker is an idiosyncratic tool if you come into it with conventional programming knowledge, but those same idiosyncrasies are in service of making it a tool that laymen can (1) learn to program with and (2) be inspired to continue programming because they’re actually able to make a game on the first day rather than struggling to draw a bloody rectangle in the middle of the screen for the first week. And then they can continue to grow more advanced with the tool and make all kinds of games (within graphical and complexity limits) for many platforms and even go on to sell them on Steam.

After learning what real programming languages are like, GML often frustrates me. If you want to implement certain types of things, it quickly becomes necessary to hack and work around the language’s many limitations.

But even so, I’ve been using it so long that it’s like an old glove that just fits snugly and in terms of development speed, it’s very difficult to beat. As long as you don’t need to do complex operations on data structures or strings5, don’t care much for the third dimension and just want to focus on making squares and circles move on a screen, it’s a fantastic tool.

And I don’t think it’s doing too badly considering the long history of its development and how many times its aims have changed. Professor Mark Overmars originally created it as basically a learning tool for kids, and later it was bought by the company YoYoGames, which initially wanted to go even further down that route and make it easier. But then they realised professional game development tools were where the money was, did an about turn and have been working towards that ever since, doing a good job within the backwards compatibility limitations. Which is why what was and still in some ways resembles a kid’s toy now has a whole host of expensive professional modules for compiling games for the web, smartphones and even consoles.

You can make a bad game in any language/with any tool, and the same is true for a good one. In a sense, GM games are like fanfiction – most of them are bad, but they are not bad as a function of being made in GM/written about established universes, but as a function of being developed by inexperienced amateurs. And there is good stuff hidden in the incredibly visible reams of bad stuff – and even the bad stuff can be bad in fascinating, amusing and different ways.

Anna Anthropy’s Rise of the Videogame Zinesters talks about the importance of a plurality of voices in game development. The book’s tone is perhaps overly hostile to “straight white programmer dudes”, but I agree with the underlying point. By lowering the bar and having easy, intuitive-to-non-programmer development systems, we allow people who might never have otherwise made a game to make a game, and they bring in new and interesting ideas.

And I think that’s what GM is for, and why it has the weird idiosyncrasies it has. I know if I hadn’t found GM – if I’d had to, at age thirteen, continue wading through the incomprehensible book on C++ I found in one of our house’s bookcases and try to wrap my head around all these abstracts before I could sate my juvenile desire to make a damn game, I might never have gotten into any of this at all.


  1. One can argue over how much using the more specialised systems really restricts you – people have made platformers in AGS, after all. But, generally speaking, making something other than a JRPG in RPGMaker or something other than a 90s point-and-click adventure game in AGS involves fighting against your own tools to a degree not necessary in GM. ↩︎

  2. Recomposition! ↩︎

  3. But not below it… not sure how that would even work. ↩︎

  4. A further welcome change in GM Studio was allowing the use of Commonwealth spellings in function names. I always found it a bit odd how, despite being developed in the Netherlands and subsequently Scotland, GM insisted on stuff like draw_set_color↩︎

  5. GML provides a handful of basic string operations, but it’s clearly not a language designed with text munging in mind – there’s definitely no regex. Also, strings are indexed starting at 1, which is inconsistent not only with the vast majority of other languages, but within the language itself (arrays start at 0, as God intended). ↩︎


similar posts
webmentions(?)