The GameMaker Language (GML) is what gives your games life and makes them tick. In this chapter we will learn about how to get started writing GML code, the basic structure, and flow of the language itself. There are two ways for writing GML in GameMaker Studio 2: one follows the traditional way of typing in the code into an editor space and the second uses the drag and drop method. We will forgo the drag and drop method in the interest of time as well as clarity when our code becomes slightly larger in scope.
Where to Write Code and What It Means
In GameMaker Studio 2 all the code we write gets compiled by the compiler before being executed. The compiler, in the simplest of terms, is a program that converts or translates the code you write into a language the computer can understand. By default you will be using the built-in virtual machine (VM) to test your game by simply pressing the Play button from the toolbar. You can also target the YoYo Compiler (YYC) or JavaScript for the HTML 5 target platform.
The VM output uses a generic runner (an application that loads and runs your game assets and code) for each target platform that interprets the compiled code. It is used mostly for smaller games or when performance is not a key factor. The YYC output compiles your code into native code that can run on the targeted platforms, unlike the VM, which acts as an intermediary. The YYC is heavily optimized and has enhanced performance but has higher compile times when compared to the VM.
You can find out more about compiling in the GMS2 documentation –( https://docs2.yoyogames.com/source/_build/1_overview/2_quick_start/6_compiling.html ).
Events are fired by GMS2 when something specific happens; for example, when a user moves their mouse over an object, the mouse-enter event for that object is fired, likewise when the user clicks and releases their left mouse button, multiple events are fired in succession—a mouse left-down event, a mouse left-pressed event, and a mouse left-released event, to name a few. We can listen for these events and act accordingly when they are fired. Only objects have the capability to listen to all events—these can be events associated with that object and its instance, or they can be global events associated with input, networks, rooms, etc.
Instances of objects placed in rooms have some properties that we can edit individually in the Instance Editor. One such special property that can be edited is the Creation Code section. The code in the Creation Code event is executed right after the object’s Create event. Here we will be able to manipulate specific properties pertaining to that instance of the object. Similarly, the Room resource also has a Creation Code event. When the Room is created, code in the Creation Code event for the room is executed.
- 1.
Object Variables: The object variables are the first to be set when a room is entered. Object variables are those defined via the Object Editor.
- 2.
Instance Variables : If you override any of those Object Variables in the instances via the Room Editor, those will get set next.
- 3.
Create Event: After this, the Create event for each object is called.
- 4.
Instance Creation Code: The Create event is followed by the instance’s creation code.
- 5.
Game Start Event: If the Object has a Game Start event, then this is called next.
- 6.
Room Creation Code: After the Game Start event, the code in the Room Creation Section for the room is executed.
- 7.
Room Start Event: Finally, the Room Start event for the Object is called.
- 1.
Begin Step Event: Executed first before the Step event
- 2.
Step Event: The actual step event, executed before objects are moved to new positions, if any
- 3.
End Step Event: Executed after the Step event
- 1.
Pre-Draw Event: Executed first, before all other Draw events
- 2.
Draw Begin Event: Executed right before the Draw event
- 3.
Draw Event: The actual Draw event
- 4.
Draw End Event: Executed after the Draw event
- 5.
Post-Draw Event: Wrapping up the Draw events with the post-draw event
- 6.
Draw GUI Begin Event: Once all other game elements are drawn, the Draw GUI Begin gets triggered.
- 7.
Draw GUI Event: Then the actual Draw GUI event occurs.
- 8.
Draw GUI End Event: The Draw GUI End event occurs last.
For more information regarding the Event Order, please visit the documentation ( https://docs2.yoyogames.com/source/_build/2_interface/1_editors/events/index.html#object_event_order ).
Apart from events, the other place where we will write code is using the Script Editor. We can directly add a Script resource and then have access to it via its name, like a function.
Let’s create a small project to go over the points we have just discussed. When starting our new game project, we will pick GML instead of Drag and Drop. Once you have the new project up and running, right-click on Objects in the Resources Panel and select Create Object.
Once done, go back to the room, and then from the Resources Panel, drag and drop our new object0 into the room. After adding the object, run the game. Once the game executes, you should be able to see your message in the Output window in the GameMaker Studio IDE.
show_debug_message() is a built-in function. A function is a set of statements together that does something specific. In this case, this function displays a message you send to it. You passed the message, “Hello, I am making games!” as a parameter to the function. Depending on their use, some functions can require you to pass parameters, while sometimes others require none. We will explore when we create our own scripts later.
Running the game now will show that the room’s Creation Code is executed at the end after the object’s Create event is fired and after the instance’s Creation Code is executed.
First when the object was created and the Create event of the object was fired, we called our welcomeScript function/script . Then we printed out another message from the same event. Then we executed the commands in the instance’s creation code as well as the room’s creation code.
Commenting Your Code
Single-Line Comment Describing the Actions in the Statements That Follows
Multiline Comments in Action
Here the first comment gives some information about the code below it, while the second comment helps to tell the compiler to ignore a set of actual code that would otherwise print the output to the console. Commenting large chunks of code, especially in larger projects in production, can cause more confusion than good. Aside from testing, commenting large sections of code is a bad practice and should be avoided.
GameMaker Studio also supports JSDoc style comments when it comes to commenting on parts of custom scripts. This will be explained when we start writing our own custom scripts.
Variables and Data
Variables are what we as well as GameMaker Studio use to store data. They can be numbers, text, true or false values, images, objects, collections of items, and much more. There are different kinds of variables in GMS—ones that are available to you globally and give you information about the system and the game itself and others that are contextual that give you information that is immediately related to where you are accessing the variable from. Apart from all these, there are variables that you create to store and manipulate different kinds of data. Let’s go ahead and create an object and see what kinds of variables are available to us and what we can do with them.
Printing Some Built-In Variables to the Console (Refer to: gms2_ch02_01.yyz)
When you check the console, you will see the width of your room and the height of your room printed one after the other as well as the x- and y-coordinate of our object in the room. If you don’t see anything, make sure you placed an instance of the object in the room by dragging and dropping it. The first two are built-in properties of the room, and the x and y variables are built-in properties for the object. Move the instance around the room to a different position and see what values you get.
Not only can you read values, most of the values that are stored in the variables can be changed. Let’s create a sprite from the sprite section in the Resources Panel and attach it to our object. Once you create a sprite, it opens in the Sprite Editor; for now, let’s just edit the image by clicking the Edit Image button and give it a solid fill. Open the object in the Object Editor by double-clicking on it. Select the sprite you have just created in the sprite section of the Object Editor.
Set the x- and y-Value of the Object to That of the x- and y-Values for the Mouse Cursor Position
When you run the game, you can see that now the object we have created follows the mouse wherever it goes. The mouse_x and mouse_y built-in variables contain the mouse position.
Rotate the sprite 1 Degree Every Frame
Instance, Local, and Global Variables
Stores the String “Hello” in the Variable Message
Extending a Player Object With Some Properties
Both Message and Length variables Will Be Discarded Once the Event/Script has Finished Executing
A Global Variable Called currentLevel
Incrementing the Global Variable
Accessing Variables From Other Instances
Our Enemy Is Yellow for Now
Draw a Circle With Our Enemy's Color
Change the Value of enemyColor in Each Instance to a Random Color
Change the Value of enemyColor to One Random Color (gms2_ch02_02.yyz)
The primary difference is that the with operator works like a loop; it technically changes the current scope to that of the instance and then executes the statements within the brackets, whereas the dot directly sets the value that is assigned to all instances (see Listing 2-14). So, in the second case, we set a single randomly generated color to all the instances. We will explore the use of the with statement and the dot as we move forward. Do try both these statements to see how they differ from each other.
Instance-Related Keywords: Other, All, and No One
Accessing the Calling Instance’s Variable Using the Other Keyword
Here, we define a variable color and create a random color value. Since we change the scope of an instance of objEnemy using the with statement, we use the other keyword to access the variable in the calling instance.
The all keyword is a special keyword that refers to all the active instances in the room. This can be used like the other keyword, especially with the with statement.
Tip
To know more about the with statement, select the word 'with' in your code and hit F1, and it will open the documentation on all language features in the GML.
Operations Using Operators
We saw the assignment operator in action while accessing assigning values to variables and changing them. In this section we will go through some of the most common operators in GameMaker Studio.
Basic Arithmetical Operators in Action
We will return to look at more operators in the next section when we deal with control flow.
Managing Control Flow
A control flow statement is a set of statements that dictate how, what, and when parts of your code should be executed. By default, the flow of all code that is executed is from the top to the bottom and executed line by line. These statements help you to dictate what happens to this flow.
The if ... else if ... else Statement
The sturcutre of the if...else if...else statement
It also works in conjunction with a set of comparison, combining, and unary operators. Let’s look at each of them using a small example.
Pseudo Code for What We Are Trying to Achieve
Moving Our Object With the Keyboard
In the first line, in our condition for the if statement, we call the function keyboard_check() with the constant vk_down, the virtual key constant for the down arrow on the keyboard. The keyboard_check() function checks if the given key is held down during the Step event and returns a value of either true or false. So, if the up arrow is pressed, then the object is moved one pixel up and when the down arrow is pressed the object is moved one pixel down. Similarly, we move the object one pixel to the right and left when the player holds down the right and left key, respectively. The function returns false when they are not pressed and no action is taken. You will notice that we wrote two separate if statements to handle two sets of keys. If we had combined them together, then only one of them would get executed. Try combining them using an if else and see what happens. Explore the code – (gms2_ch02_03.yyz).
Comparison Operators
Rewriting the movement code using the equality operator.
As you can see, keyboard_check returns either true or false, we use the equality operator represented by two equals to symbols to check if the return value of the function is true or not.
Add Variables for Our Default Speed, Current Speed, and Boost
The Step Event Now Accomodates the Boost
When the user holds down the shift key, we set the current to our boosted speed value and when the player lets the key go, we set the current speed to our default speed. We also replace the explicit value we used in the movement code with a variable. Run the code to see our little box moving and running when you hold down the shift key. Explore the code in the file gms2_ch02_04.yyz
Moves the Object 10 Pixels to the Right
Pseudocode for What We Are Trying to Achieve
Loop the Object Back if the Current x-Position of the Object is Greater Than the Room Width
Move the Object to the Left and Loop Back to the Right End of the Screen if the Current x-Position of the Object is Less Than Zero
We have changed the code so that the object is moving toward the left of the screen. As the object moves to the left of the screen, its x-value decreases and gets closer to zero, the left end of the room. As the object moves out of the screen we move it by changing the object’s x-position the width of the room. The less than or greater than symbol can be substituted with a less than or equal to symbol or greater than or equal to symbol (<= or >=) when the need arises. Explore the code –at yms2_ ch02_06.yyz.
Loops
Loops in GameMaker Studio are just like loops in any other programming language; they are used to repeat a statement or a set of statements a set specific number of times. Each method of looping is slightly different from the others and is used in different situations. Let’s explore loops through some examples.
For this section, I’ve renamed the objects we’ve created so far to objPlayer, objEnemyRight (the object that keeps moving to the right), and objEnemyLeft (the object that keeps moving to the left). After renaming the object, remove all other instances from the room, apart from the player instance. We will now use create a new object and use it to dynamically add a specific number of new enemy objects randomly into the room.
We start by creating this new empty object, renaming it to objSpawner and adding it to the room. When the game starts, the objSpawner object will create five enemies that move either left or right. Open objSpawner in the Object Editor and add a new Create event. What we will do is to use loops to create a fixed number of enemies that move either left or right on the screen.
Looping With Repeat
Repeat Statement in Action
The repeat statement takes a number as an argument, which designates the number of times the statements within the code block are executed. In Listing 2-26, the code within the curly brackets after repeat(5) gets executed five times. Run the game to see five enemies moving either left or right and the player on the screen.
The irandom_range() functions return a random integer between two given numbers. Here, the first variable dx stores our x-position, a number between zero (our left corner) and the room_width (the right corner). The second variable dy stores our y-position, a number between zero (our top corner) and the room_height (the bottom corner). To make debugging easier, GMS2 uses the same initial random seed. If you want to set your random functions to behave differently every time, you should call the randomize function at the start of your game.
We create an instance of the enemy object at dx and dy. The third parameter of instance_create_layer() is the name of the layer in which instances of objects can be created. A layer called Instances is automatically added to the layers when a room is created. You can add new layers from the properties panel in the Room Editor when you require them. The last parameter is the actual object itself whose instance you want to create. We use the choose function to choose between a set of given values—here, the two objects we want pick from.
Enemy Spawner Script
Rewriting the Repeat Statement in the Create Event for objSpawner
Our code has now become modular due to the script, and our loop is much shorter and readable. Explore the code in the file gms2_ch02_05.yyz.
Looping With While and Do...Until
The while Loop and the do...until Loop Side by Side
In the first case, we declare a variable and set its value to zero, then in the while loop we check if our variable is less than five; if the variable i is less than five then we spawn an enemy. After spawning an enemy, we increment our variable. This will go on for five times, since after each iteration, the while loop checks our condition and deems if it should execute the statements within the braces. Also keep in mind that when we write (i < 5) we imply that the statement (i < 5) == true. Equating to true is assumed and can be omitted to make your code more readable. Explore the code in the file gms2_ch02_06.yyz
In the do...until loop , the difference is very evident. Here, the condition is at the end of the loop and not the beginning, which means that the statements within the loop will get executed at least once before the condition is checked. The second difference is the condition itself, here the loop will continue running until the condition becomes true. Which means the loop will execute until the variable i becomes greater than five. In this case, since the statements inside the loop will execute at least once, we will spawn six instances instead of five. Run the code to see what happens. Explore the code in the file gms2_ch02_07.yyz.
Looping With the for Loop
The for Loop, Creating Five Enemy Instances
If you look at the while loop, you can spot the similarities. The for loop has three distinct parts separated by semi-colons. The first section is for initializing variables. The second part is for a condition that, if false, will cause the loop to end. The third part is used to increment the variable we declared in the first section. Here, in the first section we declare a variable i and set it to zero. In the second section, we check if i is less than five; if it is less than five, then we spawn an enemy by executing the statements contained within the loop’s curly braces. We then increment the variable i, which then continue the loop by checking the condition. Run the code to see what happens. Explore the code in the file gms2_ch02_08.yyz
Of Infinite Loops
An infinite loop is a loop that does not end; it keeps on running until your game becomes unresponsive and crashes. One needs to be wary of what conditions one lays down before executing code that contains a loop. If your conditions are not as clear as our examples above, or if they depend on results from functions or other arbitrary factors, then one needs to be very careful and should evaluate the code before running it.
The with Construction
We did look at the with construction a while back by changing the color of a bunch of objects. In a way, it works like a loop; it takes an object in your resources and then loops through all the instances of that object in that room. What it does additionally is that the code that is executed is within that object’s scope. This is what makes the with construction very special. Let’s add some code to the existing example and expand it to see the with construction in action.
Stopping Traffic With the with Construction
Both sets of code are like each other, but one set affects the enemy objects moving to the left, and the other set affects the objects moving to the right, separately. In both cases, we use the with construction to loop through each instance of the objects. After switching the scope to an instance, we use the distance_to_object() function to check the distance between this enemy instance’s location and the other object—here, referring to the calling object, the player. If the distance between the instance and the player is less than or equal to 100, then we counter the movement. Remember that in the Step event of each enemy object, we are moving the object 10 pixels to the right or left. Here we counter that change by the same value, 10 pixels. So, the overall effect is that the object will remain in its last position. Run the code to see what happens. Explore the code in the file gms2_ch02_09.yyz
The Switch Statement
Switch Statement Structure
The switch statement takes in an expression that can be evaluated, the result of which is compared to the constant provided with each case statement. The catch here is that the value with which the evaluated result of the expression is compared must be a constant. After each case, we use the break statement to force ourselves out of the switch statement. A default case is provided as a catchall if the expression that is evaluated does not match any of the cases.
Switch Statements Adding a Tint to the Player (Refer to: gms2_ch02_10.yyz)
Here, we club two cases, one for the number pad on keyboards as well as the row of number keys on regular smaller laptops. What happens here is that if any of those two cases are true, then the statements below them are executed. To quickly find which keys are mapped to what values, use the show_debug_message() to print the value of keyboard_key.
The break, continue, and exit Statements
After Spawning Three Enemies, When the Variable i’s Value Becomes Two, Exit the Loop
The break statement works the same way and can be used from within loops, switch statements, and the with statement. In fact, as you have seen before, it is a crucial part of the switch statement.
Exits Execution if the Object Is Not Visible
In the example in Listing 2-35, if the object’s visible property is set to false, it stops from executing any code that comes after it and exits execution.
Now that we have a basic understanding of what GML is and how to write scripts in GameMaker Studio 2 to manipulate your game, the objects within them, as well as how to make your games interactive, we can now move on to making a simple game in the next chapter.