My First Arena Shooter | GML


My First Arena Shooter | GML

Introduction

This tutorial will take you through the process of creating a small game using GameMaker Studio 2. The tutorial itself is rather large and covers everything from the GameMaker Studio 2 user interface to creating game graphics and then getting them moving, as well as player interaction within a game, and much, much more. So, to keep things simple we have split it into the following sections:

  1. Movement
  2. Shooting
  3. Enemies
  4. Tiles And Views
  5. Scoring
  6. Sounds
  7. Title Screen
  8. Spawners

Each section is also accompanied by a short video that takes you through each of the steps outlined in text, and you can close GameMaker Studio 2 at any time in the progress of this tutorial (saving your project) and when you next load the project you will be taken to the place where you left off. If you wish to play around with what you have learned so far, we recommend that you save the project in its current state - so that you can continue from the same point later - then save it again with a different name and edit the renamed project so as not to lose or change anything that is important to future sections.

The videos that accompany each section can be found from the following links:

  1. Movement
  2. Shooting
  3. Enemies
  4. Tiles And Views
  5. Scoring
  6. Sounds
  7. Title Screen
  8. Spawners

Movement

Starting A New Project

This is the My First Arena Shooter | GML tutorial designed to get you started making your first game with GameMaker Studio 2. Throughout this tutorial we will build a small "arena shooter" - a top down action game with a player, some enemies and lots of bullets. The final game will look something like this:

Final Game Screenshot

We will be keeping the tutorial as simple as possible, with the idea being to give enough of an overview of how things work for you to get stuck in and making your own games as quick as possible. 

We'll quickly give an overview of how you create a new project from scratch for the future to start with.


NOTE: If you close the accompanying video then you can get it back by clicking here


First you would open GameMaker Studio 2, and then click on the New Project button shown on the main Start Page:

New Project Button

You will then be presented with two options:

  • New Drag and Drop Project
  • New GameMaker Language Project

Drag and Drop is the powerful visual scripting language that permits you to code a project using chained action blocks, while the GameMaker Language (GML) is the propriety scripting language that permits you to create games using the code editor. For this tutorial we are going to use GML so you would click that button and then choose a project name to go with the game you want to make.

NOTE: If you are a more visual person then you may prefer to use visual scripting over code, so this tutorial is also available as a Drag and Drop tutorial here.

Once you have created and saved this initial blank project you will be presented with the GameMaker Studio 2 workspace...


The Workspace

The workspace is the name we give to the main area in the middle of the window where you will be doing the bulk of your work. There are different types of workspace too, but we'll cover that a bit more later. On the right of the workspace we have the Resource Tree, which lists all the different elements your project can use, like sprites, or objects, or rooms. We will explain what each resource type is as we come to it in the tutorial, but we won't be covering all of them.

The Main Workspace And Resource Tree

It's worth noting that the resource tree is considered to be docked to the side of the window, and it can be opened or closed using the dock buttons:

Open And Close Docks

and it can also be moved to a different screen position and docked there (for example on the left):

Changing Dock Position

GameMaker Studio 2 is fully customisable and you can set layouts and save them as well as change the colour. That aspect of the program won't be discussed in this tutorial and we'll be using the default layout, but you can find more information on customisation in the manual (press ).


Sprites

We have our new project and an empty resource tree, so it's time to get started making our game. The aim of this tutorial is to simply get a "player" object onto the screen and moving around, and the first thing we are going to need for that is a Sprite. A sprite is an image that is combined with certain properties which is then used in the game to represent something. In this case we are going to make a sprite to represent our player.

Let's go ahead and create our first sprite resource. Simply click the right mouse button  on the Sprite resource and select Create:

Create A Sprite

This will open the Sprite Editor where we can add an image and set certain properties for the sprite:

The Sprite Editor

To start with, you need to name the sprite. This name will be the unique identifier for the sprite throughout your game and can be anything you wish, although we recommend that you use some kind of identifier prefix like "spr" or simply "s", as, when you get multiple resource types in your game code, it makes everything far easier to read. So with that in mind, call this sprite "spr_player".

We now need to add an image to the sprite to use as our player. For that we'll use the following image in this tutorial, but you can create your own if you want:

Player Sprite

You need to click on the button  to open the file explorer where you can browse for an appropriate image, which must be either PNG, GIF, or JPG format. This tutorial has created a TutorialResources folder for you within the project files, and when you open the file explorer to choose a sprite, it should open on that folder where you can find the sprites used in the "Images" sub-folder. If you have any issues, you can also find the images here.

You will get a warning saying that this action cannot be undone, which is fine since this is a new sprite and we aren't going to over-write anything, so click "Yes" to continue. The Sprite Editor will now look like this:

Adding An Image

The bottom part of the editor will show a single image, and the main window will have a larger preview. If you add in a sprite animation, the bottom part will show each individual frame and clicking on them would show that frame in the preview window above, but as we only have one image in the sprite it only shows that image.

Now that we've added an image, we need to set its origin. The origin is simply a point on the sprite that will be used to position it within our game room, and you can set it by either clicking  anywhere on the preview image to set the origin to the mouse position, or using the drop down menu to set a fixed position for it:

The Origin Menu

You can see that in the image we indicate Middle Center so select that as the position for the sprite origin. With that done, we are now ready to create an Object Resource which will use our sprite.


Objects and Instances


Objects will be used for almost everything in your game, from the Player, to the enemies, to the bullets and explosions, and you can define how they behave and react by giving them code. But, before creating our first object, let's quickly run over what an object is conceptually.

The idea behind an object is to create a kind of blueprint of behaviours that can then be used within a game. We think of it as a blueprint because in a game you don't actually have any objects, you have instances instead. Instances are copies of the initial object placed within the game room. So, objects are in the resource tree, and are then used to create the instances that populate our game room. This means that changing an instance's properties will not change the properties of the object it was made from, however changing an object will change all subsequent instances created from it.

We are going to go ahead and create an object now. This object will be for our player and is what will permit it to move and shoot in the game room. To create the object, right click  on the "Object" resource folder and select Create:

Create Object

This will create a new, blank, object for us and open the Object Editor:

Object Editor

The object editor will be opened within the same workspace as the sprite editor, and it may push the sprite editor out of the visible space. You can hold down the middle mouse button  and move the mouse to "pan" the workspace around and you can also use  /  + the mouse wheel  to zoom it in and out. When you have multiple resources open you can also use  /  +  to open the workspace switcher and select the editor that you want to see directly.

Now we have our object we need to name it, so we'll follow the same convention we outlined previously and use a prefix to define what kind of resource it is and give it the unique name "obj_player" to match the sprite "spr_player" that we made previously.

We can now associate the sprite to the object by selecting it from the list of available sprites. To do this, you will need to click the button that is currently labelled "No sprite" and select the sprite "spr_player" from the list of available resources:

Adding A Sprite

Associating an object with a sprite in this way means that when you place an instance of the object in the game room, this is what will be drawn and certain attributes of the sprite will be used by the instance. Let's go ahead and place an instance in the game room now...


Rooms

The only resource that is created for you by default when you create a new project is the Room Resource. This is because all games require at least one room to run, and so GameMaker Studio 2 makes this room for you. You can delete it, and you can edit it, and you can create further rooms using the right button  menu. In this case we will simply edit the default room "room0".

Default Room

To open it for editing you need to double-click  on it, which will open the Room Editor in its own workspace:

The Room Editor

Rooms can be used for just about anything. You can have a single room for every aspect of your game, like splash screen, main menu, introduction, level 1, 2, 3, etc... or you can have a single room and generate everything using code. Normally you'd have a mix of both things, with some rooms fulfilling multiple purposes (like one room for all your different menus) and other rooms for a single purpose, like an overworld room, or a level room.

With the Room Editor workspace open, you will see that most of the screen is taken up with a large black area with grid squares over it. This are is the "room" itself, and is essentially a blank canvas where you can then place things to create your game world. In this case we are simply going to place an instance of the player object.

Everything that goes into a room is placed on a layer (we'll discuss layers in more detail in further tutorials) so make sure the Instance Layer is selected in the layer properties window:

Layer Properties

Then you can simply drag an instance of the object "obj_player" into the room by clicking  on the object in the resource tree and dragging it into the room where you want it to appear:

Add Instance To Layer

We can actually run the "game" now as we have a room and an object so go ahead and click the Run Button  at the top left of the IDE. You'll see some text scroll up in the output window at the bottom of the window, and then the game will run:

Game Image

Exciting stuff! Well, not yet... but it will be when we make our player instance actually do something. Before continuing, we should give the room a name, as the default name isn't very descriptive so give a slow double click  on resource name and call the room "rm_game". Now let's add some code to the player object.

Events and Code

You can close the game window now (if you haven't already) and then double click  on the "obj_player" object in the resource tree. This will take us back to our main workspace and focus it on the object we want to edit.

You'll notice that our object has another window chained to it, the Event Editor. A game in GameMaker Studio 2 is simply a selection of code within events that are performed a number of times every second. This is called the "game loop" and a single game loop is called a game frame. The number of game frames in a second is what gives us our Frames per Second (FPS) value for the project. By default this is set to 30, meaning that our game will perform a game frame (loop through all the game code) 30 times every second and in each single frame of the game code you have Events. These events fall into two broad categories:

  • Those events that happen every single game loop - like the Step event or the Draw event
  • Those events that happen only when a certain criteria has been met - like the Keypress event or the Create event

You can see all the event categories by clicking  on the Add Event button:

The Event List

In the image above you will see that we have highlighted the "Step" category, and within that the general Step Event, as this is what want to add to our object. If you select it then you'll see the event added into the Event Editor window and a new window chained to it:

The Code Editor

As mentioned above, the Step Event is run each and every game loop, so anything we put in here will happen once per game frame (30 times in a second for a 30 FPS game, 60 for a 60fps game, etc...). In this case we are going to add some very simple code to the event to make the player instance move right across the screen:

Code: Step Event

x = x + 4;


The step event should now look like this:

Final Test Code

All instances have some built-in variables, of which "x" and "y" are perhaps the most important as they set the position of the instance in the game room. In this case we are telling GameMaker Studio 2 to take the x position value and then add 4 to it. If you now run the game again, you'll see the player instance move off to the right:

Basic Player Movement

We now have some movement, but it's not very much fun for anyone... let's now add some new code to the player to make it respond to the user pressing the arrow keys on the keyboard.


Movement

We want to now make the player instance move when the user presses the Arrow Keys on the keyboard. Currently we are adding 4 to the "x" value every game frame, so what we want to do is only add 4 if the keyboard right arrow is being pressed. For that we use "if" followed by a condition that will evaluate to either true or false, and if it evaluates to true, then we will move the instance by 4. We'll modify the existing code to look like this:

Code: Step Event

if (keyboard_check(vk_right))
x += 4;

The conditional we are checking is the value returned by the function "keyboard_check" which returns true if the key (in this case the right arrow) is being held down and false otherwise. Note that we put the condition in brackets () and then afterwards what we want to do if it evaluates to true (in this case change the "x" position). Also note that at the end of the line of code we have a semicolon ";". This should always be added to delimit the end of one line and the start of the next.

Your code should now look like this:

Code

You can see that the Code Editor colours the different parts of what you write. All built-in functions, built-in variables, resources, local variables and constants get colour coded automatically to make reading your code easier. The exact colours used can be changed from the Preferences workspace.

Run your game again (press ) and this time the player instance should do nothing unless you press the right arrow key.

We can expand the current code block now to cover the remaining three directions of up, left and down. To do this simply select the whole line of code (left click  at the start and drag to select or double click  in the gutter - the area with the line numbers - which will automatically select the whole line) then press  /  + C to copy. Move to the next empty line and hit  /  + V to paste. Do this three times so you have four copies of the same line of code:

Code

You can now edit the code like this:

Code: Step Event

if (keyboard_check(vk_right)) x += 4;
if (keyboard_check(vk_left)) x -= 4;
if (keyboard_check(vk_up)) y -= 4;
if (keyboard_check(vk_down)) y += 4;


As you can see, we subtract from x to go left this time, and we have also changed the last two conditionals to change the "y" position instead of the x position, where subtracting from "y" moves up, and adding to "y" moves down. In GameMaker Studio 2 the (0, 0) position is considered as the top left hand corner of your room and the horizontal axis is the x axis and vertical axis is the y axis:

Room Axis And Position

Run the game now (press ) and move the instance with the arrow keys:

Player Moving Example


Rotation

We are almost finished this tutorial, but there is one final thing we want to happen with out player instance... We need to make it rotate to always face the mouse while it is being moved around the screen. That way, the user can move with the keyboard and aim and shoot with the mouse. We'll leave the shooting for the next tutorial in the series and simply add the rotation for now.

For this, we are going to use another of the built-in variables that all objects have - the image_angle. This variable controls the angle of the sprite assigned to the instance, so setting it to anything other than 0 will rotate the sprite (and its associated collision mask) by the amount given. By default this is set to 0 for every instance in the room, but it can be changed at any time, and in this case we are going to get the direction from the player instance to the mouse pointer and set the image_angle to that.

For that to work, we need the following code (still in the "obj_player" Step Event):

Code: Step Event

image_angle = point_direction(x, y, mouse_x, mouse_y);

The function point_direction takes two positions within the room and returns the direction from the first position to the second in degrees from 0 to 359. In this case from the player position to the mouse position, which we get from the two global scope variables mouse_x and mouse_y (global scope variables are variables that do not "belong" to any instance, but instead belong to the whole game and as such all instances can change them and use them).

NOTE: In GameMaker Studio 2 angles are calculated anti-clockwise where 0° is to the right, 90° is up, 180° is to the left and 270° is down.

Your step event should now look like this:

Code

If you run the game one more time now, you should see that the player instance moves around and that it turns to follow the mouse:

Final Movement Test


Summary

The first section of this tutorial is now complete, and you have the player moving around and you have their "ship" turning towards the mouse ready to start shooting! Before going any further though, let's just go over a few of the core concepts that you should take away with you from this section:

  • You were shown how to create a New Project

  • You were shown how to dock windows to the workspace, and how to move around the workspace with the mouse

  • You learned that sprites are image resources with some additional properties

  • You learned that objects are what makes a game function, and that they are "blueprints" for instances in the game room

  • You learned about the game loop and Events

  • You found out that you can only have a game if it has at least one room resource

  • You wrote some basic code, and learned about functions, built-in variables and conditionals

  • You made a test project that does something!

Hopefully when you look at that list you can feel proud of yourself. You've come a long way from knowing nothing to creating something that is actually interactive - if not quite a game yet - and have learned the basics of how to use GameMaker Studio 2. In the next section we'll expand on what you've learned here by adding bullets and other world items into our Arena Shooter game.


Shooting

Bullets

This section picks up where the previous section left off. In the previous section , you created a sprite, assigned it to an object and placed an instance of that object in a room. You then made it move around with the arrow keys, and always rotate to point at the mouse position. That was a good start, but it still wasn't a game, so in this tutorial we're going to add a shooting mechanic to the project.

NOTE: If you close the accompanying video then you can get it back by clicking here

Obviously the first thing we are going to need is a new sprite to represent a bullet. We covered how to add a sprite in the first part of this tutorial, so we'll only briefly run through the procedure here:

  • Click the right mouse  on the Sprite resource folder
  • Select "Create" to create a new sprite
  • Name the sprite "spr_bullet"
  • Click  "Import" and choose a suitable sprite (remember, while following this tutorial the file explorer should take you to the TutorialResources folder automatically where you can find the sprites used in the "Images" sub-folder). If you have any issues, you can also find the images here.
  • Place the origin of the sprite

If you have used the sprite that we used for the tutorial, you should be placing the origin near the bullet image "head" (as shown in the image below), as that is the point that we want to rotate it around and "pin" it into the room with. If you have used your own sprite then click the image where you think would be best place to put the origin for the image.

Placing The Origin

The Bullet Object

As with the sprite, we talked about how to create objects in the previous tutorial, so we'll only quickly run down how to do it here:

  • Click the right mouse  on the Object resource folder
  • Select "Create" to create a new object
  • Name the object "obj_bullet"
  • Click  where it says "No Sprite" and assign the sprite we just created

Our new object will have the Events window chained to it and ready for us to add some code into, but first we are going to go back to our player object and edit that a bit. So double click  on the object "obj_player" from the resource tree to open it in the current workspace if it's not open already.

Open the code in the Step Event. We are going to edit this now to dynamically create instances of the bullet object as the game is being played. Instance don't just get placed in the room editor, they can also be created (and destroyed) at runtime using special functions, as we'll see.

In our Step Event code, we can start by removing the comment at the top (as its not needed any more) and then add a couple of our own comments. Why would we do this? Well, in this project it may not be so important, but in larger projects it's very handy to have comments throughout the code to remind you what each part of the code does. So to get into this good habit, add a comment to the top to say that the first block of code is for movement, then at the end add another comment to say that what follows is for shooting:

Adding Comments To Code

After the "shoot" comment you need to add the following:

Code: Step Event

if (mouse_check_button(mb_left))
{

}


That's not the whole code we are going to write, but it's better if we break it down into small bits to start with so that we can point out certain important features. This code performs a conditional check to see if the left mouse button is being held down (remember, "if" conditionals check for a true or a false return value) and then we have the following symbol "{". This signifies that we are creating a new code block, and that everything that goes within the opening curly bracket "{" and the closing curly bracket "}" should be run if the conditional returns true.

Now for the code that will create our bullet if the mouse button is held down. This goes between the {} on a line of its own and should be tabbed (press  to make it clear that this code is part of a conditional code block):

Code: Step Event

instance_create_layer(mouse_x, mouse_y, layer, obj_bullet);

As you type the above code, notice that you get help from the intellisense situated at the bottom of the Code Editor:

Intellisense

The bar is coloured red in the image because the code is erroneous (it's incomplete), and we are also shown the arguments that the function we are going to use requires. This is very handy and helps to cut down on the number of times you need to revise the manual to remember what arguments each function requires.

The arguments we use for the function are the mouse position (x and y), the layer ID of the current layer that the player object is assigned to (more on layers in a moment), and the object index of the object we are going to create an instance of. The final code block should look like this:

Shoot Code

You can hit the Run button  now and see what happens...

Shoot Test

Layers

Before continuing, we should look at the concept of layers in a bit more depth. The bullet instance we are creating is created using the function instance_create_layer(). This function creates the instance and assigns it to the layer ID that you give as an argument, but what is a layer? Simply put, layers are conceptual 2D spaces that we can use to store things, and the order in which we have the layers within the room editor will affect the depth at which the instance is drawn:

Layer Depth Example

In the above image, you can see that by changing the layer order we can change whether the player instance is being drawn over the enemy instances or not. Note that if you have several instances on the same layer, then these will be drawn in the manner that is most efficient, normally from the first created on that layer to the last but this is not guaranteed. So, if you want something to appear above or below something else, it should be placed on an explicit layer. The following image is a schematic representation of how layers are rendered to help you visualise what's going on:

Layer Schematic

In our tutorial game, we have just set the bullet instances to use the same layer that the instance of "obj_player" is assigned to by using the built-in variable layer. All objects have this variable, and when an instance is created in a room, this variable will hold the unique ID value (layer index) for the layer that it has been created on, and it can be read and changed through code too. We could also have created a "Bullet_Layer" in the room editor, and then used that to explicitly say we want the bullets on a unique layer - the code for that would have been simply:

Code: Step Event

instance_create_layer(mouse_x, mouse_y, "Bullet_Layer", obj_bullet);

You can see there that if a layer has been created in the room editor, we can use its name as an identifier to target it within this function (and a few others). To find out more about the layer functions, press  to open the manual and do a search for "layers".

Better Bullets

Going back to our game, if you press Run  now then you can test our bullet creation code:

Not-very-good-bullets

Hmmm... That's not really what we want is it? We need to change the code to make the bullets fire from the player towards the mouse, and not just magically "appear" where the player clicks. To do that, let's first change the Step Event code of the object "obj_player" by simply changing the instance create code to use the player position (x / y) instead of the mouse position (mouse_x / mouse_y):

Changed Creation Code

Once you have made that change, we need to switch to our object "obj_bullet" (double click  it in the resource tree, or use  /  +  to bring up the Workspace Switcher). We need to add a Create Event to our bullet. Unlike the Step Event that we used in the player object, the Create Event will be called only once when an instance of the object is first created in a room. This makes it an ideal place to set up variables and prepare conditions for how the object is to behave.

To add the event, click the Add Event button and select Create from the event list:

Add Create Event

Adding the event will chain a Code Editor window to the Event Editor ready for use. The first thing to do is remove the comment at the top and replace it with out own saying something like:

Code: Create Event

// Set Up Motion

We want the bullet to shoot off in the direction of the mouse, so we need to add the following code:

Code: Create Event

direction = point_direction(x, y, mouse_x, mouse_y);
direction = direction + random_range(-4, 4);
speed = 16;
image_angle = direction;


Before running the game, let's quickly go over each line of that code there:

  • line 1: We set the direction to point directly at the mouse (direction is another built-in variable that all objects have and stores/sets the direction of movement - if not moving, this variable will be 0)
  • line 2: We add a slightly random element to the direction variable to give the bullets a bit of "spread"
  • line 3: We set the speed (another built-in variable) to 16, which means that every game frame it will move 16 pixels in the set direction
  • line 4: We set the image_angle built-in variable to the same as the direction so that the bullet is drawn rotated in the direction of travel (like we did for the player object toturn to face the mouse)

Now that that is all clear, go ahead and hit Run  to see what happens:

Shooting Moving Bullets

That's starting to resemble something that could be called a game, but there is an issue... because we are creating the bullets on the same layer as the player, they are being drawn on top of the player which doesn't look right. So let's fix that now...


The Bullet Layer

As we discussed previously, how things are drawn will depend on the layer order within the room editor, and if we want our bullets to be drawn under the player, we have to create a new layer and add them to that instead of the instance layer that the player is on.

If you open the room editor for our game room (double click  on the resource in the Resource Tree), you will see that by default the Layer Editor is shown docked to the top left of the Room Editor workspace:

Room Editor Layer Properties

Currently we have two layers: "Instances" and "Backgrounds". These are the default layers created for any new room, where the background layer permits you to use a colour or a sprite for a single background image, and instances permits you to add instances in to the room.

Currently the player instance is on the "Instances" layer, and we need to add a new layer for our bullets, so click the New Instance Layer button  to create a new layer and name it "BulletsLayer" (to rename a layer, simply slow-click  twice on it).

The layer has been created above the player layer, which means that anything assigned to it will be drawn after the player, which we don't want. To resolve this, click  on the layer and then drag it down the list until it's under the "Instances" layer, but over the "Background" layer.

Change Layer Order

Layer order is actually defined by a value, called the "depth" value. If you look at the Layer Properties window (by default under the Layer Editor) you can see this value shown, and as you change layer order it will change too. You can change this value if you wish, but by default it's locked and handled by GameMaker Studio 2 automatically.

Layer Depth

Essentially, the higher the depth, the "nearer" the camera it is and the lower the depth the further away, so a layer at depth -200 will draw under a layer with depth 300, for example.

We need to go back to our object "obj_player" and change the creation code to look like this:

Code: Step Event

instance_create_layer(mouse_x, mouse_y, "BulletsLayer", obj_bullet);

As mentioned previously, you can use the layer name (in "" as a string) to tell GameMaker Studio 2 what layer to use, and now if you test the game again, the bullets will be created below the player object.


Bullet Timing

At the moment, our bullets are created 1 per game frame (every time the game loops round and performs the Step Event again), which is a bit too fast for what we require. So now we need to slow the rate of fire down a bit. For this we need to add a Create Event for the object "obj_player", and in it add the following:

Code: Create Event

cooldown = 0;

All we are doing here is preparing a variable called "cooldown" for use later on in the game - remember, the Create Event is only run once when the instance is first created, so this variable is being initialised to 0 once only. We've seen built-in variables already, but this is one you are creating and it's called an instance variable. Instance variables are valid for any event in an instance and can be changed or read in other code blocks. However they are also unique to each instance, so if you have 100 "obj_player" in your game, they will all have an instance variable "cooldown" since you defined it in the base object, but each one could have it set to a different value throughout the time the game runs.

We are now going to use this variable in the Step Event of the player object to control how often the shooting occurs, like this:

Code: Step Event

if (mouse_check_button(mb_left)) && (cooldown < 1)
{
    instance_create_layer(x, y, "BulletsLayer", obj_bullet);
    cooldown = 3;
}


We have used the two & symbols in the "if" to add another condition to the check. The two symbols really just mean "and" so we are saying here "if (one condition) and (another condition) equals true {do something}", and in this way you can check multiple conditions before performing any code blocks. We have also set the "cooldown" variable to 3 after we create the bullet instance, which means that the next game frame, the condition will fail and no bullet will be created because "cooldown" is not less than 1.

Finally we need to add the next line, after the current code, and outside of the "if" check:

Code: Step Event

cooldown = cooldown - 1;

This simply subtracts 1 from the "cooldown" variable every game frame, meaning that now we can shoot once every 3 frames instead of every single frame. Your full codeblock should now look like this:

Full Step Code

If you test the game again now, you can see that the bullets fire a bit slower, but for our purposes it still seems too fast. This is now an easy thing to fix, as it requires a simple change to the number of frames we wait between creating each new bullet instance. So, change the "3" in the Step Event "if" code block to "10" and test again.


Summary

That brings us to the end of Shooting section of the "My First Game" tutorial. Let's just pass over the core concepts and things that you should know after completing this tutorial:

  • How to add comments to code
  • How to use "if" to check multiple conditions
  • How to write code in one instance to create a new instance
  • How to create new layers and change layer order
  • How to create an instance variable and use it

Our project is still not quite a game, as it's missing a few important things... One of which is an enemy to shoot at! So, the next section in this series will see us creating a new object for the enemy and having it react to the player and the player bullets...


Enemies

Game Speed

You've made it to the Enemies section of our "My First Game" series of tutorials, and you're doing well! You should now have a player ship that can move and shoot, and bullets that are timed to come out at regular intervals. So, what next? Well, most games have some form of goal to achieve and more often than not this involves shooting something, so in this tutorial our goal is to add in some enemies for the player to shoot.

NOTE: If you close the accompanying video then you can get it back by clicking here

Green Furry Enemies

Before getting to the part where we add the enemies, however, we are going to take a moment to speed the game up and make it feel more responsive. We could do this by changing the amount of pixels that player moves when the arrow keys are pressed, along with changing the bullet speed and the timer instance variable we use... but there is an easier way!

As mentioned briefly in a previous tutorial, all games run at a speed which is defined by the number of times the game loop runs in a second. Each loop is called a game frame and so we call the speed at which a game runs the game Frames Per Second (FPS). By default this is set to 30 by GameMaker Studio 2 for any new project, which is fine for puzzle games, or games that don't require fast response times or even for mobile games, but for arcade style games like ours a preferred value would be 60.

The game Frames per Second setting can be changed in the Game Options which you can open from the button  at the top of the IDE, or by going to the Resource Tree Main options item:

Options Window

As you can see from the image above, here you can change a few things that will affect how your project will run, including the game FPS value. So, set this to 60 now, then click on Apply and close the options then test the game again. You'll see that everything is much faster, and smoother when playing.

The Enemy Object

The workflow for creating the enemy object is the exact same as that which we used for the player and bullet objects, so we'll simply list the steps here, as you should be familiar with how it goes:

  • Create a new sprite resource
  • Call it "spr_enemy"
  • Import an image (remember, while following this tutorial the file explorer should take you to the TutorialResources folder automatically where you can find the sprites used in the "Images" sub-folder). If you have any issues, you can also find the images here.
  • Set the origin of the sprite to the middle (use the drop down menu)
  • Create a new object resource
  • Call it "obj_enemy"
  • Assign the enemy sprite we just added to the new object

If all has gone correctly, your workspace should look like this:

Workspace

We now add a Create Event for our enemy object. In this event, we will add the following:

Code: obj_enemy Create Event

hp = 5;

"hp" is an instance variable that we want all instances of this object to have, and it will store the "health points" for the enemy object. Everytime we shoot the enemy, we will deduct 1 from this value until it reaches 0 and is removed from the game. Keep in mind that instance variables need to be initialised before use, and so that is why we have them in the Create Event, as that event is run for every instance the moment it is created in a room and the event only runs once so the variable is only set once at the start.

Next we need this:

Code: obj_enemy Create Event

spd = 1.5;

This variable will be used to define the movement speed of the instance. Note that you should not use the variable "speed", as that is one of the built-in variables that all objects have and we don't want to use that for this. Why are we using the instance variable and not the built in one? Well, we don't have to, and we could just use the value 1.5 in all future code when we deal with the speed, but if we want to change that value, it would mean searching through all the code and manually fixing it. This can be very time consuming and error prone, so we use the instance variable instead to store the value. This means that should we want to change it, we only need to change it in this one event, and all the rest of the code will "just work".

We now need to add a Step Event to the enemy object. This event runs every game frame and in it we are going to check for a player instance in the room, and if one is found we'll move towards it. The code looks like this:

Code: obj_enemy Step Event

if (instance_exists(obj_player))
{
    move_towards_point(obj_player.x, obj_player.y, spd);
}


We check to see if there is an instance of the player object first because later we access certain player variables. This is a bit of forward thinking on our part, as we will eventually have the player "die", removing its instance from the room, and if we try to access the variables of an instance that doesn't exist, then the game will error and crash.

If there is an instance of the player, we then use the move_towards_point() function to tell the enemy to move at the speed of the "spd" variable. Notice how we tell the enemy where to move to... The method shown is the "point" method for accessing variables in another instance. Simply provide <object_index>.<variable> or <instance_id>.<variable> and you can get or set values from another instance, as long as it exists in the room (see comments above).

Finally we want to add this:

Code: obj_enemy Step Event

image_angle = direction;

You've seen this line before in the other two objects, so you should know what we're doing here... but it's worth noting that when we use the move_towards_point() function, we are setting the built in speed and direction variables, which is why this last line works to rotate the instance in the direction of movement.

You should open the Room Editor now on our game room and add a few instances of this enemy object into it (click  and drag from the resource tree into the room area), and then test the game:

Enemy Test

Collisions

At the moment, the enemies just follow the player around and don't actually do or react to anything else. We need to fix that, so to start we need to edit the Step Event again. At the moment it looks like this:

Current Code

We are going to expand on this to include a check to see if the "hp" variable we initialised previously is less than or equal to 0, and if it is we are going to destroy the instance (remove it from the game room). This is done using the following code, which you should add in after the current code:

Code: obj_enemy Step Event

if (hp) <= 0 instance_destroy();

The next thing to do is to make the "hp" variable actually go down, as currently that check will always return false since we only set the "hp" to 5 and nothing else.

Open up the object "obj_bullet" (if it's not open already). We need to add a Collision Event here to detect the collision between the bullet (the calling instance) and "obj_enemy" (the colliding instance):

Adding A Collision Event

Now, in this event we will need to affect the "hp" variable of the colliding instance, and we saw that we could do this using the "point" method previously. However that won't work in this case, as there are multiple enemy instances within the room and GameMaker Studio 2 doesn't know which one you actually want to affect. So we can't write:

Code: obj_bullet Collision Event with obj_enemy

obj_enemy.hp = obj_enemy.hp - 1;

That code will subtract 1 from ANY of the instances of "obj_enemy" in the room, and so is best used when we know that there is only one unique instance to be accessed. Instead we need to write the code like this:

Code: obj_bullet Collision Event with obj_enemy

with (other)
{
    hp = hp - 1;
}
instance_destroy();


The important part here is the very first line where we introduce a new function - with - and a new keyword - other. We use "with" to change the scope of the following code block, making it run from the instance or object that we indicate. If we did with (obj_enemy) {}, for example, the code that we place within the {} would run in all the instances of obj_enemy. However we only want the code to run in the other instance involved in the collision so we use the keyword "other". As you might have guessed, other is a special reserved keyword that GameMaker Studio 2 uses in the collision event to reference the unique instance ID value of the other instance in the collision, in this case one of the enemy instances.

After we take the point of off the "hp" variable, we then call a function to destroy the calling instance (the bullet). Test game now and see what happens...

Collision Masks

While you've been testing your game, you may have noticed that the bullets don't often actually appear to hit the enemies when they disappear... yet the hit is being registered, and the bullet is disappearing and the enemy "hp" is being affected. What's happening?

To answer that we need to back to the Sprite Editor and explain another of its features - the ability to set up a collision mask. So, open up the bullet sprite now and click the section labelled Collision Mask:

Collision Mask Interface

This section of the sprite editor permits you to define the area of the sprite that will be used to detect collisions, where a collision is defined as when two collision masks overlap at any point. By default, you are set to use a rectangular collision mask, and this collision mask will cover the whole area of the sprite. However if you look at the bullet sprite, you can see we have a lot of "blank space" which is being used for collision detection. This is something we need to change...

You can edit the collision mask by simply dragging the little box "handles" around in the sprite preview window:

Edit Bounding Box

And you can also set these values directly using the input boxes in the collision mask section. What we want is to have the collision mask cover only the "head" of the bullet so that the rest won't register, something like this:

Bullet Collision Mask

You now need to open the other two sprites (for the player and for the enemy) and edit their collision masks too, as in the image shown below:

Player And Enemy Collision Mask

Note that we have left the player collision mask a fair bit smaller than the sprite itself. In most arcade games, the collision mask is kept smaller than the sprite to give the player a bit more room for error, and in this game we will do the same.

Player Collisions

Before we end this tutorial, let's quickly add in a collision for the player colliding with the enemy. This won't be the final way we do this, but for testing and to give a feel for how the game will pan out, it is fine.

To start with open up the object "obj_player" and add a new collision event with the object "obj_enemy":

Collision With Enemy

In this event we need to add the following code:

Code: obj_player Collision Event with obj_enemy

game_restart();

All this code does is (as you might imagine) restart the game the moment an enemy instance "touches" the player instance, meaning that we now have to not just shoot the enemies, but dodge them too. This is a very basic mechanic and we'll refine it more in later tutorials, but for now it's enough for us to test and get a feel for how everything plays.

You should run the game again now, and you'll see that the experience is quite different to what it was before. The collisions are solid and look better, and there is a certain skill required by the player to avoid the enemies while shooting.

Summary

In this short section we have added yet another object to our game, the enemy, and got things feeling a bit more like how an arcade game should feel. The main points you should have learned from this are:

  • Where the Game Options are and how to change the Game FPS
  • How to access variables in other instances using the "point" method or using with()
  • How to edit the collision mask for a sprite and what effect it will have on the game

That might not seem like much, but the core concepts here are probably amongst the most important you can learn. Being able to access variables in other instances is incredibly useful and very powerful, and it's important to know how and when it can be done. The same for the collisions mask, as having the wrong collision mask in your game will seriously affect the gameplay and the fun of the player.

In the next section , we are going to look at making the visual aspect of the game nicer, in particular adding backgrounds to the arena, as well as making the arena bigger and having a "camera" follow the player around...


Tiles and Views

Tiles and Views

This is the Tiles and Views section of our "My First Game" tutorial, and if you've done the first three sections then you've already got the beginnings of a small arena shooter game. Your player can move and shoot, and you have enemies that attack the player. However, it's not very pretty, since everything happens on a plain black background. It's also not a very big play area for the player to move around in, making the game feel cramped. In this section we will change that by making it more attractive to the player using tiles and also expand the play area using cameras.

NOTE: If you close the accompanying video then you can get it back by clicking here

This section is a bit different to previous ones in that we won't be using any GML code. Everything we want to do can be achieved using only the Room Editor, so to get started double click  the room "room0" in the Resource Tree to open the Room Editor workspace.

The Room Editor Workspace

Tile Set Sprites

To add a background to our game we want to use a Tile Set resource. Tile Sets are all based on sprites, and although so far we have only used sprites for game entities like the player or the bullets, we can also use sprites for backgrounds and tile sets, which can also be animated and do other interesting things.

First thing to do is create a new Sprite resource (right click  on the Sprite resource folder and select Create). Then import a tile set image. We are going to use the following image:

Example Tile Set

You can get the above tile set from the TutorialResources folder that the tutorial made automatically, in the "Images" sub-folder, when you click the Import button (if you have any issues, you can also find the images here).

The tiles we are going to use for the game are 128x128 pixels each, all placed on the same image in a grid. Tile Sets are always comprised of a single sprite image, and must always be based on a grid, although the grid does not have to be square, just regular, ie: you can have 24x96 tiles, or 32x32, etc... as long as each part of the tile set image is on a grid it can be used in GameMaker Studio 2 as a tile set.

Note that the top left corner of the tile set sprite is empty. GameMaker Studio 2 will always use the first tile of a tile set as a "blank" tile, which is what is placed by default as an empty tile. When you add a tilemap layer in the room editor, which we'll cover in a moment, it will be empty and you won't see anything, but it's actualy filled with these "blank" tiles, and when you start to paint tiles onto the tilemap layer, if you delete a tile, it's not actually being removed, but instead it's being changed for this "empty" tile too. Note that even if the image you supply has non-transparent pixels in this first grid cell, they will not be drawn, so when making tile sets keep this in mind and just leave this first tile blank.

You can name this sprite now, something like "spr_background", and close the sprite editor before continuing (we don't need to set the origin of this sprite nor any of the collision properties as they are not relevant for tiles).

Making a Tile Set

We now need to make a new Tile Set resource, which is done the same way as for sprites and objects, etc... by right clicking  on the Tile Set resource folder and selecting Create. This will open the Tile Set Editor:

Tile Set Editor

The tile set window is comprised of two parts to start with: the main Tile Set Editor window which has the Tile Set Properties window chained to it. To start with lets name the tile set "tl_background" and assign it the sprite we just created. You can assign the sprite by either clicking the Select Sprite button in the Properties window, or the button marked No Sprite in the Editor window.

Assigning A Sprite To A Tile Set

When you import the image, you'll see that it is covered in a grid that is 16x16px per cell. This grid shows the way that the image will be split to create the final tile set cells, and if you have used the tutorial image you can probably tell that the current settings are way to small for the image we are using.

Tile Set Grid 16x16

In the above image you can see we have marked the first two sections of the tile set properties. These control the tile set cell width and height and setting these will dictate how the tile set image will be split up and how it will be displayed when painting it into a room tilemap layer. For this tile set you need to set each of these values to 128px.

The rest of the values can be left at their default settings, as they are used for creating "offsets" and "split distances" between individual tiles in a tile set image. Basically, some tile set images may be created with "empty" areas around each tile and so you can set the pixels or cells between each individual part of the image here. However our tile set has all the tiles packed right beside each other, so we don't need to worry about this.

Our tile set is now set up and ready to add to a room...

Tilemap Layers

We need to go back to the Room Editor workspace now, as we want to create a new layer called the Tilemap Layer. We currently have two instance layers (one for the player and the enemies, one for the bullets) and a background layer. We want to add a tilemap layer between the background layer and the bullet layer so click the Add Tilemap button  to add the layer first, then click  on it and drag it to position it between those two.

Add A New Tilemap

As with almost everything in GameMaker Studio 2, you can give this new layer a name like "TilesLayer" or something, and then you can go ahead and assign the tile set that we made previously to it. This is done by going to the Layer Properties window - which is opened by default on the left of the room editor when you select any layer - and then clicking the button that says "No Tile Set" which will open a window to let you select the tile set to use:

Choose A Tile Set

The tile set selected will now open on the right of the room editor workspace and you can click on any of the tiles to select it for "painting" into the room:

Painting Tiles

We now need to paint the tiles in a way that makes sense for the room and the tile set chosen, so select the appropriate tiles to make the final tilemap layer look like this:

Final Tilemap Layer

A few things to note here:

  • Tiles will always be added to a grid the same size as the tile set assigned to the tilemap layer
  • You can only place one tile per grid square
  • You can select the "empty" tile to paint with to removed previous tiles, but you can also use the right mouse button
  • You can move around the room using the middle mouse button  and moving the mouse

You can test the game now and it should play exactly the same as it did before, only now we have a nicer background for the action to happen on. In a later tutorial in this series we'll look at creating a "wall" around the arena so the player can't leave the room area, but for now we have what we wanted.

Room Size

The play area for the game is a bit small so the next thing we are going to do is make the roomsize a bit bigger. This is easily done by simply changing the width and height of the room from the Room Properties, which by default can be found at the bottom left of the room editor window:

Room Properties

You can see that the default width and height here are 1024x768, but that's too small so let's just double the width value to 2048 by clicking  on the input box and changing it. After you change it you will see that the room in the editor has now expanded to twice its width (you can use the zoom controls undefined to expand the viewable area if you can't see the changes, or alternatively hold down  /  and then use the mouse wheel  to zoom in/out).

Room Width

If you run the game at this point, you will find that you now get a massive game window that is way to big for most people to actually play in, so we need to sort that out using cameras. However before we get to that, you should fix the tilemap layer so that it covers the whole room:

Fixed Tilemap

Cameras and Viewpoints

To prevent the issue with the huge window we need to tell GameMaker Studio 2 to only show a portion of the game room using a camera view. We can set this up from the room editor too, from the section titled Views in the Room Properties. Clicking on that section will expand the different properties:

View Properties

The first thing to do here is to check the box beside Enable Views. No matter how you set things up, if you don't enable views then nothing will change, so this is very important! Next you need to expand the section on View 0. You can have multiple camera views in a room, and they can all be enabled and displayed at different positions (permitting, for example, a two player split screen game, with a camera view for each player), however for our game we only need one, and that's the camera View 0.

View 0 Expanded

There are a lot of options here, but the first thing we need to do is switch the view "on". This is done by checking the box labelled View Visible. Even if you have enabled views, if you don't have one that is set to be visible you won't get the desired result. Once it's been checked, you'll see a rectangle appear in the actual room preview to show you where the new view camera will be placed:

View 0 Enabled

This view camera "rectangle" is defined from the view properties, with the xpos and ypos values being the top left corner of the view, and then the width and height defining the size from that point. You can also set the the camera view port, which is what defines the area of the screen that will be used to display the camera view. Basically, you can set a view camera to be any size, and then set the view port to be a different size and the camera view will be scaled to fit in that space. So you can have a camera view that is 480x320 (for example) but make the camera port 960x640 and the camera view will be scaled up to fit. However for our game we are keeping things simple and all those values can be left at their default settings.

The final thing we need to do is set the Object Following. By default a view camera is static, ie: it won't move unless you code it to, but we can set it to follow an instance of an object automatically and without code using this button:

Object Following

Once you've set it to follow the player object, we also need to set the Horizontal Border and Vertical Border values. These dictate how close to the edge of the view the instance needs to be before the view camera will move to follow it. At the moment it's 32px, which means that the player would have to move right up to the edge of the view camera window before it will move to follow, which in an arena shooter like ours is just too small... we want it to move before that so that the player can see what is around them. So, set those values to 256px each:

Object Following Border Values

Notice we leave the two "speed" values at -1... If we set this then we are telling the view camera to only move by a specific amount each game frame, so that if we set it to (for example) 5, then the view camera will follow the player only 5px per game frame. This can be really useful and can give a nice "catchup" effect if the instance being followed moves faster than the values given, but for our game we want the camera to always stay centered on the player, so we leave it at -1, which is essentially telling the camera view to move "instantly" to the instance being followed position every game frame.

If you run the game now you can see how this all works to make the camera follow the player while they explore a larger room and shoot the enemies...

Summary

Getting to grips with the room editor is essential if you want to get the most out of GameMaker Studio 2, and there are many, many features to it. In this section we've covered two of the main ones, the layer system - and in particular tilemap layers - and the camera views system. Let's quickly cover the main points you should have picked up while going through this:

  • How to set up a tile set resource
  • How to create and order layers in the room editor
  • How to paint tiles into a tilemap layer
  • How to enable and set up view cameras
  • How to make a view camera follow an object instance

Our arena shooter game is starting to look pretty good now, and we have all the essentials in there. However it's missing one or two things to take it from a prototype to a finished game, one of which is a scoring system which is what we'll cover in the next section of this tutorial...


Scoring

Score Controller

You've made it to the Scoring section of the "My First Game" tutorial and in this section we are going to look at an important aspect of any game... keeping score. We're going to create a score for the player to use as a measure of how well they are doing in the game, and not only will we be keeping score, but we'll be using the different functions for drawing text to show it to the player as well. So, the player shoots an enemy and "kills" it and the game will award them a certain number of "points" which we'll add to their score. This score will then be displayed prominently at the top of the screen...

NOTE: If you close the accompanying video then you can get it back by clicking here

To keep things clear and easy to manage in our arena shooter project, we need to make a new object to act as out main "controller" object for the score. You should be familiar with how to do this so go ahead and make one now and call it "obj_score":

  • Click the right mouse  on the Object resource folder
  • Select "Create" to create a new object
  • Name the object "obj_score"

The object "obj_score" will be our controller object and so we don't assign a sprite to it since we want it to draw other things (like the score text). Not assigning a sprite to an object means that when we run our game with an instance of that object in a room it will not be drawn, but that doesn't mean that it isn't doing anything. In this case our controller object won't be drawing a sprite but it will (by the end of this tutorial) draw some text and manage the player score value and certain game events.

Now we need to add an instance variable to the object in the Create Event which will be used to hold our score value. So, add a Create Event now, and in the code window that pops up type:

Code: obj_score Create Event

thescore = 0;

It is worth noting that GameMaker Studio 2 has a built in global scope variable "score" which can be used to keep track of the player's points. Now, because the score variable is global in scope it means that you can only ever have one score value for the entire game, which can be limiting and it's generally better to have unique variables for score in each instance (for example, if we wanted to make a two player game then we couldn't use score and would have to make instance variables for each one, the same as we have here).

With that done we need go ahead and add a Draw Event.

The Draw Event

We are now going to add a Draw Event to our object. Click  the Add Event button now and select the Draw Event category:

The Draw Events

As you can see, there are multiple draw events to choose from, but for this we simply need the general draw event, which is the one at the top of the category list (highlighted in the image above). The general draw event is the one that GameMaker Studio 2 uses when it default draws your instance sprite. What does this mean? Well, so far we haven't added any Draw Event to any of our instances, and yet they all draw their sprites to the screen when we run the project. This is because when you don't add a general Draw Event to an object yourself, GameMaker Studio 2 will automatically assume you want to draw the sprite assigned to the instance along with any transforms (like colour or alpha or scale) that you have added. Essentially, the general Draw Event defaults to using this line of code:

Code: General Draw Event

draw_self();

That function will also draw the assigned sprite along with any applied transforms, just like the default drawing for an instance when it has no Draw Event. Now, when you add a general Draw Event to an object and in it add some code, you are telling GameMaker Studio 2 that you want to handle what is being drawn and GameMaker Studio 2 will no longer draw anything except what you have put in the event, so it won't draw the assigned sprite unless you tell it to (and you can draw any sprite, it doesn't have to be the assigned one).

The general Draw Event (and all other draw events) will be performed once for every game frame, much like the Step Event will, and note that you can have unrelated code or functions in the draw event as well as draw functions (but it is recommended that you keep this to a minimum and use the Step Events instead where possible). However, all drawing must be done in a draw event, and placing draw functions in any other event will not work.

Just before we continue, it's worth noting that the Draw Events other than the general drawing, will not affect the instance default drawing, so that you can, for example, have no general Draw Event but have a Draw End Event and the instance will still default draw the sprite and whatever you have added into the Draw End Event. If you want to know more about the different events in the Draw Event category, you can press  to open the manual and read up on them in the section on the Object Editor.

Local Variables

In our draw event we want to draw the score text on the screen at the same position at all times. This means that we can't use an absolute room position since as the camera moves about, the text will be lost off of one side or the other of the camera view. So what we need to do is draw the score text relative to the view camera. To make this easier we are going to use a couple of local (temporary) variables. We already know about global scope variables (they belong to the game, not any object in particular and can be read/set by all at any time), and instance scope variables (which are unique to every object instance, but can be accessed by other objects using the "point" method or "with", as discussed in the Enemies Section)... but there is a third class of variables called local or temporary variables which we are going to use now.

A local variable is simply defined as one that is "local" to the script or event that is using it. This means that it is created when you use the keyword var and then discarded again at the end of the event or script that created it. This is useful for many, many things, not least of which is storing one-off values from calculations and saving memory resources. In this case we want to use local variables to store function return values, so add the following to our Draw Event:

Code: obj_score General Draw Event

var cx = camera_get_view_x(view_camera[0]);
var cy = camera_get_view_y(view_camera[0]);
var cw = camera_get_view_width(view_camera[0]);


The code above simply gets the (x/y) position of the camera that is assigned to the view port[0] as well as its width using the global scope array view_camera. You can have multiple view ports active in a room (up to 8, starting at 0, so it's view_camera[0 ... 7]) and can assign a camera to each one, so referencing (for example) view_camera[3] will mean you are dealing with the camera assigned to view port[3]. In this case we use view port[0] since that is the one we set up in the room editor in the last section.

Now we have those values, actually drawing the text requires a single line of code:

Code: obj_score General Draw Event

draw_text(cx + (cw / 2), cy + 25, string(thescore));

As you type this code in, note that the bottom of the Code Editor will display "argument help" for the function, showing what value is expected for the function to work. In this case it's an (x/y) position and a string, and since the variable we want to draw holds a number rather than a string we have to "wrap" it in the string() function (which takes any number value and converts it into a string, so something like 3.1456 will become "3.1456"). We could just as easily have written a string in there too.

You could drop this instance into the game room, but all you'd see would be a "0" on the screen since we don't actually add to the score value yet, so let's do that now.

Setting Score

To get our score to go up as we play, we need to add some extra code into the Step Event of the object obj_enemy, so open that now if it is not open already. We currently have the following code in that event:

The Current Enemy Step Event

We need the enemy to add a value to the score object variable "thescore" and so for that we will use the "with" command. We covered "with" in the Enemies Section where we used the "other" keyword to refer to the other instance in the collision. However "other" in that context only works for the collision event, and we are going to use "with" in the Step event, so we need to give it an object index or an instance ID to work with. Change the code now so it reads:

Code: obj_score Step Event

if (hp) <= 0
{
    with(obj_score) thescore = thescore + 5;
    instance_destroy();
}


We supply the "with" command with the object index (the name in the resource tree) of the object we want it to target, in this case obj_score, so all code afterwords will be run as if it belonged to that object. Note that when using an object index like this, if there are more than one instance of the object in the room when the code is run it will run for all instances of the object. Using obj_score in this case is fine because we are only going to have one of them in the room, but you can use "with" to target a single instance if you target an instance ID (which you can get from the Room Editor or by certain functions).

Our code will now add 5 onto the score variable every time an enemy instance is destroyed, and we can now add the score object into our game room. To do that open the room resource (double click  on it in the resource tree), and then drag an instance of the object obj_score into the room and place it anywhere. Since it doesn't have a sprite it will be shown with a question mark icon .

Score Object With ? Icon

You can play the game now and you will see the score displayed at the top, and if you shoot and destroy the enemy it will go up by five. You might have to squint a bit though, as it's written rather small and isn't very easy to read, so lets fix that now.

Styling Text

We need to style the text on the screen to make it more readable and nicer to look at and for that we'll start by adding a new Font Resource. If you right click  on the Font resource and select Create it will create a new font resource and open the Font Editor:

The Font Editor

Following the naming convention <prefix>_name we'll call this new font fnt_score. You can then go ahead and select a font from your machine to use as the game font and then in the editor set the style to bold to make it stand out that much more (if the font you select permits it), and set the size to 24 to make it bigger. Note that in the Font Editor you have a preview window that by default shows "Hello World". You can actually delete this and type in any text to get a preview of how it will look in the game. The other options in the Font Editor are outside of the scope of this tutorial, but you can read up on them by pressing  to open the manual and going to the section on the Font Editor.

The Font Editor Settings

You can close the font editor now, as the next bit of styling we need to do is done through code. At the moment the text is being drawn black which helps make it harder to see for the player, so we are going to fix that. Go back to the controller object obj_score and make sure you have the general Draw Event code open as we need to edit it.

The first thing to do is tell GameMaker Studio 2 that we want to draw with new font resource, so before the draw_text function we need to add:

Code: obj_score General Draw Event

draw_set_font(fnt_score);

As the function name implies, this will set the font for drawing any text. Note that this will set it for the whole game and all subsequent text will be drawn using this font even if you don't use this function anywhere else, so if you want to use various fonts in a game you must call this function before every item that needs drawing, but if your game only needs one font then you can call this function in a controller at the start of the game once only.

We want to set the colour that the font is drawn in, so now add this line of code:

Code: obj_score General Draw Event

draw_set_colour(c_white);

We use c_white to set the colour to white and this is a GameMaker Studio 2 constant. Constants are special in that they never change throughout the game (hence they are not variables) and GameMaker Studio 2 has a number of them built into the language, like c_white, c_black or c_red for colours or pi or true for values. You can also make colours using different functions that take the RGB or HSV values, and you can even use hex notation like $983c95, where 98 is the blue component, 3c is the green component and 95 is the red component. Again, like the draw_set_font() function, this will set all further drawing to use the given colour (and not just for text either, this will affect shapes that are drawn and few other things), and so can be called once at game start if the colour isn't going to change, or once everytime that you want to draw with another colour.

The final code for the general Draw Event should now look like this:

Final Draw Event Code

You can now run the game again and you should see that the score is much more visible, maintains it's position and goes up when an enemy is destroyed:

Final Game Screenshot

Summary

In this section we spent some time showing how to add text your game and increment the score, showing the player how they are doing as they they play. The main points you should have picked up while working through this section are as follows:

  • How to use the Draw Event
  • How to use (and understand) local variables
  • How to use with to change instance variables in other instances and objects
  • How to create a font resource and use it
  • How to set a font and a colour for drawing

That might not seem like much, but with this knowledge you can start to set up more complex displays for your player to include any number of written details, and format it to follow the view camera at the position you want. It's worth taking a moment to look at all the different draw functions for text that are available to you (press  to open the manual) as they permit you to scale the text, or limit it to a specific string width (number of characters) and many other things. In fact, you can save this project file and then save it again under a different name and experiment a bit with the different functions to see what they do (you can load the original saved tutorial later to continue).


Sound

Sounds Files

This section we are going to dedicate to adding some sound to our game. Basic sound is incredibly easy to do and so this section will be a short one, but what you'll learn can be used anywhere in your own projects. At the moment the game is looking good, but it has no sound effects, and sound is an essential part of any gaming experience so let's go ahead and rectify that...

NOTE: If you close the accompanying video then you can get it back by clicking here

In this tutorial we will simply be adding a single sound effect when you destroy the enemy instances, so you can use any sound effect that you have on your hard-drive or you can use the one that we have supplied along with the tutorial, which will be found when you open the file explorer in the TutorialResources folder, in the "Sound" sub-folder (if you have any issues, you can also find the sound here). If you make your own sound effect, you should export it as a *.wav, an *.ogg or as an *mp3 format sound. However note that in general you want all sound effects to be *.wav format and all music to be *.mp3, and *.ogg can be either, but is generally preferred for longer sound effects and music too.

The Sound Editor

Creating a sound resource is exactly the same as creating any other resource. Simply go to the Resource Tree and right click  on the Sound resource and click Create. This will open up the Sound Editor:

The Sound Editor

First thing to do is the name this new resource, and since it's for when the enemy dies we'll call it snd_death, so name it that now. You can then click on the Import button  to bring up a file selector and look for an appropriate sound which as mentioned at the start should be a *.wav format file (remember, while following this tutorial the file explorer should take you to the TutorialResources folder automatically where you can find the sounds used in the "Sound" sub-folder). If you have any issues, you can also find the sound here.

Once you have named the resource and added the file, you can use the audio preview buttons in the editor to listen to the sound and set its volume:

Playback Controls

The rest of the options here we can leave at their default values, but if you want to find out more about them then hit  to open the manual and go to the section on the Sound Editor. You can close the Sound Editor now as we are finished with the resource and are ready to add it to our game.

Playing A Sound

Playing a sound is really simple and requires a single line of code. We are going to place this line into the enemy object obj_enemy, specifically in the Step Event, just before we destroy the instance. So, open this object now for editing and go to the Step event and add this line just before the instance_destroy() function:

Code: obj_enemy Step Event

audio_play_sound(snd_death, 1, false);

This line plays the given sound with a given priority. For small projects you don't need to worry too much about priority, but for larger projects it is important. When too many sounds are playing at once for the hardware to handle, this value will be used to decide which sounds are stopped to let the others be heard. So, sounds with a higher priority will have more chance of being kept playing while lower priority sounds will be stopped. Music for example would generally have a high priority so that it is never stopped, while small effects like shooting noises can have a lower priority. Since we aren't concerned with this right now we can leave it at zero. Note that we set the last argument to false too. This is the "loop" argument and if you set it to to true then the sound wll loop continuously until the game ends or we call a function to stop it. Generally this is what you want for music or ambient effects like a wind or waterfall sample.

The code block should now look like this:

Play A Sound On Destroy

You can test the game now and kill a few enemies to hear the sound play.

Pitch

When you play the game you should here the sound being played every time the enemy is destroyed, however the sound will quickly become tiring to the ear of those that play the game. We need to "spice it up" a bit and one of the easiest way to do this is to simply change the pitch of the sound.

So, still in the obj_enemy Step Event, and before the function for playing the sound, we need to add the following function:

Code: obj_enemy Step Event

audio_sound_pitch(snd_death, random_range(0.8, 1.2));

When you use a sound effect or music in GameMaker Studio 2 it is played with a pitch value of 1. this is an arbitrary number that simply tells GameMaker Studio 2 to play the sound "as is". If you lower the value of the pitch to say 0.7, then the sound will play at a lower pitch and a value higher than 1 like 1.5 will play it higher. In the function above we have used the random_range function to give us a random value between 0.8 and 1.2 for the sound effect, and so every time it is played it will sound slightly different. This is a great method to add more life to your games and can be applied to almost any repetitive sound effect to make it more interesting and realistic.

The code in the Step Event should now look like this:

Change Sound Pitch

Note that if you set the pitch anywhere in code then the referenced sound will always play at that pitch unless set again, which is why we call this function not once at the start of the game, but every time the sound is going to played.

Summary

In this short section we have added a simple sound effect to the game and made it a bit more interesting by changing its pitch every time it plays. The main points you should have picked up while working through this section are as follows:

  • How to add a new Sound Resource
  • How to play a sound
  • How to change a sounds pitch

It's not much for such an important aspect of any game, but surprisingly those two functions are about all you'll need for adding sound effects into your project. If you want to, you can experiment some more now and add some extra sounds into the game for when the player shoots and moves or when he dies. Just remember that before you start experimenting, save this project file and then save it again under a different name and experiment a bit with the renamed file before loading the original to continue with the tutorial.


Title Screen

Title Page Sprites

When you start a game, you aren't normally just dropped straight into the action, but instead you are usually presented with some kind of title screen. In this section , we are going to show you how to set up a basic title screen for your game using a new room and with some extra graphics.

NOTE: If you close the accompanying video then you can get it back by clicking here

To start with we'll need a couple of sprites. One for the background, and one to show the game title, so make a new sprite now and import the "bg_tile_dark.png" image from the TutorialResources folder (if you have any issues, you can also find the images here). As usual, name your sprite something appropriate like "spr_darktile" and then close the sprite editor.

Dark Background Tile Sprite

We have no need to change any of the properties for this sprite so you can close it now and then create another one and call it "spr_titlescreen". This sprite will hold the title screen text graphic, so open the tutorial assets folder and load the sprite "titlescreen.png".

Title Screen Text Sprite

With this sprite we need to change the origin. We've covered this previously, but just to remind you, simply click  on the Origin drop down menu and select Middle Center to position the origin in the exact center of the sprite

Title Screen Text Sprite Origin

You can close this sprite now and we'll move on to creating the title screen room.

Title Room

At the moment, our game just starts in the main game room, but we want it to show a title screen and have the player do something like press Enter or click anywhere to actually start the game. To achieve this we are going to use another Room Resource, so right click  on the Room resource folder and select Create Room to make one. The new room will be added after the current one:

New Room Resource

We'll call this new room "rm_titles". There is a minor problem that we have to fix now, though... GameMaker Studio 2 will run rooms in consecutive order, meaning the room at the start of the room resource list is the one that will be shown initially when we start the game. This means that we now have to re-order the rooms so that the title room is placed before the game room.

Reorder Rooms

Notice that when you reorder the rooms there are actually two different places where you can drop the room when you click  and drag. The first is dropping it on another rom (the room being dropped on is highlighted), which means that you want the selected room to become a "child" of the other room, and the other is dropping it over a room (where a bar is highlighted above the room), which will place it in the room order. Making child rooms is an advanced and powerful feature, but outside the scope of this tutorial, so just drag it into the position above the room "rm_game", as shown in the animation above for now.

Title Room Properties

By default the room will have been created with a Background layer, so select that now. This layer will be used to show the background tile sprite that we added previously. This is done from the background Layer Properties window:Background Layer Properties

The background layer is a layer that can be cleared to a single colour and/or have a single image applied to it. In this case we want to assign the dark tile sprite we added, so click on the section marked <no sprite> and select the sprite spr_darktile. You'll see that the image now appears in the top left corner of our room as a background, but that's not exactly what we want... we want it to tile across the screen. We could have created a tilemap layer for this and added the background as single tile, but with the background layer it's a lot simpler: just tick the Vertical tile and Horizontal Tile check-boxes:

Tile Background

Before continuing let's just look at a couple of the other options for backgrounds. You'll see that we have the option to Stretch the background image too. All this will do is stretch the background image used to fill the whole room area, which with a small image like the one we are using doesn't look right, which is why we tile it instead. You can also set the Horizontal Speed and Vertical Speed for the background layer. This will scroll the layer by the number of pixels given every game frame, so setting a horizontal speed of, for example, -2 will scroll the background from right to left at a rate of 2 pixels every game frame.

If you set this in the room editor you won't see anything happen, but in your game it will scroll. However sometimes you want to preview a change like this without having to compile the whole project to see how it looks, which is where the Animate feature of the room editor comes in very handy. In the main editor window, you have a bunch of tools in the top right corner:

Room Editor Tools

The one we are interested in here is the Play Animation button . Clicking this will animate the whole room such that any backgrounds that are set to move, or tile sets that are set to animate, or any sprite assets that have sub-images will be shown as they would appear in the game:

Animate Background

With the horizontal background speed now set to -2, this will add interest to our title screen, we can continue on to make our title object.

Adding The Title

To move from one room - the title room - to the next one - the game room - we will need a new object, so go ahead and make one now (Right click on the object resource folder and select Create). Name this object "obj_title" and assign it the title sprite we created at the start of this section. Now add a Create Event:

The Title Object

We are going to have our object draw its sprite fading in gradually, just to create a slightly nicer looking title screen. To so this we need to use the built in variable image_alpha. All instances of objects have a number of built in variables that are related to how the assigned sprite will be drawn. In the Scoring Section we explained how GameMaker Studio 2 default draws the sprite assigned to an instance of an object, and most of the objects in this game have no Draw Event. However letting GameMaker Studio 2 default draw the sprite does not mean you cannot change how it is drawn, as you can using the different built in variables. You can change things like the scale, the rotation, the colour and the alpha, all through using the built in variables. In this case we are going to change the alpha to make the sprite "invisible" at the start of the room. For this we add the following code into the Create Event:

Code: obj_title Create Event

image_alpha = 0;

We also want to make sure that the sprite will be drawn in the exact centre of the screen, so we also add the next two lines:

Code: obj_title Create Event

x = room_width / 2;
y = room_height / 2;


Next we need to add a Step Event with the following:

Code: obj_title Step Event

image_alpha = min(image_alpha + 0.02, 1);

All this code is going to do is get the minimum value between the current image_alpha + 0.02 and 1, and then assign that to the image_alpha variable itself again. In this way we can ensure that the image alpha will increment until it reaches 1 and go no higher (this is important, as while you can set the image alpha value to more than 1 - or even negative numbers - this will have different effects on different platforms and the recommended value for this variable is always between 0 and 1).

We can now drop this object into the title room and test the game (open the room editor, click  on the object in the Resource Tree, then drag an instance of it into the room editor and release the mouse). You don't even have to worry about placing it properly because we've taken care of that in the Create Event of the object.

Room Introduction

Changing Rooms

The title text sprite says "Press Enter To Start", so we now need to have our instance detect that key and go to the next room to start the game. For that we need to add a Key Up Event

Key Up Event

The Key Up Event detects when a key has been released and won't trigger until that happens. We use this event just to give the user a moment to get ready to play, as the other two key events would change the room instantly. In this event we want to add a single line of code:

Code: obj_title Key Up Event

room_goto_next();

There are a number of functions available for moving between rooms, but we only need this one since it simply goes to the next room in the Resource Tree, which is our game room. Care must be taken when using this function, though, as if there is no room after the current one (ie: the room using this code is the last one in the Resource Tree) we will get an error which will cause the game to close. Note that if we want to go to a specific room without following the order of the resource tree, then we would use:

Code: obj_title Key Up Event

room_goto(rm_game);

The room_goto() function lets us go to any room in the resource tree. You can press  to open the manual and see what all the different room functions are. If you run the game now, you'll get the title screen and pressing Enter will take you to the main game room to play. Note that because we already have the function game_restart() in the player object when they die, then when the player dies the title screen will be shown again, so the player can prepare before each game after dying.

Summary

After completing this section we now have a nicer introduction to the game. While setting this up you should have learned the following:

  • How to add and re-order rooms in the resource tree
  • How to use image_alpha and other built in variables
  • How to use the keyboard events
  • How to change rooms using functions

With that we are almost finished with this tutorial, but we have one final task before us. Currently the game has a limited number of enemies to destroy, which limits the gameplay greatly, so in the next section we'll look at "spawning" more enemies as you play...


Spawners

Game Room Setup

Our project is shaping up quite nicely, but we have one final thing to fix before we can really call it a game... Currently, if you destroy the enemies without getting hit, then the game doesn't do anything and the user has to close the game window. That's not very satisfactory for the player! So, in this section we are going to talk about "spawners", which will be controller objects designed to constantly spawn enemies.

NOTE: If you close the accompanying video then you can get it back by clicking here

Before continuing to add these spawner objects, we first of all need to remove the enemies that are currently in the room and prepare a new instance layer, so open the room "rm_game" now. In this room, we are going to create a new layer specifically for our enemy instances, so create a new layer now (click  on the New layer icon ) and name this layer EnemyLayer. Click  on the layer and drag it into the position between the Instances and BulletsLayer.

Add Enemy Layer

You can now select the Instances layer and then in the actual room editor workspace, use  /  +  to select all the enemy instances one at a time. Using  /  like this will group all the instances together so that you can do things with them like move or, as is the case here, delete them.

Select All Enemy Instances

With them all selected simply press the  key to delete them. Now we can go ahead and create our spawner object.

The Enemy Spawner

We need to create a new object for the spawner so do that now (click  on the Object resource folder and select Create), and name this object obj_enemyspawn. Assign the object the enemy sprite too (spr_enemy), as what we are going to do is make our enemies "grow" in size and then spawn, so that the player has a visual cue that the spawner is there and that an enemy is coming and they can move out of the way and prepare. Having an enemy just appear out of nowhere would be unfair and annoying to the player!

We now need to add the following code into the new objects Create Event, so add that event now and then this:

Code: obj_enemyspawn Create Event

image_xscale = 0.1;
image_yscale = 0.1;


What we are doing is setting the built in variables for the assigned sprite scale to 10% of the base scale. GameMaker Studio 2 bases all the scale on factors, where a factor of 1 is 1:1 with the original sprite image, so a factor of 1.5 is 150% bigger and a factor of 0.5 would be 50% smaller. So, here we are setting the scaling factor to be 10% and then we are going to scale the image up to 100% before spawning the enemy.

To actually scale it up we need to use the Step Event, so add that now. We will use the same approach to scaling that we used previously for the image_alpha, so add this:

Code: obj_enemyspawn Step Event

image_xscale = min(image_xscale + 0.02, 1);
image_yscale = image_xscale;


This will scale up the image xscale until it reaches 1 at which case it will stay at 1. We don't bother writing the same line of code for the yscale since we want it to scale equally on both axis, which is why we simply set the yscale to be the same as the xscale.

We need to add another line of code after the scaling in the Step event:

Code: obj_enemyspawn Step Event

if image_xscale == 1 instance_change(obj_enemy, true);

First we check to see if the scale is 1 (100%) and if it is we change the instance into a version of the instance obj_enemy using the function instance_change. Note that we set the second argument in this function to true. The second argument is for making the instances being swapped perform their Destroy and Create events. There are times when you want to change instances but don't want to run these events in which case you'd set the argument to false. However here we do want the enemy object to run its Create event so we leave it as true.

With that done, you can go back to the game room and on the new EnemyLayer layer add five or six instances of the object (click  on the obj_enemyspawn object in the resource tree and then drag it into the editor window and release the mouse button). When ready, click the Play button  to test your game and you should see that the enemies now scale into existence.

Enemies Scale Into Existence

The Spawn Controller

We have our enemy spawn object now, but we don't have anything to re-spawn the enemies when there are none left, so for that we are going to create a controller object called obj_spawner. Go ahead and create a new object now and give it that name. You don't need to assign a sprite to it, but you will need to open up the Create Event. We are going to use alarms in this object to create instances of our enemy spawn object within the room, so to start with add the following code:

Code: obj_spawner Create Event

spawnrate = 60;
alarm[0] = spawnrate;


The first line sets a variable to control the speed at which enemies will spawn. The next line sets the alarm [0] to the value of spawnrate. But what are alarms? They are special events that will count down every game frame until they reach 0, at which time they will perform the code added into the event in the object. So, in our code we are setting the alarm[0] to 60, meaning that it will subtract 1 from 60 every game frame until it reaches 0 at which point any code in the alarm[0] event will run. You have 12 alarm events and they are accessed using the built in variable array alarm[0 .. 11].

NOTE: An alarm does not stop on 0, but will actually count down to -1. So the event runs when the alarm reaches 0 and then subtracts one more from the alarm to get to -1. This is useful to know as it means you check to see if an alarm has run or not by checking to see if it is less than 0.

You need to add the corresponding alarm event now, so since we are using alarm[0] you need to add Alarm Event 0:

Alarm Event 0

This event will only trigger when the alarm[0] array has counted down to 0 and in it we want to add the following:

Code: obj_spawner Alarm 0 Event

instance_create_layer(random(room_width), random(room_height), "EnemyLayer", obj_enemyspawn);
alarm[0] = spawnrate;


Here we are creating an enemy spawn object at a random position within the room and on the layer we created at the start of this section. We then reset the alarm to the spawnrate value so that it will count down again and spawn another one. In this way, every 60 frames a new enemy will be created for the player to shoot at.

You can close this object now and go back to the game room. We had added in enemy spawn objects but we don't need them anymore so select them and delete them ( /  +  to select, and  to delete). Now drag an instance of the controller object obj_spawner into the room and run the game.

Enemies Spawning In Game

Summary

After completing this section we now have a pretty much complete, albeit basic, game. Adding the spawner objects has made the gameplay more dynamic and enables the user to play again and again and try to beat their own highscores. Let's quickly run through what you should have learned from this section :

  • How to select multiple assets placed in a room
  • How to use image_xscale and image_yscale to change the scale of a sprite
  • How to use instance_change to switch objects
  • How to use an alarm in a controller to create timed events

With that, this tutorial is pretty much finished. We have covered all the basics that you need to know to get yourself started making games, and we've given you a base game to work on and try to change or add to. Things to consider adding to this project would be:

  • A game over screen
  • A high score feature where the highest score ever is saved and shown to the user
  • Enemies that shoot at you
  • Background music and better/more sound effects

The list could go on and on, but it's entirely up to you what you do from now on. GameMaker Studio 2 has given you the tools, so use them to the make the games that you have always wanted to make.