Getting Started – An Introduction to GML

Exclusive offer: get 50% off this eBook here
GameMaker Game Programming with GML

GameMaker Game Programming with GML — Save 50%

Learn GameMaker Language programming concepts and script integration with GameMaker: Studio through hands-on, playable examples with this book and ebook

€19.99    €10.00
by Matthew DeLucas | April 2014 | Open Source

GML or GameMaker Language is a great tool for expanding the already vast variety of tools provided by GameMaker: Studio. GML scripts allow users to write their own code, creating an organized codebase that is easier to modify and debug than GameMaker: Studio's built-in drag-and-drop functionality.

Before exploring GML's use in creating actual games, this article by Matthew DeLucas, the author of GameMaker Game Programming with GML, will go over the basics of the language, such as the following components:

  • Syntax and formatting
  • Variables
  • Functions
  • Statements
  • Arrays

(For more resources related to this topic, see here.)

Creating GML scripts

Before diving into any actual code, the various places in which scripts can appear in GameMaker as well as the reasoning behind placing scripts in one area versus another should be addressed.

Creating GML scripts within an event

Within an object, each event added can either contain a script or call one. This will be the only instance when dragging-and-dropping is required as the goal of scripting is to eliminate the need for it. To add a script to an event within an object, go to the control tab of the Object Properties menu of the object being edited. Under the Code label, the first two icons deal with scripts. Displayed in the following screenshot, the leftmost icon, which looks like a piece of paper, will create a script that is unique to that object type; the middle icon, which looks like a piece of paper with a green arrow, will allow for a script resource to be selected and then called during the respective event. Creating scripts within events is most useful when the scripts within those events perform actions that are very specific to the object instance triggering the event. The following screenshot shows these object instances:

Creating scripts as resources

Navigating to Resources | Create Script or using the keyboard shortcut Shift + Ctrl + C will create a script resource. Once created, a new script should appear under the Scripts folder on the left side of the project where resources are located. Creating a script as a resource is most useful in the following conditions:

  • When many different objects utilize this functionality
  • When a function requires multiple input values or arguments
  • When global actions such as saving and loading are utilized
  • When implementing complex logic and algorithms

Scripting a room's creation code

Room resources are specific resources where objects are placed and gameplay occurs. Room resources can be created by navigating to Resources | Create room or using Shift + Ctrl + R.

Rooms can also contain scripts. When editing a room, navigate to the settings tab within the Room Properties panel and you should see a button labeled Creation code as seen in the following screenshot. When clicked on, this will open a blank GML script. This script will be executed as soon as the player loads the specified room, before any objects trigger their own events. Using Creation code is essentially the same as having a script in the Create event of an object.

Understanding parts of GML scripts

GML scripts are made up of many different parts. The following section will go over these different parts and their syntax, formatting, and usage.

Programs

A program is a set of instructions that are followed in a specific order. One way to think of it is that every script written in GML is essentially a program. Programs in GML are usually enclosed within braces, { }, as shown in the following example:

{ // Defines an instanced string variable. str_text = "Hello Word"; // Every frame, 10 units are added to x, a built-in variable. x += 10; // If x is greater than 200 units, the string changes. if (x > 200) { str_text = "Hello Mars"; } }

The previous code example contains two assignment expressions followed by a conditional statement, followed by another program with an assignment expression.

If the preceding script were an actual GML script, the initial set of braces enclosing the program would not be required.

Each instruction or line of code ends with a semicolon ( ;). This is not required as a line break or return is sufficient, but the semicolon is a common symbol used in many other programming languages to indicate the end of an instruction. Using it is a good habit to improve the overall readability of one's code.

snake_case

Before continuing with this overview of GML, it's very important to observe that the formatting used in GML programs is snake case. Though it is not necessary to use this formatting, the built-in methods and constants of GML use it; so, for the sake of readability and consistency, it is recommended that you use snake casing, which has the following requirements:

  • No capital letters are used
  • All words are separated by underscores

Variables

Variables are the main working units within GML scripts, which are used to represent values. Variables are unique in GML in that, unlike some programming languages, they are not strictly typed, which means that the variable does not have to represent a specific data structure. Instead, variables can represent either of the following types:

  • A number also known as real, such as 100 or 2.0312. Integers can also correspond to the particular instance of an object, room, script, or another type of resource.
  • A string which represents a collection of alphanumeric characters commonly used to display text, encased in either single or double quotation marks, for example, "Hello World".

Variable prefixes

As previously mentioned, the same variable can be assigned to any of the mentioned variable types, which can cause a variety of problems. To combat this, the prefixes of variable names usually identify the type of data stored within the variable, such as str_player_name (which represents a string). The following are the common prefixes that will be used:

  • str: String
  • spr: Sprites
  • snd: Sounds
  • bg: Backgrounds
  • pth: Paths
  • scr: Scripts
  • fnt: Fonts
  • tml: Timeline
  • obj: Object
  • rm: Room
  • ps: Particle System
  • pe: Particle Emitter
  • pt: Particle Type
  • ev: Event

Variable names cannot be started with numbers and most other non-alphanumeric characters, so it is best to stick with using basic letters.

Variable scope

Within GML scripts, variables have different scopes. This means that the way in which the values of variables are accessed and set varies. The following are the different scopes:

  • Instance: These variables are unique to the instances or copies of each object. They can be accessed and set by themselves or by other game objects and are the most common variables in GML.
  • Local: Local variables are those that exist only within a function or script. They are declared using the var keyword and can be accessed only within the scripts in which they've been created.
  • Global: A variable that is global can be accessed by any object through scripting. It belongs to the game and not an individual object instance. There cannot be multiple global variables of the same name.
  • Constants: Constants are variables whose values can only be read and not altered. They can be instanced or global variables. Instanced constants are, for example, object_index or sprite_width. The true and false variables are examples of global constants. Additionally, any created resource can be thought of as a global constant representing its ID and unable to be assigned a new value.

The following example demonstrates the assignment of different variable types:

// Local variable assignment. var a = 1; // Global variable declaration and assignment. globalvar b; b = 2; // Alternate global variable declaration and assignment. global.c = 10; // Instanced variable assignment through the use of "self". self.x = 10; /* Instanced variable assignment without the use of "self".
Works identically to using "self". */ y = 10;

Built-in variables

Some global variables and instanced variables are already provided by GameMaker: Studio for each game and object. Variables such as x, sprite_index, and image_speed are examples of built-in instanced variables. Meanwhile, some built-in variables are also global, such as health, score, and lives. The use of these in a game is really up to personal preference, but their appropriate names do make them easier to remember. When any type of built-in variable is used in scripting, it will appear in a different color, the default being a light, pinkish red. All built-in variables are documented in GameMaker: Studio's help contents, which can be accessed by navigating to Help | Contents... | Reference or by pressing F1.

Creating custom constants

Custom constants can be defined by going to Resources | Define Constants... or by pressing Shift + Ctrl + N. In this dialog, first a variable name and then a correlating value are set. By default, constants will appear in the same color as built-in variables when written in the GML code. The following screenshot shows this interface with some custom constants created:

GameMaker Game Programming with GML Learn GameMaker Language programming concepts and script integration with GameMaker: Studio through hands-on, playable examples with this book and ebook
Published: April 2014
eBook Price: €19.99
Book Price: €32.99
See more
Select your format and quantity:

Functions and accessing script resources

A function is a statement that executes a program; it is either built into GML or created as a script resource. Functions can either execute an action, such as changing the alignment of a font during a Draw event, return a value, or do both. Functions have to be followed by a set of parentheses—( )—to execute properly. Another important aspect of functions is arguments. These comma-separated sets of data—string, integers, objects, and so on—are accessible to functions when executed. If there are no arguments, the parentheses are left empty; however, when needed, arguments are placed in between them. The following are some examples of functions with and without arguments:

// Executes an action, in this case, drawing the instance. draw_self(); /* Executes an action which requires arguments, in this case,
drawing an arrow. */ draw_arrow(0,0,100,100,2); // Obtains a random value between two provided values. random_value = random(10, 23);

GameMaker: Studio provides a wide variety of functions. Many of these functions will be used; however, to find information about all of GML's available functions, go to Help | Contents... | Reference or press F1.

Scripts created as resources can be accessed using two methods. Either the script's name can be referenced and used like a built-in GML function or the function script_execute can be used, as shown in the following code snippet:

// Executes the script directly like a built-in function. scr_script_resource("argument", obj_button, 0.12, false); /* Executes the same script as the previous line but through the use
of "script_execute". */ script_execute(scr_script_resource, "argument", obj_button, 0.12, false);

The advantage of using script_execute is that it allows a script-assigned variable to be used as shown in the following code:

// Assigns an instanced variable with the script resource's index. self.script = scr_script_resource; // Executes the assigned script. script_execute(self.script, "argument", obj_button, 0.12, false);

The script_execute function can only be used on scripts created as resources; additionally, variables cannot be assigned built-in functions. The following code demonstrates this problematic situation:

// Assigns an instanced variable with a script resource ID. self.script = scr_script_resource; // Calling the instanced variable will cause a compile error. self.script("argument", obj_button, 0.12, false);

Arguments

As mentioned previously, some functions require arguments. When creating a script, these arguments can be accessed within the script using the keywords argument0 through argument15, allowing for up to 16 different values if necessary.

Expressions

Expressions represent values usually stored within variables or evaluated by a conditional statement, which will be explained later. They can be real numbers, such as 3.4; hexadecimal numbers starting with a $ sign, such as $00FFCC (usually used to represent a color); and strings, which are created by encasing them in single or double quotation marks, for example, 'hello' or "hello".

Expression symbols

Expressions can be manipulated and evaluated using different symbols. The equals sign or assignment operator = sets the value of a variable as shown in the following code:

// Assigning a variable with a value. a = 10; /* Assigning a different variable with an expression, in this case,
the sum of the previously declared variable and 7.5. */
b = (a + 7.5);

Expressions can also be combined with basic mathematical operations, as shown in the following code:

// Addition and subtraction, + and - val = a + 20 - b; // Multiplication and division, * and / val = a * 20 / b; /* Expressions encased in parenthesis, ( and ), will be evaluated first. */ val = (a + 20) * (b - 40); // + can also be used to concatenate, or link, strings together. str = "hello " + "world";

Mathematical operations can be combined with = to create a compound assignment operator and perform relative addition, subtraction, multiplication, or division, as shown in the following code:

// Relative addition, += x += y; // equivalent to x = x + y; // Relative subtraction, -= x -= y; // equivalent to x = x – y; // Relative multiplication, *= x *= y; // equivalent to x = x * y; // Relative division, /= x /= y; // equivalent to x = x / y;

The main advantage of this code is its extreme simplicity. In the previous examples, x is a simple variable to type out, but if a variable name is longer, the preceding code cuts down on having to unnecessarily retype that name on both sides of the assignment operator.

Variables can also be incremented by one value as shown in the following code:

var a, b, c, d, str_a, str_b, str_c, str_d; a = 1; b = 1; c = 1; d = 1; // The return string will be "1" but a's value is now 2. str_a = string(a++); // The return string will be "2" and b's value is 2. str_b = string(++b); // The return string will be "1" but c's value is 0; str_c = string(c--); // The return string will be "0" and d's value is 0; str_d = string(--d);

In summary, if ++ or -- is included after the variable, the variable's current value is returned and then is incremented. If ++ or -- is set before the variable, its current value is incremented and that new value is returned.

Boolean comparisons, as shown in the following code, compare expressions and return the values true or false, which are GML constants equal to 1 and 0 respectively:

// Less than, < if (a < 20) { instance_create(a, 20, obj_ball); } // Less than or equals to, <= if (a <= 20) { instance_create(a, 20, obj_ball); } // Equals to, == if (a == 20) { instance_create(a, 20, obj_ball); } // Not equals to, != if (a != 20) { instance_create(a, 20, obj_ball); } // Greater than, > if (a > 20) { instance_create(a, 20, obj_ball); } // Greater than or equals to, >= if (a >= 20) { instance_create(a, 20, obj_ball); }

Booleans can also be combined for evaluation, as shown in the following code:

// And, &&, will return true if both booleans are also true. if (a == 20 && b == 40) { val = "and"; } /* Or, ||, will return true if at least one of the booleans is true. */ if (a == 20 || b == 40) { val = "or"; } /* xor, ^^, will return true if one booleans is true and the other false. */ if (a == 20 ^^ b == 40) { val = "xor"; }

Conditional statements

Conditional statements utilize a Boolean expression with a corresponding program. If the value returned is true, the program following the conditional statement will be executed; otherwise, it will be skipped. There are several types of conditional statements in GML, each of which has its own uses.

If, else, and switch

The if statement is probably the most common conditional statement that will be used while making these games. The if statements were introduced when discussing Booleans previously; the following is another example illustrating a simple if statement:

/* If the value of a is greater than the value of b, a is returned;
otherwise, b is returned. */ if (a > b) { return a; } else { return b; }

In the previous example, assuming both a and b are real numbers, if the value of a is greater than that of b, a is returned; otherwise, b is returned. One thing that should be noted is that b will be returned if it is less than or equal to a, since this is the opposite of greater than.

Now what if a variable needs to be compared against many different conditions? The following could be done:

/* Assigns the temperature of an object based on a color with multiple
if-else-statements. */ if (color == c_green || color == c_purple || color == c_blue) { temperature = "cool"; } else if (color == c_red || color == c_orange || c == c_yellow) { temperature ="warm"; } else if (color == c_black || color == c_gray || color == c_white) { temperature ="neutral"; } else { temperature ="other"; }

When there are a lot of objects, each with its own complex program that needs to be executed, the execution of these objects could become rather tedious and confusing. Instead of using long chains of if-else statements, a switch statement can be used. A switch statement is created with the keywords switch, case, break, and default, as shown in the following code:

/* Assigns the temperature of an object based on a color with a
switch statement. */ switch (color) { case c_red: case c_orange: case c_yellow: temperature = "warm"; break; case c_green: case c_blue: case c_purple: temperature ="cool"; break; case c_black: case c_white: case c_gray: temperature = "neutral"; break; default: temperature = "other"; break; }

The previous switch statement is identical to the layered if-else statements created earlier. Each case statement is essentially testing whether or not the value of the variable supplied after switch is equal to its own. If the values are equal, the program is executed. If a default case is supplied, this program will be executed if none of the other cases satisfy the conditional statement.

repeat, while, do, and for

The repeat, while, do, and for statements are all examples of statements that execute a program multiple times and this is often referred to as a loop. The Repeat statement is used to execute the same program for a specified number of times:

// Creates 10 buttons at random positions between 0 and 100. repeat (10) { instance_create(random(100), random(100), obj_button) }

The previous code will create 10 instances of obj_button at random positions. The while statement will execute a program until a condition is no longer met:

// Reduces x by 10 until it is no longer greater than 100. while (x > 100) { x -= 10; }

In the previous code, the value of x will be reduced until it is less than 100. The do statements are very similar to the while statements, except they require an additional keyword—until:

/* Initially reduces x by 10 but continues until x is less than
or equal to 100. */ do { x -= 10; } until (x <= 100);

The difference between do and while is subtle. In a do statement, the program is executed first and then the condition is checked; in a while statement, the condition is checked first before executing the program. The for statements or for loops have built-in conditions as shown in the following code:

// Declares a local variable. var i; /* 10 buttons are created, evenly spaced apart horizontally by 100
units with an initial offset of 25. */ for (i = 0; i < 10; i++) { instance_create(i * 100 + 25, 25, obj_button) }

The first statement after the keyword for sets the local variable i to 0. Then, there is the condition that if the value of i is less than 10, the block of code within the loop is executed. The value of the variable i is then incremented and checked again within the conditional statement, running the program if the statement is still valid. Now the previous for statement is very similar to the repeat statement. The important difference is that i is incremented by the statement, making this a good way to loop through values in a one- or two-dimensional array, which will be covered shortly. The following sample code demonstrates this functionality with a two-dimensional array:

// Creates two local variables. var i,j; /* This is an example of a nested for loop in which a
10 by 10 grid with values ranging from 0 to 99 will be created. */ for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { grid[i,j] = i + j * 10; } }

The following code shows the same functionality as the one performed using the repeat statements:

// Creates local variable and then assigns their values to 0. var i, j; i = 0; j = 0; /* Repeat functionality is performed 10 times and 10 more times
within each repeat-statement. */ repeat (10) { repeat(10) { // assign the grid value grid[i,j] = i + j * 10; // increment after each inner repeat. j++; } // increment after each outer repeat. i++; }

As demonstrated, a for statement is much clearer than a repeat statement in situations like these.

Before moving on, it must be noted that the repeat, while, do, and for statements can create a situation known as the infinite loop. Infinite loops are very dangerous and cause a game to freeze up, ignoring input from the user and forcing them to shut down the game. The following are some examples of statements that can cause this; do not use these in actual code as they are just examples of what not to do:

/* Since the condition in the while-statement is always true,
it will never terminate. */ while (true) { x += 1; } // Declares a local variable. var i; /* This for-statement will never end since i will never be less than 0. */ for (i = 0; i >= 0; i++) { x += i; }

break, continue, and return

As previously mentioned, looping statements can sometimes cause a situation in which they do not terminate. There are two ways to resolve this issue. One is known as break, which was already demonstrated in the switch statements to break out of a case statement, but it can also be used to break out of a loop. The following is an example of a break statement:

// local variable used in the for loop var i; /* Iterates through 1000 enemies. At the first undefined enemy, the loop is
exited, otherwise, the enemy moves by a random value
between 1 and 10 horizontally. */ for (i = 0; i < 1000; i++) { if (enemies[i] == noone) { break; } enemies[i].x += random_range(1,10); }

In the previous code sample, the for statement is broken out of and no longer continues running once the internal condition is met. Suppose break were to be replaced with continue, as shown in the following example:

// local variable for for loop var i; /* Iterates through 1000 enemies. If an undefined enemy is encountered,
the remaining enemies are still checked; otherwise, the enemy is
moved by a random value between 1 and 10 horizontally. */ for (i = 0; i < 1000; i++) { if (enemies[i] == noone) { continue; } enemies[i].x += random_range(1,10); }

The for statement would proceed to checking the additional enemies, even if they are all undefined. The break and continue statements, however, are very useful because in both examples, an undefined enemy is to be encountered and the game will crash upon trying to add the value generated by random_range to its x value. The return statements are used most often in GML scripts that are created as resources. The return statement will not only end any script in its entirety, but also return whatever value follows it as demonstrated in the following code:

// This script will simply add and multiply two arguments var result; result = (argument0 + argument1) * (argument0 * argument1); return result;

If you want to obtain a value using the previously shown function, it can be accessed through the following code, assuming that the function scr_add_multipy is a script resource:

// Execute the method and assign it to x. x = scr_add_multiply(1, 2);

GameMaker Game Programming with GML Learn GameMaker Language programming concepts and script integration with GameMaker: Studio through hands-on, playable examples with this book and ebook
Published: April 2014
eBook Price: €19.99
Book Price: €32.99
See more
Select your format and quantity:

Arrays

Now the one additional feature that variables have is that they can contain multiple values through the use of arrays. An array is a fundamental and useful programming tool. Instead of having to create a unique variable name to store dozens of values, such as those for a high-score list or a role-playing game's inventory system, all of these values can simply be stored in an array. The following code demonstrates the creation and assignment of an array in code:

// A local array is declared and assigned four values. var array; array[0] = 10; array[1] = false; array[2] = 32.88; array[3] = "a";

The following figure illustrates the data contained within each portion of the newly created variable:

By enclosing an integer within brackets—[ ]—after a variable name, a one-dimensional array is created. This integer is then called an index.

Arrays are useful for storing data that is iterated, but they do have their limits. An array cannot contain more than 32,000 members as this will cause an error, and arrays with several thousand members can cause memory issues and their initialization can be rather slow, so it is best to avoid using them when possible.

Two-dimensional arrays

A two-dimensional array is created by adding two comma-separated integers within the brackets after a variable name. They are great to use when creating a grid system: the first index represents a column and the second represents a row, as shown in the following code:

/* A 2-dimensional array is declared and assigned four different values. */ var grid; grid[0,0] = 10; grid[0,1] = false; grid[1,0] = "ab"; grid[1,1] = 95.5;

The following figure illustrates the data within the two-dimensional array created in the previous code block:

Commenting

Throughout the previous code samples, characters such as //, /*, or */ have been used. These create comments, which are lines and blocks of code that are not compiled or that will not show up in the results of the game. Comments are used mostly to document code so that when others are looking at that code, it is easier to understand it and appears much more professional. They can also help the original programmer by citing information that may not be obvious or when the code hasn't been looked at in quite some time. Comments can also be used to temporarily remove a section of code without having to delete it while debugging or testing new features.

Up to this point, the comments in the provided code samples have been very verbose for demonstration purposes.

The // characters will comment just one line of code. Any number of lines encased within /* and then */ will be part of a comment block. Finally, using /// in the first line of a script used in an event will make the comment show up in the editor as shown in the following screenshot:

Summary

This article introduces you to the basic formatting and syntax of GameMaker Language (GML). These topics will be expanded by creating a simple button.

Resources for Article:


Further resources on this subject:


About the Author :


Matthew DeLucas

Matthew DeLucas has been a gameplay engineer with Schell Games in Pittsburgh, Pennsylvania for over five years. He has worked on a wide range of interactive projects for PC, Web, mobiles, and consoles. Matt has also released independent projects for PC and Xbox 360, such as Convextrix, a puzzle game, and Battle High, which is a fighting game series. Being a programmer and designer, Matthew has also participated in almost every official, 48-hour Global Game Jam, managing to help his team achieve success while experimenting with new ideas.

Matthew began his programming career in GameMaker: Studio and has become proficient with additional game engines, such as Gamebryo and Unity3D, and scripting languages such as C#, Python, Lua, and MaxScript for 3DS Max. Often, he chronicles his experiences with game production on his blog at www.mattrifiedgames.blogspot.com.

Matthew has had a desire to work in the game industry ever since he was young, and he enjoys all of the facets of game production—programming, design, and art. His favorite genres include platformer, puzzles, racing, and fighting games, all of which influence his designs.

Books From Packt


Learning Libgdx Game Development
Learning Libgdx Game Development

Windows Phone 8 Game Development
Windows Phone 8 Game Development

jQuery Game Development Essentials
jQuery Game Development Essentials

Gideros Mobile Game Development
Gideros Mobile Game Development

OUYA Game Development Essentials
OUYA Game Development Essentials

HTML5 Games Development by Example: Beginner’s Guide
HTML5 Games Development by Example: Beginner’s Guide

 Marmalade SDK Mobile Game Development Essentials
Marmalade SDK Mobile Game Development Essentials

SFML Game Development
SFML Game Development


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software