© Yadu Rajiv 2018
Yadu RajivDeveloping Turn-Based Multiplayer Gameshttps://doi.org/10.1007/978-1-4842-3861-5_2

2. An Introduction to GML

Yadu Rajiv1 
(1)
Bangalore, Karnataka, India
 

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.

With all these events available, there will be times when you would want to know the exact order in which these events are executed. We are unable to clearly determine the exact order in which events are executed during each frame of the game, but we can be sure of certain events that always run in the same order. Let’s look at the first set of events that are fired and the order in which they are fired.
  1. 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. 2.

    Instance Variables : If you override any of those Object Variables in the instances via the Room Editor, those will get set next.

     
  3. 3.

    Create Event: After this, the Create event for each object is called.

     
  4. 4.

    Instance Creation Code: The Create event is followed by the instance’s creation code.

     
  5. 5.

    Game Start Event: If the Object has a Game Start event, then this is called next.

     
  6. 6.

    Room Creation Code: After the Game Start event, the code in the Room Creation Section for the room is executed.

     
  7. 7.

    Room Start Event: Finally, the Room Start event for the Object is called.

     
Apart from these events, the Step and Draw events of objects are also executed in a set order. The Step events in an object occur in the following order:
  1. 1.

    Begin Step Event: Executed first before the Step event

     
  2. 2.

    Step Event: The actual step event, executed before objects are moved to new positions, if any

     
  3. 3.

    End Step Event: Executed after the Step event

     
The Draw events execution order is as follows:
  1. 1.

    Pre-Draw Event: Executed first, before all other Draw events

     
  2. 2.

    Draw Begin Event: Executed right before the Draw event

     
  3. 3.

    Draw Event: The actual Draw event

     
  4. 4.

    Draw End Event: Executed after the Draw event

     
  5. 5.

    Post-Draw Event: Wrapping up the Draw events with the post-draw event

     
  6. 6.

    Draw GUI Begin Event: Once all other game elements are drawn, the Draw GUI Begin gets triggered.

     
  7. 7.

    Draw GUI Event: Then the actual Draw GUI event occurs.

     
  8. 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.

A new object called object0 will get added, and it will open in the Object Editor. In the Object Editor, click on the Add Event Button and select Create. In the Script Editor that opens, on the third line, type the following:
show_debug_message("Hello, I am making games!");

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.

Even if the instance of our object is invisible, it is still part of the room; and the Create event was executed when the instance of the object was created. Now double-click on the instance of our object named object0 from within the room editor to bring up the Instance Editor. In the Instance Editor, click on the Creation Code button to bring up the Script Editor. Add the following line to the Creation Code event:
show_debug_message("I am an instance of object0");
Run the program again to see what results you get. You will notice that our first message is displayed first, followed by the message from the instance creation code. Now let’s try to add creation code to the room. Double-click the room in the Resources Panel and in the Room Editor, click the Creation Code button in the Properties panel to the left. In the Script Editor that opens, key in the following:
show_debug_message("Welcome to the room!");

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.

Finally, let’s create a script. We can do this by right-clicking on the Script section in the Resources Panel and selecting Create Script. As soon as the script is created, we have the option to rename it: we’ll call our first script welcomeScript. Once this is done, click inside the Script Editor that is open in front of you. As you can see, the Script Editor’s tab now says welcomeScript.gml. Script resources are saved on the disk and can be accessed outside of the GMS2 project as well. Scripts are usually used to store generic, often repeated, but useful functionality in a reusable manner. They can also be used to make your code cleaner and organized. Type the following in the Script Editor:
show_debug_message("Welcome to GameMaker Language! Have fun coding!");
Now to execute this script, let’s go back to the Object Editor and select its Create event. Right above where were we wrote the code before, add in
welcomeScript();
show_debug_message("Hello, I am making games!");
Execute the game, and you should see the result shown in Figure 2-1.
../images/456885_1_En_2_Chapter/456885_1_En_2_Fig1_HTML.jpg
Figure 2-1

Output window with our debug messages

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

Commenting on code is a way to annotate your code for clarity. When you create larger games, the amount of code and scripts that you use would need to manage usually increases exponentially. Comments serve to help you understand quickly what a chunk of code is intended to do. This comes in handy when you revisit sections of code after long intervals or act as helpful notes for other members of the team who would need to work with you. There are primarily two ways of commenting in GameMaker Studio: a single line comment and the multiline comment. Comments are ignored by the compiler, and you can write anything in them. Listing 2-1 below shows you how a single line comment works.
// updates the player position
x += speedx * dt;
y += speedy * dt
Listing 2-1

Single-Line Comment Describing the Actions in the Statements That Follows

Multiline comments function like single-line comments but can encompass a larger area. They also have a starting and an ending block. Anything after the first /* and before the */ is treated as a comment and ignored by the compiler. Listing 2-2 below shows you how multiline comments work.
/*
To find the column without the % modulo operator
column = index - (row * width)
*/
column = index % width;
row    =  floor(index / width);
/*
show_debug_message(row);
show_debug_message(column);
*/
Listing 2-2

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.

Add a new object into the Objects section in the Resources Panel. Drag and add it into the room, and then open the Object from the Resources Panel in the Object Editor. Once it is open, click the Events button and add a Create event. Add the code shown in Listing 2-3 into the Create event. Let’s try to print to the debug console the data that is contained in some of the built-in variables that we can use in the future.
/// working with variables
// some properties of the room
show_debug_message(room_height);
show_debug_message(room_width);
// some properties of the object
show_debug_message(x);
show_debug_message(y);
Listing 2-3

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.

Let’s change the x- and y-property of our object. Create a new event for the object and select the Step event. We will use the assignment operator = to set the values for the x and y variables. Let’s set the x- and y-values to the mouse x- and mouse y-values, respectively (Listing 2-4).
x = mouse_x;
y = mouse_y;
Listing 2-4

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.

Since we have a sprite attached to the object, we can manipulate the sprite’s properties as well. Let’s try to change the rotation of the sprite every frame by adding this to the Step event. Run the game to see what happens, as in Listing 2-5.
image_angle = image_angle + 1
Listing 2-5

Rotate the sprite 1 Degree Every Frame

Instance, Local, and Global Variables

We saw some of the built-in variables which we have access to in action in the previous section. Here, we will look at instance, local, and global variables. Declaring and initializing an instance variable in GameMaker Studio is as easy as assigning a value to the variable name (see Listing 2-6).
message = "Hello";
Listing 2-6

Stores the String “Hello” in the Variable Message

As we have seen, most of the code you write is associated with objects or is in scripts. When you declare and initialize a variable like you see above, that variable has a scope within that object and its instances. You will constantly use them, as they can also be used across events and from other objects and instances. If you declare a variable in any event it becomes part of that object. Variables like these can be used to extend objects with custom properties, and it is good practice to declare and initialize them in the Create event. Trying to access variables that have not been declared will lead to errors, and those that are not initialized with any data may work in unpredictable ways. Instance variables for an instance will remain in memory till that instance is destroyed.
playerMana = 10;
playerHealth = 100;
playerAttack = 5;
Listing 2-7

Extending a Player Object With Some Properties

Local variables are like instance variables, but their scope is limited to the events in which they are declared. They are discarded and removed from memory as soon as the event is done. Because of their temporary nature, you will use them to do quick operations where you do not need to store data permanently or for a longer duration of time. You declare a local variable by using the var keyword before the variable name (see Listing 2-8).
var message;
message = "hello friends!";
var length = string_length(message);
Listing 2-8

Both Message and Length variables Will Be Discarded Once the Event/Script has Finished Executing

Global variables are variables that are persistent across instances, rooms, and scripts. Once declared they remain in memory and can be accessed anywhere until the end of the game. Global variables are useful to hold and manipulate information across instances, scripts and rooms. A great place to declare your global variables would be the start of the game. All global variables are declared as part of a constant called global. The variable you want to declare is added to this using a dot operator (see Listing 2-9).
global.currentLevel = 0;
Listing 2-9

A Global Variable Called currentLevel

We can manipulate this variable just as easily from anywhere. This is a blessing and a curse. If you are not extra careful when creating and manipulating global variables, they can cause serious problems in your game that can be very hard to track down and fix.
global.currentLevel = global.currentLevel + 1;
Listing 2-10

Incrementing the Global Variable

Accessing Variables From Other Instances

Instance variables can be accessed from other instances in two ways: one using the dot operator and the other using the with statement. There are different implications that one needs to be aware of while using these. You can access all instances and their variables in a room by using their object’s name, which means if you have 10 instances of the object objEnemy, you can manipulate all of them by using the variable objEnemy . This is one of the features that make GameMaker Studio very powerful. Let's do a small exercise to see how this works (see Listing 2-11). Start by creating a new object called objEnemy and add a color variable to it in the Create event.
enemyColor = c_yellow;
Listing 2-11

Our Enemy Is Yellow for Now

Let’s make the enemy visible by giving it a shape. Add a draw event to your enemy object and add the code from Listing 2-12 to it.
draw_set_color(enemyColor);
draw_circle(x,y, 10, false);
Listing 2-12

Draw a Circle With Our Enemy's Color

Drag and drop 10 of objEnemy objects onto the room, and run the game to see all of them being drawn on the screen. Once this is done, let’s see how the dot operator and the with statement works. Create a new object and rename it objEnemyColorRandomize . Create a new rectangular sprite and add it to this object. Add a new Mouse-Left Released event to this object and add the following code to it (see Listing 2-13). Once this is done, drag and drop it to the middle of the room.
with(objEnemy) {
        enemyColor = make_color_hsv(irandom_range(0,255), 200,200);
}
Listing 2-13

Change the Value of enemyColor in Each Instance to a Random Color

The with statement in this case loops through all the instances of the object called objEnemy and changes the enemyColor variable to a random color.
objEnemy.enemyColor = make_color_hsv(irandom_range(0,255), 200,200);
Listing 2-14

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

The other keyword is primarily used in two contexts: within a collision event, where the other refers to the other instance that collided with the calling instance and when using the with statement. We discussed that the with statement temporarily changes the scope to that of the given instance, and when, within that scope, you want to access data from the calling instance, you will need to use the other keyword. Let’s rewrite our previous example to show you how the other keyword works (see Listing 2-15).
color = make_color_hsv(irandom_range(0,255), 200,200);
with(objEnemy) {
        enemyColor = other.color;
}
Listing 2-15

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.

The no one keyword is usually used to designate that the variable holds no instance. It is used widely to check if a variable holds some instance. In the following example, the instance_position function returns an id of an instance of the object objEnemy if it exists at the location mouse_x and mouse_y. If the object doesn’t exist at that location, “noone” is returned.
var foundEnemy = instance_position( mouse_x, mouse_y, objEnemy);
if(foundEnemy != noone) {
        instance_destroy(foundEnemy);
}

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.

Your basic arithmetical operators are +, -, *, and /, which do addition, subtraction, multiplication, and division, respectively. Listing 2-16 provides an examples of these.
sum = 5 + 10;
largeSum = sum * 15;
split = largeSum / 4;
Listing 2-16

Basic Arithmetical Operators in Action

Some of these operations can be written in short-hand using ++ and -- for addition and subtraction, respectively.
sum = sum + 1;
is the same as
sum++
Similarly, division can be rewritten with the keyword div. So, rewriting our earlier examples, we get the following:
split = largeSum div 4;
GameMaker Studio also has a handy modulo operator that gives you the remainder after division between two numbers. The modulo operator is represented by the % symbol or can be written as mod.
r = largeSum % 4;

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

You use the if statement to check if a specific condition has been met and, if so, then do some actions; if those conditions weren’t met, do some other actions. The basic structure of the if statement is shown in Listing 2-17.
if(condition) {
        // execute this if condition is met
} else if(another_condition) {
        // first condition not met, another_condition met
} else {
        // condition has not been met, execute this section
}
Listing 2-17

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.

In our example, we will make a small object with a sprite and move it on the screen using the keyboard keys. This time around, we will use only a Step event and the if statement. Start by creating a new project, adding an object and creating a small 64px × 64px sprite for the object. Drag and drop the object into the room and then add a Step event to the object from the Object Editor. Before we start off, let’s write a bit of Pseudocode to describe what we are about to do (see Listing 2-18). Pseudocode is a way of describing an algorithm or the steps involved in a computer program at a higher level. It borrows the lingo and structures of a programming language but is written in human readable form for people to understand how a program or piece of code works.
If the up arrow is pressed, then
        Move object one pixel to the up.
Else if the down arrow is pressed, then
        Move object one pixel to the down.
If the left arrow is pressed, then
        Move object one pixel to the left.
Else if the right arrow is pressed, then
        Move object one pixel to the right.
Listing 2-18

Pseudo Code for What We Are Trying to Achieve

We want our object to move when we press our four arrow keys in each of those directions. We will use a built-in utility function keyboard_check() to see if the arrow keys are being held down. The keyboard_check() function takes in one parameter that represents the key we are checking for. To make our lives easier, GameMaker Studio has built in constants that we can use for the keys we need. In our Step event, write the code from Listing 2-19.
if(keyboard_check(vk_up) == true) {
        y -= 1;
} else if(keyboard_check(vk_down) == true) {
        y += 1;
}
if(keyboard_check(vk_right) == true) {
        x += 1;
} else if(keyboard_check(vk_left) == true) {
        x -= 1;
}
Listing 2-19

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

GameMaker Studio has a set of comparison operators that come in handy when using the if statement. In the Listing 2-19, we use the short-hand for checking if the value returned by the keyboard_check function is true or not and execute the statements if they are only true. The above can be rewritten in the following way and it will still work:
if(keyboard_check(vk_up)) {
        y -= 1;
} else if(keyboard_check(vk_down)) {
        y += 1;
}
if(keyboard_check(vk_right) {
        x += 1;
} else if(keyboard_check(vk_left)) {
        x -= 1;
}

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.

The opposite of the equality operator is the inequality operator or not equal to. This is written by combining two symbols: a not (!) symbol and an equal to (=) symbol. Let’s give our small box the ability to move a bit faster when a key is held down and if the key is not held down, we will go with the default speed. Let’s add some new variables to store our speed and boost values by adding them into the Create event of the same object (see Listing 2-20).
playerSpeed = 1;
playerBoost = 10;
currentSpeed = playerSpeed;
Listing 2-20

Add Variables for Our Default Speed, Current Speed, and Boost

if(keyboard_check(vk_shift)) {
        currentSpeed = playerBoost;
} else {
        currentSpeed = playerSpeed;
}
if(keyboard_check(vk_up)) {
        y -= currentSpeed;
} else if(keyboard_check(vk_down) {
        y += currentSpeed;
}
if(keyboard_check(vk_right)) {
        x += currentSpeed;
} else if(keyboard_check(vk_left)) {
        x -= currentSpeed;
}
Listing 2-21

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

Now that you understand the difference between the equality (==) and inequality (!=) operators , let’s look at some more comparison operators. Let’s start by adding a new object into our resources. Create a sprite for the object and fill it with a bright red color. Add the object toward the bottom-center of the room and open the object in the Object Editor. What we are going to do is to automatically move this object across the screen. Think of it as an enemy moving across the screen. Add a Step event to the object and add the code as in Listing 2-22.
x += 10;
Listing 2-22

Moves the Object 10 Pixels to the Right

If you run the game now, you will see that our new red object moves to the right and disappears off the screen. We want the object to loop back and come through the other side of the screen once it leaves the room. This is shown in Listing 2-23.
If the object's x-position is outside the room toward the right, then
Move the object back to the start of the room.
Listing 2-23

Pseudocode for What We Are Trying to Achieve

To find out if the object has gone beyond the size of the room, we have two handy variables: room_width and room_height that will provide us with the size of the room. To check if our object has gone outside, all we need to do is to check if the object’s x has gone beyond the room_width, since we are moving toward the right. If we were moving toward the left, we would have had to check if our object’s x was less than zero. We can rewrite the Step event as shown in Listing 2-24.
x += 10;
if(x > room_width) {
        x = 0;
}
Listing 2-24

Loop the Object Back if the Current x-Position of the Object is Greater Than the Room Width

Duplicate this object, drag and drop the new object onto the room above the player to the top-middle. Open this object up in the Object Editor and change the Step event so that this new object moves to the left of the screen. As explained earlier, we will have to check if the object has moved to a position outside the screen to the left—that is, if the object’s x position is less than zero.
x -= 10;
if(x < 0) {
        x = room_width;
}
Listing 2-25

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

The most straightforward way to repeat an action a specific number of times is to use the repeat statement. In the Create event for objSpawner write the code shown in Listing 2-26.
repeat(5) {
        // pick a random x and y position to spawn our enemy object
        var dx = irandom_range(0,room_width);
        var dy = irandom_range(0,room_height);
        // pick a random enemy and create an instance of it at the random x and y we picked instance_create_layer(dx,dy,"Instances", choose(objEnemyRight, objEnemyLeft));
}
Listing 2-26

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.

Since we will be exploring multiple types of loops to reduce redundancy, we will create a script and move the enemy spawning code there. Add a new script to the Resources Panel, and rename the script to spawnEnemy. Move our enemy spawner code from the loop above to the new script in Listing 2-27 and save it.
/// spawnEnemy - spawn a random enemy at a random position in your room
// pick a random x and y position to spawn our enemy object
var dx = irandom_range(0,room_width);
var dy = irandom_range(0,room_height);
// pick a random enemy and create an instance of it at the random x and y we picked
instance_create_layer(dx,dy,"Instances", choose(objEnemyRight, objEnemyLeft));
Listing 2-27

Enemy Spawner Script

Now in the Object Editor for objSpawner, we can rewrite the Create event like that in Listing 2-28.
repeat(5) {
        spawnEnemy();
}
Listing 2-28

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 do...until loops are very similar to each other. Both loop until a specific condition is met. To reiterate our previous example and to understand the similarity or difference, let us look at them side by side (see Listing 2-29).
var i = 0;
while(i < 5) {
        spawnEnemy();
        i = i + 1;
}
var i = 0;
do {
        spawnEnemy();
        i = i + 1;
} until(i > 5);
Listing 2-29

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 is like the while loop, except that it contains the declaration of a variable, a condition if invalidated exits the loop, and a part for incrementing the variable. So, if we were to rewrite the same while loop from our previous example, we can do so like shown in Listing 2-30.
for(var i=0; i<5; i++) {
        spawnEnemy();
}
Listing 2-30

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.

In the Step event of our player object, append the code in Listing 2-31.
with(objEnemyLeft) {
        if(distance_to_object(other)  <= 100) {
                x += 10;
        }
}
with(objEnemyRight) {
        if(distance_to_object(other)  <= 100) {
                x -= 10;
        }
}
Listing 2-31

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

The switch statement is a set of conditions and set of statements that get executed if a condition is met. It works almost like the if...else-if...else statement but suited for longer branching code. It also helps reduce redundancy by clubbing conditions that have the same outcome together. Let’s write some code that will give our player sprite some color. We will do so by changing the image_blend variable value to a color, either by setting it to a constant color value or by using a one of the color creation functions. The switch statement structure is as shown in Listing 2-32.
switch (expression) {
    case constant:
        // code here
        break;
    default:
        // code here
        break;
}
Listing 2-32

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.

We will write our code so that when the player presses the number keys from 1 to 5, the player’s sprite will change to a red, green, blue, random color, and original color respectively. Let’s use the switch statement to make this work. We will not be using the default case, as we don’t want any color change to happen if the user does not press any key.
switch (keyboard_key) {
    case vk_numpad1:
    case 49:
        image_blend = c_red;
        break;
    case vk_numpad2:
    case 50:
        image_blend = c_green;
        break;
    case vk_numpad3:
    case 51:
        image_blend = c_blue;
        break;
    case vk_numpad4:
    case 52:
        image_blend = make_colour_hsv(255, 255, random(255));
        break;
    case vk_numpad5:
    case 53:
        image_blend = c_white;
        break;
}
Listing 2-33

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

There will be cases where you will be required to exit a loop prematurely or to let it skip over to the next step (jump to the increment section or next instance and continue from there).
for(var i=0; i<5; i++) {
        spawnEnemy();
        if(i == 2) {
                break;
        }
}
Listing 2-34

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.

When the program encounters a continue statement in a loop, it skips the rest of the code and jumps to the start of the next loop. If the continue statement is used outside of a loop, it exits the current scope of execution, which means if the program encounters a continue statement in a script, it exits the script instantly and continues from the calling instance’s event. If a continue is encountered in an event, the program exits the event. A continue statement within a with statement causes the script to move the execution to the next instance. The exit statement works in a similar way, but it exits the current script or event and does no further processing.
if(!visible) exit;
Listing 2-35

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.231.245