Anatomy of a GML file
GML is GameMaker's built-in scripting language. A .gml file is either one event of one object or a script of reusable functions, and since GameMaker 2.3 a single script file can hold as many functions, constructors, and macros as you want.
File extensions: .gml
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a GML file
Comment
Single-line (//) or block (/* ... */), ignored by the compiler.
// comments out the rest of the line and /* ... */ brackets a block that can span many lines, exactly as in C, Java, and JavaScript. Neither form reaches the running game, so they cost nothing at runtime. Because a .gml file is opened inside GameMaker rather than as a standalone document, a block comment at the top is the usual place to record which object and which event the file belongs to. The IDE shows you that in its own chrome, but the comment survives when the code is pasted into a forum post or a diff.
Function documentation (`///`)
Triple-slash lines with @ tags that feed the IDE's autocomplete.
A comment starting with three slashes is read by the GameMaker IDE rather than merely ignored. @description, @param, and @returns populate the tooltip and argument hints you get when you later type the function's name, which is the closest thing GML has to a type signature. A type in braces (@param {Real} _x) tells Feather, the built-in code analyser, what to expect, and square brackets around a name mark it optional. On an object event, a single /// @description ... line at the top also renames that event in the IDE's event list, so a Step event can read "handle input" instead of "Step". Olde
Macro (`#macro`)
A compile-time constant. The value is pasted in wherever the name appears.
#macro MAX_SPEED 8 defines a name the compiler substitutes literally before the game is built. There is no variable, no memory, and no lookup at runtime, which makes macros the idiomatic way to name a tuning number you never intend to change while the game is running. A macro can also be scoped to a build configuration with #macro Config:NAME value, so a Debug build and a Release build can disagree about the same constant without an if anywhere in your code. Macros are not variables: you cannot assign to one, and they are visible across the whole project regardless of which file declares them.
Enum
A named set of integer constants, referenced as Name.member.
enum PlayerState { idle, running, hurt } creates three compile-time constants numbered from 0 upward, read back as PlayerState.idle and friends. You can also assign explicit values (error = -1), and later members continue counting from the last one given. Enums exist so that state machines stop being written in raw integers. if (state == 2) tells a reader nothing; if (state == PlayerState.hurt) tells them everything, and costs exactly the same at runtime because the compiler has already replaced the name with the number.
Local variable (`var`)
var scopes a variable to the current event or function only.
var _spd = 4; creates a variable that lives until the end of the event or function that declared it, then vanishes. It is not stored on the instance, so nothing else can read it, and re-running the event next frame starts from scratch. The leading underscore is a naming convention, not syntax: GameMaker projects use it so that a glance at _spd versus spd tells you whether the value survives the frame. Locals are also the fastest kind of variable in GML, since the compiler can resolve them without going through an instance's variable table.
Instance variable
An assignment with no var belongs to the instance and persists.
Writing hp = 100; with no keyword in front of it stores the value on the instance running the code, where it stays until that instance is destroyed. Every instance of an object gets its own copy, which is what lets forty enemies each track their own health with one line of code. Some instance variables are built in and already exist before you touch them: x, y, speed, direction, image_index, visible, and a few dozen more. Assigning to x does not just record a number, it moves the instance, because the engine reads that variable every frame. And yes, y grows downward.
Global variable (`global.`)
One shared copy, readable from any instance in any room.
global.score = 0; creates a variable that belongs to the game rather than to any instance. It survives room changes and instance destruction, and any code anywhere can read or write it by naming the global. prefix explicitly. That prefix is deliberate. GML makes you say global. every single time precisely because the alternative, an invisible shared variable, is how save files quietly corrupt themselves. Score, current level, and settings are reasonable globals; anything that logically belongs to one thing on screen is not.
Function definition
function name(args) { ... } declares a reusable, callable block.
Since GameMaker 2.3 a script file is ordinary code that happens to declare functions, so one file can hold as many as you like. Before that release every script *was* a single function and the file name *was* the function name, which is why older tutorials look so different from current ones. A function declared at the top level of a script is global: any object can call it from any event. Functions are also values, so one can be stored in a variable or a struct field, passed to another function, and called later, which is how callbacks and simple state machines are usually wired up.
Constructor and struct
function Name() constructor builds structs with new Name().
Adding the constructor keyword after the parameter list turns a function into a blueprint. new Weapon("Rusty Sword", 3) runs the body against a fresh struct, and every plain assignment inside becomes a field on that struct. A struct is a bag of named values with no sprite, no position, and no events: unlike an instance, it is not a thing in a room. A one-off struct can also be written as a literal, var _pos = { x: 0, y: 0 };, where a colon rather than = separates key from value. static marks a member that is stored once for the constructor rather than once per struct, which is how methods are
`with` (scope switching)
Runs a block as if it were each matching instance in turn.
with (obj_enemy) { ... } executes its body once for every instance of obj_enemy in the room, and inside that body self *is* that enemy. Bare x, hp, and instance_destroy() therefore refer to the enemy, not to whoever wrote the code. Passing a single instance id instead of an object runs the block exactly once, for that instance. Inside a with, the keyword other refers back to whoever ran it, which is how the two sides talk to each other (other.hp -= damage). This is the single most GML-flavored feature in the language, and the single easiest way to write code that reads correctly and does somet
Control flow
if/else and the loops, including GML's own repeat and do ... until.
GML has the C-family set (if/else, for, while, switch, break, continue, return) plus two of its own. repeat (3) { ... } runs a block a fixed number of times with no counter variable at all, and do { ... } until (condition) tests at the bottom, so the body always runs at least once. Note that it is until, not while: the loop ends when the condition becomes *true*. The ternary condition ? a : b works as an expression, and and, or, and not are accepted as spelled-out aliases for &&, ||, and !. GML will forgive a missing semicolon in many places, but the manual tells you to end every statement wit
Built-in function call
The engine's own library: input, collision, instances, drawing.
Almost everything a game does routes through a built-in function rather than a class or a module import. keyboard_check(vk_right) polls input, place_meeting(x, y, obj_wall) asks the collision system a question, instance_create_layer(x, y, "Instances", obj_spark) spawns something, and instance_destroy() removes the caller. The naming is consistently subject_verb, which makes the autocomplete list the real documentation: type audio_ or ds_list_ and the whole family appears. One family is special: the draw_* functions only produce anything when called from a Draw event, because that is the only p
Official GML site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.