To celebrate 20th anniversary of Game Maker, YoYo Games decided to run a #GM20 game jam on itch.io. Theme? 20. One of the examples was making a game in 20 lines of code. So I decided to take that challenge. Now, let me show you how I made Bulldozer #GM20 – a game in 20 lines of code.
Caveat: Bulldozer #GM20 was made in Game Maker: Studio 2, which handles much stuff behind the scenes. As a line of code I’m counting actual lines written in various object events (ending with ‘;‘). “If” condition was counted as one line and the following statement as a next line.
Designing the "game in 20 lines"
When you have a limited number of lines of code to use, you’re limited to what kind of game, and with how many features, you can make. I recently was thinking about a game where you destroy buildings with a bulldozer. It seemed to be a good fit, as it didn’t sound too complicated. I knew that I could squeeze movement for each axis into a separate line of code and I came up with a fun combo scoring system (where the trick was to choose the correct order of destroying buildings). I was good to go, but then I realized that I won’t have any room for the quality of life features.
I needed 4 lines to handle scoring after destroying buildings, 3 lines to load high-score and 3 lines to save it. That’s half of the resources that I had. That meant that always when you played the game, you had to start from the 1st level. No continue, load or level select. That’s a big downside, especially as I wanted to have many levels (though I didn’t make too much in the end). Selecting a level on the title screen costs 6 lines of code total (handling and drawing it). So I had to choose – quality of life but only a simple puzzle game or harsh to play but fun arcade game. I decided to go with added functionality and ease of play. It turned out quite well, so let’s start talking how it was all made.
Controlling a bulldozer
The trickiest part of making Bulldozer #GM20 was making bulldozer move and animate with as little lines of code as it was possible. It turned out to be not that hard.
The player controls the bulldozer object. It moves on a grid, so every key pressed shifts us by the size of a single grid cell. For each axis it can be done in one line:
x += 128*keyboard_check_pressed(vk_right)*place_meeting(x+128,y,obj_building) – 128*keyboard_check_pressed(vk_left)*place_meeting(x–128,y,obj_building);
So what we have here?
- 128 is the size of the grid cell, a distance we want to move by,
- keyboard_check_pressed() returns 1 when key is pressed and 0 when not,
- place_meeting() is here to see if we can move in that direction, as you can only enter spaces with damaged buildings to destroy, you can’t backtrack or sidetrack, and that’s the main difficulty of the game.
Pretty straightforward, key_check and place_meeting allows us to move when the condition is met. We can have both x-axis directions in the same line with simply ‘+‘ for going right and ‘–‘ for going left. A similar thing happens with y-axis:
y+=128*keyboard_check_pressed(vk_down)*place_meeting(x,y+128,obj_building) – 128*keyboard_check_pressed(vk_up)*place_meeting(x,y–128,obj_building);
So now we are 2 lines down. Place the bulldozer and some buildings in a room and you can play around. Now let’s move onto the sprite_index:
sprite_index = A + sprite_index – sprite_index*B;
(I added A and B to make it clear because they take quite a bit of one line)
A = keyboard_check_pressed(vk_right)*spr_bulldozer_right + keyboard_check_pressed(vk_left)*spr_bulldozer_left + keyboard_check_pressed(vk_down)*spr_bulldozer_down + keyboard_check_pressed(vk_up)*spr_bulldozer_up
B = (keyboard_check_pressed(vk_right) + keyboard_check_pressed(vk_left) + keyboard_check_pressed(vk_down) + keyboard_check_pressed(vk_up))
So this is an almost 400 character line and the most complex. Every sprite is built from one image, thus we don’t need to worry about image_speed, but let’s get to what we see above. A gives us a correct sprite for a correct key, that’s simple, but what when we don’t press a key? That’s why we have “+ sprite_index“, it keeps the last “pressed” sprite to be drawn, but since we don’t want it to be added also in steps when we pressed the key, we need to subtract it from the sprite_index. That’s when B comes to play. It gives 0 when no key is pressed and 1 when it is. So then we’re left with only an A value.
Phew, that was quite a long part, thankfully that was the most complicated stuff about Bulldozer #GM20, now let’s move to the logic behind beating a level.
Right now we stand at 3 lines of code. Let’s do something fun, something bulldozer is meant to do – destroy buildings:
To remove an object from the game (room) we use function instance_destroy. Now we can pass the id of the instance we want to destroy with instance_place (in comparison to place_meeting it returns the id, not boolean value). We simply put bulldozer’s current coordinates (as we moved on the building) and we are one step closer to the end of the level, which we do like that:
if(!instance_exists(obj_building) && alarm < 0)
alarm = room_speed;
We check to see if there are any buildings left, if no, we set alarm to value and in alarm code we have this short line:
I decided to add a little pause before leaving to the next level, so we wouldn’t have a sudden change – we’ve just moved to a building’s space, let’s have a little breather before moving on. That requires adding an “alarm < 0″ check to our if, so we won’t be setting an alarm over and over.
Now, that’s a total of 7 lines of code and we’ve finished with the gameplay. To this point, we were writing only in obj_player but I think that it is time to add some juice to the game.
Art of destruction
Right now you can move and destroy but it doesn’t feel so good. We needed something to spice things up. Let’s go the obj_building destroy event and add this:
Now we added an explosion animation (obj_boom) and played a sound. All of a sudden our game has a soul.
Since we don’t want the explosion animation to be displayed in a loop, we go to obj_boom and in animation end event add this:
It couldn’t be simpler, right? 10 lines down, 10 more left, let’s move back to obj_building and add some quality of life.
Bulldozer #GM20 is a puzzle game, no surprise that you can get stuck without any chance to move. Forcing the player to close the game and start from the beginning would be bad, that’s why we will use 4 lines of code to give players some help.
if(keyboard_check_pressed(vk_space) || keyboard_check_pressed(ord(“R“)))
Escape key allows players to return to the title screen if they would want to start over and select a different level. Players also can restart a level if they would get stuck. Those 4 simple lines can vastly improve the experience with our game, so it’s worth it to spend them here.
We are left with 6 lines so let’s head over to the title screen.
Back to the Start
We are almost done with coding Bulldozer #GM20, now we’ll let players choose a level before the start. Firstly, we need to initialize a level variable in create event:
global.Level = 1;
Then, let’s go to the step event:
global.Level = clamp(global.Level + keyboard_check_pressed(vk_up)+keyboard_check_pressed(vk_right)-keyboard_check_pressed(vk_down)-keyboard_check_pressed(vk_left), 1, 7);
Here we’re setting a level variable, and since key_checks returns 1 or 0, we can use them here as well. Simply up and right will add 1 to the level variable, while down and left will subtract 1. Function clamp() keeps variable between the minimum (1) and maximum (7) value. That’s very useful and can save some space or lines.
Now let’s press enter and go to the selected level:
Function asset_get_index() returns index based on passed name of an Game Maker asset. When we have all levels named “rm_X“, it’s easy for us to combine string “rm_” with level variable (remember to turn real number into string with function string()).
With 2 lines of code left we can draw on the screen which level is already being selected:
After selecting a font, we’re simply drawing text with a level variable in it.
Now you can press enter and enjoy the game made with only 20 lines of code.
At the summit
Making Bulldozer #GM20 was quite fun, though it for sure is not the most optimal way of coding. Being limited by lines of code is very artificial but, like always, there is something to learn here.