Button Controls
Button Controls
GAMEMAKER
Gurpreet S. Matharoo Sam Spade
27 March 2023
Beginner
o User Interface
Your game needs a great UI to match its great gameplay, and one of the most important UI
elements is a button, which you can easily make in GameMaker.
In this tutorial, you'll learn how to make a button that's easy to customise, scale, and modify to fit
your needs.
BEFORE WE START
If you'd like to follow along with this tutorial, you can download the sample GML Code
project or the sample GML Visual project.
In the sample project, I've set up a room, a camera which follows an object, and included all the
assets (sprites, sound, and font) I'll be using.
If you run the sample project, you should see the camera panning along the room.
To set up your sprite, open its Sprite Editor. Go to the Nine Slice section, and check the
“Activate Nine Slice” box.
Position your guides to create “nine slices”. For this sprite, I've found 36 pixels on each side to
work nicely, and I've set the center to stretch.
You can test out how the sprite will scale in the Preview panel on the right. Read more about
Nine Slicing in the manual.
Our sprite has three frames, but an animation speed of 0. This is because we don't actually want
our sprite to animate. We want the sprite's origin point to be centered for this demo, but you can
experiment with different origin points in your project.
PROGRAMMING THE BUTTON
Next, add a Create event.
In the Create event, we need two variables: hovering and clicked. Set these variables to false.
hovering = false;
clicked = false;
Go to the object’s “Variable Definitions” tab and add a new variable. Name the
variable button_text, set the type to String, and set the default value to "Default" (including
quotes).
Next, add a Draw event. GameMaker automatically draws your sprite, but you can override that
behavior by creating your own Draw event.
Since we want to draw our button to the GUI layer, leave this Draw event blank.
Make sure the event has at least one comment, which it does by default.
draw_self();
This tells GameMaker to draw the sprite exactly as if the Draw event held no code or actions.
Now we need the following four lines of code.
draw_set_font(fnt_button);
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_color(c_white);
This code tells GameMaker which font to use, how it should be aligned, and what colour to draw
it with. In our case, we're using fnt_button, aligning it to the center and drawing it white. You
can experiment with different values here to learn more.
Note: If you’re not using the base project given above, you'll need to create the fnt_button Font
asset.
Finally, we need one more line of code:
draw_text(x, y, button_text);
draw_self();
draw_set_font(fnt_button);
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_color(c_white);
draw_text(x, y, button_text);
Note: GameMaker draws your game to the screen a bit like a painter adding paint to a canvas -
when something is drawn, it's drawn over what has already been drawn. So, it's important that we
draw our button first using draw_self(); and then draw the text over it.
Our button is now ready to be placed in our room.
Many things in GameMaker work based on their position in the room. For example, the built-in
draw event draws an instance of an object using its x and y values as room coordinates. This
means that the button will not move with the camera.
If you zoom or rotate the camera, the problem gets even worse!
The GUI layer solves this problem by using a different coordinate system. On the GUI layer, the
top left of your screen is always 0, 0 and the bottom right of your screen is always the width and
height of your GUI layer, regardless of how your camera moves, zooms, or rotates.
This means a couple of different things. First, when you draw something on the GUI layer, it will
not move or rotate when your camera moves or rotates.
Second, it also means that your GUI layer can have a different resolution than your camera. For
example, in this project, I've set the camera's width and height to be half the width and height of
the viewport.
Note: While there are other ways to control your GUI's width and height, by default GameMaker
will automatically set your GUI size to be the same as your viewport’s size.
However, even though our game's camera is both zoomed in and moving, it'll remain at the
correct size and in the correct position because we're drawing our button to the GUI layer.
For this project, I've kept it simple. Since our room is the same width and height as our viewport
(and therefore our GUI layer), we can use our room as a guide for placing our button. If we want
the button to appear in the center of our screen, we should place it in the center of our room.
Note: Most of the time, your GUI Layer will not be the same size as your room and this trick
won't work. Just remember that when you're drawing objects to the GUI layer, they need to be
placed in the room where they should appear on your GUI layer.
You can also use sequences to solve this problem. We'll cover this at the end.
Now, we can run the game and the button will appear in the center of the screen, even though the
camera is moving across the room and is actually half the resolution of our button.
Using the GUI layer does come with one drawback; we can no longer use GameMaker's built-in
events for mouse collision, or the built-in mouse_x and mouse_y variables.
However, GameMaker also provides us with several functions to get the mouse's position on the
GUI layer, which we can use instead.
These built-in functions take one argument, which is the device you want to convert. When using
a mouse, it will always be device 0.
clicked = true;
}
Note that we're checking two conditions, hovering and mouse_check_button_pressed. If and
only if both of these are true, we know that we've clicked on the button and can
set clicked to true.
if (mouse_check_button_released(mb_left))
clicked = false;
if (hovering)
audio_play_sound(snd_button, 1, false);
room_restart();
}
}
Finally, we want to add the following code to control the button’s image index.
if (clicked)
image_index = 2;
}
else if (hovering)
{
image_index = 1;
}
else
{
image_index = 0;
}
If we are clicking on the button, we want the image index to be 2. If we're hovering but not
clicking, we want it to be 1. And, if we're neither hovering nor clicking, we want it to be 0.
The entire Step event in GML Code should now look like this:
hovering = position_meeting(device_mouse_x_to_gui(0),
device_mouse_y_to_gui(0), id);
if (hovering)
{
audio_play_sound(snd_button, 1, false);
room_restart();
}
}
if (clicked)
{
image_index = 2;
}
else if (hovering)
{
image_index = 1;
}
else
{
image_index = 0;
}
Our basic button is now done and we can test it. You can hover over it and click to restart the
room.
However, we have one very important step that will make this button much more
useful: Inheritance.
We could duplicate this button and change some code, but what if we wanted to change how our
buttons work? We'd have to make that change in every button.
What if we wanted a way to refer to all of our buttons at once? We'd have to list every button.
This might work if we only have two buttons, but what if we had ten or twenty? Eventually, it
would become very difficult to manage.
Duplicating our entire button object every time we need a new button will create problems down
the road. Instead, we'll use inheritance.
Using inheritance, we'll turn our current button into a parent and then have button children
inherit the button parent's code. This means that any changes we make to the button parent will
apply to the button children.
Additionally, it means we have a way of referencing all of our buttons at once by referencing the
button parent.
This is a method. A method is a customized command that you create, and it can then be used as
a built-in function.
Note: If you want to learn more about methods, check out their page in the manual.
Normally, you would put the code you want to run when the method is called inside the brackets.
However, in this case, we want the method to be blank. We'll override this method in the
children.
In GML Visual, use the New Function Code block and leave it empty.
This will make sure the method assigned as activate_button is called when a button is pressed.
Now comes the cool part. We'll create two new objects and name
them obj_button_restart and obj_button_exit and give them the same sprite we were using
before.
Make them children of object_button_parent.
For each button, we override the button_action method by recreating it. You can write code to
have each button do something unique.
For the restart button, use room_restart() and for the exit button use game_end().
activate_button = function()
room_restart();
}
Add this in obj_button_exit’s Create event (after inheriting it).
activate_button = function()
game_end();
}
Go to the room and replace obj_button_parent with these new buttons (position them how you
would like), run it, and everything works.
Add your buttons to that sequence asset and make sure your playhead is at the start before
continuing.
Stretch the asset keys for your tracks so they last for the whole Sequence, and then position and
scale your buttons however you'd like. You can see my version below.
Next, add a colour multiply channel to your button track.
If you hit play, you should see the buttons fade in. Make sure looping is disabled (which it is by
default).
PLAYING THE SEQUENCE
Add a Key Press event for the space bar in obj_game, and insert the following code:
if (!layer_sequence_exists("MenuSequence", sequence_id))
sequence_id = layer_sequence_create("MenuSequence",
display_get_gui_width()/2, display_get_gui_height()/2,
seq_menu);
}
else
{
layer_sequence_destroy(sequence_id);
}
This code will create a sequence if none exists and destroy it if one already does.
In the first block, the sequence is being created in the center of the GUI layer. We use
the display_get_gui_width (and equivalent height) function and divide the values by 2 to get the
center.
The code above creates the sequence in a layer called “MenuSequence”, which we’ll create in a
second.
The sequence_id variable that we’re setting here needs to be initialised in the Create event, so
add this line of code there:
sequence_id = noone;
Finally, go to the room and remove the buttons that already exist. Add an Asset Layer instead
and title it MenuSequence.
Now, when we run the game and hit the spacebar, the buttons will fade in, and if we hit it again,
they'll be destroyed. And, of course, you can make the sequence as complicated as you want.
Here's one where the buttons slide in.
You can download the full GML Code project here and the full GML Visual project here.
SUMMARY
Some key takeaways from this tutorial:
You can use the Draw GUI event to draw things on the GUI Layer, instead of drawing them
in the room.
You can use the device_mouse_x_to_gui (and_y) function to get the mouse position in GUI
space.
You can use Object Inheritance to create one parent with the base code and create multiple
children from it.
You can create your own methods in objects and then override those methods in children
objects to make the child object perform different behavior.
You can use Sequences to lay out and animate your buttons.
If you have any questions, feel free to reach out to @itsmatharoo on Twitter.
Happy GameMaking!
BACK TO TUTORIALS