Hi there! Welcome to the Hero’s Trail sandbox project. This
is an overview of the GML Visual template available within GameMaker. We’ll go through the structure of the project and see how
its various features have been implemented.
This page is divided into the following sections:
The project consists of three main parts:
There are many more features that are implemented in the Hero's Trail complete game tutorial.
The player can move in all 4 directions and has animations for each direction. The enemies follow a path that is pre-defined in the room. These are the moving parts of the game, which are fully expanded upon in the tutorials linked above, however the biggest part of this project is its environment.
All of the above parts are explained in the sections below, and after you’ve read all of it, you will have enough knowledge to modify and to expand upon this base project.
The project has three rooms:
rm_menu is the first room (as ordered in the Room Manager) and serves as the game’s title screen. Pressing “Play” on this screen takes you to the first level, rm_level_1.
There is another room called rm_level_2, which is your canvas to create a level your way. The base project does not let the player continue into the second level, as that is implemented in Part 2 of the Hero’s Trail tutorials.
The rm_level_1 room has a lot of layers, which we’ll go through later in this tutorial. Let’s first take a look at its Room Properties, specifically the “Viewports and Cameras” section.
By default, GameMaker shows you the whole room when you run your game. However, for games like Hero’s Trail, you only want to see the area surrounding the player. This is achieved by creating a camera with a specific resolution, which follows the player and displays its surrounding area.
We have checked the “Enable Viewports” option and set Viewport 0 to be visible. This is required for our camera to function, which is set up in the rest of the options below.
The main properties we need to focus on here are the Width and Height under “Camera Properties” and “Viewport Properties”. The former is the size of the camera inside the room, and the latter is the size of the game window (also known as the viewport).
In this room, both of these are set to 1280x720. You can make the camera size smaller than the viewport size, in which case the game will be upscaled to fit the window.
This section lets you set up the camera so it follows a particular object. We’ve assigned obj_player so it follows the player.
“Horizontal Border” and “Vertical Border” are the minimum margins between the camera’s borders and the player before the camera starts moving. We’ve set this to be half the camera’s resolution so that the player always stays in the centre.
For more information on the camera settings, read the manual page on Room Properties.
The Hero’s Trail project uses various kinds of layers to achieve its polished look. Let’s have a look at all of the layers in rm_level_1:
The “Instances” layer, highlighted in the above image, is the main layer that contains our player and enemy instances. All other layers are placed either above or below this layer, depending on where they should appear. There are two main layer groups that appear on either side of the Instances layer:
These are visual layers that need to appear below the player.
These are visual layers that need to appear above the player.
These include floor, water and shadow tiles, and the walls that
need to appear behind the player
These include the wall tiles that need to appear on top and hide
any instances behind it
Important: If you edit the top tiles layer, you must also edit the wall instances in the Wall_Collisions layer so the collisions are updated.
The depth of the first layer in this group is set to 100 to ensure that all layers that follow are drawn at a high depth (appearing below other layers)
The depth of the last layer in this group is set to -12000 to ensure that all layers above it are drawn at a low depth (appearing above other layers)
For learning about Layers and their depth values, please read the manual page on Layer Properties.
Layer / Group
This group contains all Path Layers used by the enemy instances.
This layer contains all wall instances that are used to stop the player from moving into wall tiles. Note that its visibility is turned off by default.
Important: Whenever you make any changes to the wall tiles, you must edit the instances in this layer (or place new ones) so the collisions are updated too.
Visuals_Above_Instances -> Lighting
This layer contains light instances that are placed on torch tiles. This is used to achieve the glow that appears on all torches.
This layer contains sound instances that are placed on torch and fountain tiles. These instances handle playing the sound stored in its sound_to_loop variable to achieve torch and fountain ambience.
Let’s take a look at the Tile Set assets that we use to build our levels:
This Tile Set contains static tiles that are used to build the floor and bridges. It also contains shadow tiles that are used in a separate layer (“Tiles_Shadows”) to place shadows over water tiles.
This Tile Set contains tiles used for the walls. A majority of this Tile Set contains tiles used for Auto-Tiling, which you can access from the “Auto Tiling” menu.
This Tile Set contains animated tiles, which are placed in the Visuals_Below_Instances group. This set comprises the water tile, torch tile, fountain, flag, etc. The animations are created from the various frames present within the Tile Set.
You can learn more about tiles on this page: A Practical Guide to Using Tiles
The player object (obj_player) has a move_speed variable that stores the amount of pixels it moves every frame. This value is used in the Keyboard events to move it on the X or Y axis.
Within the Keyboard events, the “Set Instance Variable” action is used with the Relative option to modify the instance’s position based on the key that is held. For example, holding the Left key will add -movespeed to the X coordinate, moving it left.
For animating the player’s movement, we make use of three separate sprites:
Any one of these sprites are assigned to the player depending on which key is held. In case of horizontal movement, the “side” sprite is used, and the horizontal scale of the instance is simply changed to make it turn left (-1) or right (1).
Collisions can be complicated to implement, so this project contains a simple but effective collision solution using GML Visual. This is handled in a collision event with obj_collision_parent (which is the parent of all objects that are used to stop instances from moving):
This event runs once the player and a collision object (e.g. a wall) have come into contact. At this point the player should stop, however this results in a problem: if the player has only touched a wall because of its movement on the X axis, it stops on both axes.
Ideally, it should stop only on the X axis and continue moving on Y, given that there is room to move vertically. To achieve that we have the following actions in the collision event:
This checks whether the player is not colliding with a wall when its X position is returned to its previous frame (xprevious). If that is true, the player jumps back to that X position and does not otherwise stop (meaning it continues moving on the Y axis).
If the previous condition was false, it performs the same check on the Y axis, and if that is true it jumps to that point. If neither of these are true, it jumps to the previous position on both axes, completely stopping the player’s movement.
If you’ve made changes to any of the wall tiles, you will need to update the contents of the Wall_Collisions layer. This is what contains the actual collision boxes that stop the player from moving into a wall, which are simply instances of the obj_editor_wall object, and you can place them over some wall tiles by dragging the object in from the Asset Browser:
The top area of the wall is not covered by the wall instance so the player can freely walk “behind” that wall:
Make sure to make this layer invisible again once you are done editing it!
The props placed around the room (barrels, chests, etc.) are children of obj_collision_parent, much like obj_editor_wall (which is placed over wall tiles). The difference is that the former are visible obstacles, where the editor wall object is invisible.
To create more obstacles you can simply make them children of obj_collision_parent and place them in the room.
Footstep sounds make use of Broadcast Messages. Every 5 frames the player’s animations send out a broadcast message that simply says “step”, to tell the player object that it should play a footstep sound.
This message is received by the player object in its Broadcast Message event (found under “Other” events). If the message says “step”, it plays a footstep sound:
That’s how simple it is!
The main enemy object, obj_baddie, uses Paths for movement. Paths are pre-created sets of points which an instance can follow.
The baddie object has a path_to_follow variable that can hold a path asset. When the instance is created, it starts following that path. Each baddie instance in the room has a different path assigned to it (to change a variable for an instance, double-click on it and open the “Variables” menu).
Since the enemy follows a predefined path, it does not receive directional input like the player does, so it has to calculate where it is moving by comparing its current position against its previous position.
This is done in the End Step event, where it first retrieves the speed values on the X and Y axis by performing a subtraction on the current and previous X/Y positions.
Note: We have used the End Step event as it runs after the Step event. This way we can perform actions after the instance has moved in the main Step event.
It then performs a set of conditions to check whether the enemy is moving right, left, down or up. In any of those cases it performs the same actions as the player’s keyboard events, where its sprite and horizontal scale are changed so it faces in the correct direction.
The aforementioned “SoundObjects” room layer contains instances of obj_sound_loop, which plays a sound on loop with varying volume depending on the distance to the player. This is achieved in its Step event, where it uses the point_distance() function to get its distance from the player and then changes the volume of its sound based on that distance.
The first “Set Audio Volume” action uses the min() function which selects the minimum value from the ones provided to it. This means that if the ‘100 / distance’ expression happens to be greater than 1, it will simply use 1, meaning that the audio volume will never go above that value.
The Hero’s Trail project is a great sandbox for you to create your next action-adventure game. Make sure to follow the Hero's Trail full game tutorial so you can take this project to completion!
If there is anything else that you would like to learn about the Hero's Trail template project, reach out to us and we will make sure it is covered!