In the last chapter we built a tower toppling physics game where the player could use TNT, Wrecking Balls, and Magnetic Cranes to destroy towers made of Glass, Wood, and Steel Pillars. In this chapter we are going to build this game by implementing a Shop, a Score Screen, and Level Intro Dialog. We are also going to rework the HUD so that only the equipment that is available can be used to implement a countdown timer, and add buttons for restarting the level and going to the Shop. In order to accomplish all of this, we will spend some time looking at arrays and data structures for storing information and using global variables.
In the last chapter we built two rooms, Level_01
and Level_12
, for testing the HUD and game difficulty. We now need to make rooms for all the levels in between those two, plus a few additional rooms for the Front End, Shop and Level Select:
Level_02
to Level_11
. Set the size of the rooms as follows:Level_02
– Level_04
is set to Width: 640
and Height: 480
Level_05
– Level_08
is set to Width: 960
and Height: 720
Level_09
– Level_11
is set to Width: 1280
and Height: 960
0
, Y: 0
, W: 640
, and H: 480
so that each room will display properly on screen.Chapter 7/Backgrounds/
. Make sure that Remove Background is not checked.Level_02
– Level_04
: 384Level_05
– Level_08
: 576Level_09
– Level_11
: 784obj_Overlord
and obj_Menu
each. Each room should look something like the following screenshot:MainMenu
, with a Width of 640
and Height of 480
. Move this to the top of the Rooms
folder in the Resource Tree.bg_MainMenu
, and load Chapter 7/Backgrounds/BG_MainMenu.png
. Make sure that Remove Background is not checked.bg_MainMenu
. The box for Visible when room starts should be checked. We are done with this room for now so click on OK.LevelSelect
and Shop
with the appropriate backgrounds applied. The position in the Resource Tree does not matter. We now have all the rooms we will need for the game.The main menu is the very first screen the player will see and it consists of two objects: a button to start the game and a game initializing object with all the global variables:
obj_Global
.scr_Global_GameStart
. We will be adding code to this as we go along, but for now we just need to initialize the score:score = 0;
scr_Global_GameStart
. Click on OK.MainMenu
and place a single instance of obj_Global
somewhere in the room.obj_Button_Parent
.scr_Button_Parent_Create
, and attach this to a Create event with the following code:image_speed = 0; image_index = 0;
scr_Button_Parent_MouseEnter
, and attach it to a Mouse | Mouse Enter event with the code to change it to the second frame of animation:image_index = 1;
scr_Button_Parent_MouseLeave
and attach it to a Mouse | Mouse Leave event:image_index = 0;
The parent object is now complete and the setting should look like the following screenshot:
spr_Button_Start
, with Remove Background turned off, and load Chapter 7/Sprites/Button_Start.gif
. Center the Origin and click on OK.obj_Button_Start
, and apply spr_Button_Start
as the Sprite.obj_Button_Parent
so that the hover states will function properly.scr_Button_Start_MousePressed
, and attach it to a Mouse | Left Pressed event with the following code to go to the room LevelSelect
:room_goto(LevelSelect);
obj_Button_Start
into MainMenu
near the bottom of the screen at X: 320
and Y: 416
. The Room should look like the following screenshot:MainMenu
and that the Start Button functions as designed.The next room we are going to build is LevelSelect
. In this room there will be a button for going to the Shop and buttons for each level in the game, with only the first level unlocked at the start. As the player progresses, the buttons will unlock and the player will have access to all previous levels. To achieve this we will dynamically create buttons for each level in the game and use a 2D array to store all this information.
A 2D array is just like the arrays we have already used in the book. It is a single static list of data but it allows for multiple values per row, like a spreadsheet. This is a very powerful tool at our disposal as it makes it much simpler to group several different elements together:
scr_Global_Levels
, and start by initializing some global variables:globalvar level, totalLevels;
As we are always trying to simplify our code we can use an alternate method of declaring global variables with globalvar
. This declaration method functions exactly the same way as global
, but it will allow us to write level
instead of global.level
. While this will save us plenty of keystrokes, it is up to us to remember that it is a global variable as it is not as obvious.
level[0, 0] = Level_01; level[0, 1] = false;
To create a 2D array, you just need to place two numbers inside the brackets. The first number is for the row and the second for the column. Here we have only a single row that has two columns. The first column will hold the room name and the second column will be for whether that room is locked or not; in this case Level_01
is unlocked.
totalLevels
variable. Here is the complete script for all 12 levels:globalvar level, totalLevels; level[0, 0] = Level_01; level[0, 1] = false; level[1, 0] = Level_02; level[1, 1] = true; level[2, 0] = Level_03; level[2, 1] = true; level[3, 0] = Level_04; level[3, 1] = true; level[4, 0] = Level_05; level[4, 1] = true; level[5, 0] = Level_06; level[5, 1] = true; level[6, 0] = Level_07; level[6, 1] = true; level[7, 0] = Level_08; level[7, 1] = true; level[8, 0] = Level_09; level[8, 1] = true; level[9, 0] = Level_10; level[9, 1] = true; level[10, 0] = Level_11; level[10, 1] = true; level[11, 0] = Level_12; level[11, 1] = true; totalLevels = 12;
scr_Global_GameStart
and execute this script after the score variable.scr_Global_Levels();
spr_Button_Shop
, and with Remove Background turned off, load Chapter 7/Sprites/Button_Shop.gif
. Center the Origin and click on OK.obj_Button_Shop
, and apply spr_Button_Shop
as the Sprite.obj_Button_Parent
.scr_Button_Shop_MousePressed
with the code to switch rooms:room_goto(Shop);
Chapter 7/Fonts/boston.ttf
and select Install. Then follow the directions when prompted.fnt_Large
, fnt_Medium
, and fnt_Small
. All three will have a Font of Boston Traffic
. Set the Size of fnt_Large
to 20
, fnt_Medium
to 16
, and fnt_Small
to 10
.spr_Button_LevelSelect
, and with Remove Background turned off, load Chapter 7/Sprites/Button_LevelSelect.gif
. Center the Origin and click on OK.obj_Button_LevelSelect
, and apply spr_Button_LevelSelect
as the Sprite. These buttons cannot be parented to obj_Button_Parent
as they require the ability to have a locked state, which will affect the hover states.scr_Button_LevelSelect_Create
, and attach it to a Create event.isLocked = true; myLevel = MainMenu; myNum = 0; image_speed = 0; alarm[0] = 1;
We start by making all buttons locked by default. We set a default room for the room it should go to when clicked, and a number that we will draw on top. Finally we stop the sprite from animating and set an alarm for one step.
scr_Button_LevelSelect_Alarm0
, and attach it to an Alarm | Alarm 0 event:if (isLocked) { image_index = 2; } else { image_index = 0; }
If the button is to be locked, we set the sprite to display the locked frame. Otherwise it is unlocked and we show the first frame.
scr_Button_LevelSelect_MouseEnter
, and apply it to a Mouse | Mouse Enter event:if (isLocked) { exit; } else { image_index = 1; }
For the hover state of the button we first check to see if it is locked. If it is, we just exit the script immediately. If it is unlocked, we switch to the hover frame.
scr_Button_LevelSelect_MouseLeave
, and apply it to a Mouse | Mouse Leave event:if (isLocked) { exit; } else { image_index = 0; }
scr_Button_LevelSelect_MousePressed
, attached with code to change rooms if it is unlocked only:if (isLocked) { exit; } else { room_goto(myLevel); }
scr_Button_LevelSelect_Draw
that we can use to draw the button with the appropriate number on top. Add this to a Draw | Draw event:draw_self(); draw_set_color(c_black); draw_set_font(fnt_Large); draw_set_halign(fa_center); draw_text(x, y-12, myNum); draw_set_font(-1);
First we need to draw the sprite that has been applied to the object itself. Next we set the drawing color to black, set the font, and center align the text. We then draw the text held in the myNum
variable, dropping it down a bit on the Y axis so that it centers vertically. Since we will be drawing a lot of text in this game, we should force the font to the default font by setting it to -1
value. This will help prevent this font from affecting any other drawn font in the game:
obj_LevelSelect_Overlord
, to build the menu upon entering the room.scr_LevelSelect_Overlord_RoomStart
, with the following code:column = 0; row = 1; for ( i = 0; i < totalLevels ; i++ ) { lvl = instance_create((72 * column) + 128, 80 * row + 128, obj_Button_LevelSelect); lvl.myLevel = level[i, 0]; lvl.isLocked = level[i, 1]; lvl.myNum = (i + 1); column++; if (column > 5) { row++; column = 0; } } instance_create(320, 440, obj_Button_Shop);
We start by establishing variables for the row and columns that we will need for the layout of the buttons. We then run a loop starting at zero and run it for the total amount of levels we declared in the global variable totalLevels
. Inside this loop, we first create an instance of obj_Button_LevelSelect
and offset it in both horizontal and vertical directions with an additional 128 pixels of padding for a margin between the edge of the screen and the buttons. We then change the button's myLevel
and isLocked
variables by setting it according to the values in the level
global array. Next we change the myNum
variable to indicate what number will be drawn on the button. The last few lines of code are how we will limit the amount of columns and add additional rows of buttons. Every loop we increase the column count and once it passes five we reset it to zero. This will give us a row of six buttons. If we have more than six buttons, a new row will be created that can have another six buttons. This means we can add levels to the array later and they will be added into this menu automatically creating new rows for every six levels. Last but not least, we spawn an instance of the SHOP button at the bottom of the screen.
LevelSelect
and place a single instance of obj_LevelSelect_Overlord
somewhere in the room. This is all we need, and to do so click on the checkmark.LevelSelect
and it should look like the following screenshot. Only Level 1 is accessible at this point and the button is yellow. All the other buttons are gray, indicating that they are locked. Clicking on the button for Level 1 will take you to that level and the SHOP button should take you to the Shop.The only room we have left to build is the Shop where the player will be able to purchase equipment to be used in each level. The room will consist of icons for each piece of equipment, a listing of the price, and a button to purchase the equipment. We will also have a display showing how much cash the player currently has and this will update as they spend money:
TNT
: 0
, WRECKINGBALL
: 1
, MAGNET
: 2
.SPRITE
: 0
, OBJECT
: 1
, AMOUNT
: 2
, and COST
: 3
. When this is complete the settings in the editor should look like the following screenshot:scr_Global_Colors
, with this code:globalvar yellow; yellow = make_color_rgb(249, 170, 0);
We create a global variable for our color and then use a function that has parameters for the amount of red, green, and blue to make our special yellow color.
scr_Global_GameStart
and execute scr_Global_Colors()
.To build a proper Shop and inventory system we need more control over the data than a static array allows. We need something more malleable and searchable. This is where data structures come in. Data structures are special dynamic structures similar to arrays, but with the ability to manipulate the data with specific functions for things such as shuffling or reordering the data. GameMaker: Studio comes with six different types of data structures, each with its own set of functions and benefits:
We are going to start with a Grid data structure as we need several rows and columns of information for each item. Create a new Script, scr_Global_Equipment
and write the following code to build the Grid:
globalvar equip; equip = ds_grid_create(3,4); ds_grid_set(equip, TNT, SPRITE, spr_Menu_TNT); ds_grid_set(equip, TNT, OBJECT, obj_Ghost_TNT); ds_grid_set(equip, TNT, AMOUNT, 1); ds_grid_set(equip, TNT, COST, 100); ds_grid_set(equip, WRECKINGBALL, SPRITE, spr_Menu_WreckingBall); ds_grid_set(equip, WRECKINGBALL, OBJECT, obj_Ghost_WreckingBall); ds_grid_set(equip, WRECKINGBALL, AMOUNT, 0); ds_grid_set(equip, WRECKINGBALL, COST, 1000); ds_grid_set(equip, MAGNET, SPRITE, spr_Menu_MagneticCrane); ds_grid_set(equip, MAGNET, OBJECT, obj_Ghost_MagneticCrane); ds_grid_set(equip, MAGNET, AMOUNT, 0); ds_grid_set(equip, MAGNET, COST, 3000);
We start by declaring a global variable which we then use to hold the ID of a Grid. When creating a Grid we need to declare how many rows and columns it needs. For this game we have three rows for the pieces of equipment and each piece has four columns of data. We set the value for each Grid cell individually, so slot 0 is the sprite to use, slot 1 the object to spawn, slot 2 is for how many the player starts with, and finally, slot 3 is how much it will cost to purchase. We have done this for each piece of equipment and we (the player) will start the game with single TNT only.
scr_Global_GameStart
and call this script. We now have all the equipment categorized and ready for the Shop.scr_Global_Inventory
, and start a List:globalvar inventory; inventory = ds_list_create(); ds_list_add(inventory, TNT);
We declare a global variable and then use it to hold the ID of the List we create. At the start of the game we have already established that the player will have some TNT, so that is all we need in the inventory.
scr_Global_GameStart
. Here is the complete code:score = 0; scr_Global_Levels(); scr_Global_Colors(); scr_Global_Equipment(); scr_Global_Inventory();
spr_Button_Buy
, and with Remove Background turned off, load Chapter 7/Sprites/Button_Buy.gif
. Center the Origin and click on OK.obj_Button_Buy
, and assign spr_Button_Buy
as the Sprite.obj_Button_Parent
.scr_Button_Buy_MousePressed
, with the following code:if (score > ds_grid_get(equip, myItem, COST)) { ds_grid_add(equip, myItem, AMOUNT, 1); score -= ds_grid_get(equip, myItem, COST); if (ds_list_find_index(inventory, myItem) == -1) { ds_list_add(inventory, myItem); } }
In order to purchase an item, we first need to check to see if the player has enough money. For this, we compare the score
against the data held in the Grid we created. You will notice that we have a variable, myItem
, that has not been initialized in the button itself. We will create that variable dynamically later, when we spawn the button. If the player can purchase the item, we increase the amount the player owns, and reduce the amount of money by the price of the item. Finally, we check to see if the player already has some of the item in their current inventory. If this is the first item of its type, we add it to the inventory List.
obj_Shop_Overlord
.scr_Shop_Overlord_RoomStart
, with the code for spawning the buttons needed in the Shop:for ( i = 0; i < ds_grid_width(equip); i++ ) { buyButton = instance_create(512, (96 * i) + 152, obj_Button_Buy); buyButton.myItem = i; } instance_create(502, 440, obj_Button_Start);
We start by running a loop through each row of the equipment Grid so that we know how many buttons need to be created. We then spawn a purchase button which will be stacked vertically on screen. Next we pass the myItem
variable that is used in the mouse pressed event. The last thing we do is create a start button in the lower right corner of the screen so that the player can go back to LevelSelect
option.
scr_Shop_Overlord_Draw
, and add it to a Draw | Draw event:draw_set_color(c_black); draw_set_halign(fa_center); for ( i = 0; i < ds_grid_width(equip); i++ ) { draw_sprite(ds_grid_get(equip, i, SPRITE), 0, 96, (96 * i) + 152); draw_set_font(fnt_Small); draw_text(116, (96 * i) + 166, ds_grid_get(equip, i, AMOUNT)); draw_set_font(fnt_Large); draw_text(300, (96 * i) + 140, ds_grid_get(equip, i, COST)); }
First we need to set the font color to black and center-align the text. We then run a loop through the equipment Grid to draw each component. We first draw the proper sprite in the correct location to line up with the buttons. Here we use a small font to draw the amount of the item the player owns in the small space in the lower right corner of the sprite. We then change to a large font and display the price of the item.
draw_set_color(yellow); draw_set_font(fnt_Medium); draw_text(96, 416, "Cash"); draw_set_font(fnt_Large); draw_text(96, 440, score); draw_set_font(-1);
We set the color to our special yellow color for the rest of this text. We set a medium font to display the word Cash
and then change to a large font for the actual amount they have. Finally, we reset the font to default.
Shop
and place a single instance of obj_Shop_Overlord
object somewhere in the room. We are done with this room so click on OK.3.129.218.69