Appendix A: Game – Plane Game
We’ll now put into practice everything you’ve learnt to make a small fun shooting game.
This game will include
Parallax Background
Music and Audio Control
A Highscore Save System Using INI Files
A HUD with Score, Healthbar and Lives
Enemy Spawning System Using Alarms and Timelines
Health and Lives System
Paths for Enemies
Functions for Processing Data
Multiple Room Layers
An Interactive Menu System
Fonts and Text Management
Enemy AI That Targets The Player
DS Lists for Randomizing Enemy Spawning
Randomization for Generating Background Items
Collectible Items
Paths for Enemies to Follow
Effects When Hit or Collecting Items
Various Rooms for Different Game Elements
Lots of Collisions for Player, Enemies, and Projectiles
Mouse and Keyboard Input
Health, Lives, and Score Management
Sprite Control and Drawing
A Weapon Control System
Lots of Conditional Structures for Game Control
Script for choosing an enemy
Various Variables to Keep Track of What’s Going On Within the Game
In this game, you control a plane through an endless level. You will shoot the enemies and avoid their weapons. There are four enemy types, each of which behaves differently, with separate ways to move and attack the player. The game will have a basic parallax background to give the illusion of movement. The aim is to get the highest score and survive as long as possible, and to collect bonuses for health, lives, extra points, and weapons. Player loses health if hit by an enemy or enemy’s weapons. The game will have a splash screen to set up the game and load any current highscore. The menu will allow you to start the game, view game info, or exit to windows. In addition, there will be a “gameover” screen displaying the player’s final score, and updating the saved highscore if a new record is achieved.
I’ll show all steps required to make this game. I have omitted most of the downloading and sourcing of audio and graphics as that is not really within the scope of this book, though I included importing and setting up/formatting of images. All assets used are available in the download that comes with this book. There is also a completed project file that you can import.
I’ll be using the Rubber Duck technique when making this game. This method assumes you are explaining how to do something to a rubber duck – so every detail is explained. See www.freecodecamp.org/news/rubber-duck-debugging/ for a more in-depth explanation. Feel free to skip over sections that you are comfortable with.
When I start a new project, I generally do things in the following order:
1.
Plan out main game features with a pen and paper.
2.
Source suitable graphics and audio – checking the license for each, ensuring that I can use it for the project.
3.
Set up splash screen room.
4.
5.
Set up Game Information room.
6.
7.
Set up first level (which could be just 1 or many depending on the game).
8.
9.
10.
11.
12.
13.
14.
Create Enemy Spawning System.
15.
Plan Out Main Game Features with a Pen and Paper
Although making and programming a game can be a dynamic process, I generally sketch out the look of a game on paper, make notes on what objects there will be and how they interact with each other. Sometimes this can be just a page or two, or more – a recent project had over 30 pages of notes stuck on my floor. For a simple game like this, it is just one page, plus the notes I made at the start of this chapter. This does a great job of explaining what this game entails.
A lot of the time you’ll add a feature, think of better option and program this instead. Creating a game is a creative process, and I personally welcome adding new ideas while working on a game project.
Source-Suitable Graphics and Audio
As mentioned previously, I won’t include sourcing most of the images, just the formatting, which will be covered in the following sections.
All the assets used in this game are in the resource download folder Game.
Let’s head over to
FlamingText.com and create a suitable logo for the game, as shown in Figure
A-1, so add shadows or edit in an image editor to add effects, etc., if you so choose.
Import this into a new GameMaker project, and name the sprite as spr_logo and set the origin as middle center.
We’ll use this sprite on the splash screen, menu, game info, and gameover rooms.
Set Up Splash Screen Room
First rename the default room name as room_splash, this will be the first room that is run.
Let’s create a background for it. Create a new sprite
bg_menu and load in the sprite from the resources folder,
splash_and_menu_background. You’ll notice that the image is particularly large, so let’s resize so it can fit into the splash room. There is a quick shortcut for doing this – click where shown in Figure
A-2.
Set the dimensions as shown in Figure
A-3, clicking Apply to set the changes.
Set this sprite as the background for
room_splash, checking the box for stretch so the image fills the whole room, as shown in Figure
A-4.
Next we’ll set up an object that will be used to initialize the variables needed for the game.
Create an object
obj_splash and assign the sprite
spr_logo to it. Pop the initialization code into its
Create Event:
/// @description Set up
//position in middle of room
x=room_width/2;
y=room_height/2;
alarm[0]= game_get_speed(gamespeed_fps)4;//show splash screen for 4 seconds
//Create ds list for enemy spawning
global.enemy_list=ds_list_create();
score=0;//declare starting score
health=100;//declare starting health
lives=5;//declare starting lives
global.special_weapon=5;//start with 5 special weapons
global.shots_fired=0;// so you can keep track of shots fired
global.hits=0;// so you can keep track how many hits player makes
global.kills=0;//to keep track of player's kills
//load any highscore - set as 0 if no data
ini_open("gamedata.ini");
global.highscore = ini_read_real("data", "highscore", 0);
ini_close();
Then put the following into an
Alarm 0 Event:
/// @description Go to menu
room_goto(room_menu);
Place an instance of this object into
room_splash, as shown in Figure
A-5.
That is all for this room.
Set Up Menu Room
First, create a new font font_button and set as Arial size 20. We’ll use this font to draw descriptions on the menu buttons.
Next we’ll source some buttons you can use for the menu, so head over to clickminded.com/button-generator/ to create some sprites.
Let’s make some nice big buttons, in three different colors, for example, blue, green, and red as shown in Figure
A-6.
Once you’ve downloaded them, create a sprite
spr_button and import all three images and set the origin as middle center, and order them as shown in Figure
A-7. Setting the origin as middle center will make it easier to position them in the middle of the room, and also make formatting the text to appear centered when you assign it to draw later.
Now create an object, obj_menu_play, and assign the sprite you just created.
In a
Create Event pop in the following:
/// @description Set up
image_speed=0;//turn off animation
image_index=0;//set initial image
display="PLAY GAME";
///move to middle
x=room_width/2;
We’ll use the
Step Event to control the button with:
/// @description Control Sprite && Detect Click
//change subimage on mouse over button (or not)
if position_meeting(mouse_x,mouse_y,id)
{
image_index=1;
}
else
{
image_index=0;
}
//change image if left button pushed on it:
if position_meeting(mouse_x,mouse_y,id) && mouse_check_button(mb_left)
{
image_index=2;
}
//Do something on mouse release over button
if position_meeting(mouse_x,mouse_y,id) && mouse_check_button_released(mb_left)
{
room_goto(room_game);
}
We’ll draw the button and assigned text with the following in the
Draw Event:
/// @description Draw Button & Text
draw_self();//draw currently assigned subimage
draw_set_font(font_button);
//set allignment
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_colour(c_white);//set text colour
draw_text(x,y,display);
Next we’ll set up two other buttons, first is
obj_menu_info with the
Create Event code set as
/// @description Set up
image_speed=0;//turn off animation
image_inde=0;//set initial image
display="GAME INFO";
///move to middle
x=room_width/2;
and it’s
Step Event code as
/// @description Control Sprite && Detect Click
//change subimage on mouse over button (or not)
if position_meeting(mouse_x,mouse_y,id)
{
image_index=1;
}
else
{
image_index=0;
}
//change image if left button pushed on it:
if position_meeting(mouse_x,mouse_y,id) && mouse_check_button(mb_left)
{
image_index=2;
}
//Do something on mouse release over button
if position_meeting(mouse_x,mouse_y,id) && mouse_check_button_released(mb_left)
{
room_goto(room_info);
}
The
Draw Event code is the same, with
/// @description Draw Button & Text
draw_self();//draw currently assigned subimage
draw_set_font(font_button);
//set allignment
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_colour(c_white);//set text colour
draw_text(x,y,display);
and a final button to exit to Windows with the
Create Event code:
/// @description Set up
image_speed=0;//turn off animation
image_inde=0;//set initial image
display="QUIT GAME";
///move to middle
x=room_width/2;
Step Event with
/// @description Control Sprite && Detect Click
//change subimage on mouse over button (or not)
if position_meeting(mouse_x,mouse_y,id)
{
image_index=1;
}
else
{
image_index=0;
}
//change image if left button pushed on it:
if position_meeting(mouse_x,mouse_y,id) && mouse_check_button(mb_left)
{
image_index=2;
}
//Do something on mouse release over button
if position_meeting(mouse_x,mouse_y,id) && mouse_check_button_released(mb_left)
{
game_end();
}
and the
Draw Event with the same as previously used:
/// @description Draw Button & Text
draw_self();//draw currently assigned subimage
draw_set_font(font_button);
//set allignment
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_colour(c_white);//set text colour
draw_text(x,y,display);
Now load in the music we’ll use for the menu,
snd_music_menu, which is in the resources folder. We’ll set up as shown in Figure
A-8, this option reduces the overall size of your game, and is the ideal setting for music in most cases.
Next create a new object,
obj_menu_logo and assign
spr_logo to it. Pop the following in its
Create Event:
/// @description Music Control
audio_stop_all();//stop anything already playing
audio_play_sound(snd_music_menu,1,true);//play music on loop
Create a new room,
room_menu and apply the same background as for the splash room, remembering the
Stretch option for the background. Add one of each of the button objects and one of
obj_menu_logo, as shown in Figure
A-9.
Set Up Game Information Room
Next we’ll create a room to display some information about the game. Create a room room_info and assign the same background as used previously.
Create a new font,
font_info, let’s use Comic Sans size 22, as shown in Figure
A-10.
Create an object obj_info and assign spr_logo. You won’t use default drawing, just assigning it so you can see it placed in room.
Pop the following in the
Create Event:
/// @description Set alarm
alarm[0]=game_get_speed(gamespeed_fps)*5;
and in an
Alarm 0 Event:
/// @description Back to menu
room_goto(room_menu);
and in a
Draw Event:
/// @description Draw stuff
draw_sprite_ext(spr_logo,0,room_width/2,100,0.5,0.5,0,c_white,1);
draw_set_font(font_info);//set font
//set allignment
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_colour(c_black);//set text colour
draw_text(room_width/2,room_height/2,"Example Game
Move With Keys W And D
Shoot Bullet With Left Mouse Button
Shoot Special Weapon With Right Mouse Button
Shoot Enemies
Avoid Enemies & Bullets
Collect Crates For More Special Weapons");
When tested, this will look like that shown in Figure
A-11.
Set Up Gameover Room
Next we’ll set the room that the player will go to when they have lost all their lives and health. It will show some stats that the player has achieved, and also check whether the player has set a new highscore. Create a room, room_gameover and set the background as before, remembering to select the Stretch option in Background settings.
Create a new object obj_gameover and assign the sprite spr_logo to it.
In its
Create Event, put the following code that checks whether the player has or has not set a new highscore and update, if required. It will also set a string we’ll use as a message when you draw the player’s statistics, we’ll also center it in the room.
/// @description Check for highscore
if score>global.highscore//check if better than current highscore
{
///save to ini file if bigger
ini_open("gamedata.ini");
ini_write_real("data", "highscore", score);
ini_close();
//set a message
score_message="New HighScore";
}
else//do this if not a new highscore
{
score_message="No New HighScore - Best Score Was "+string(global.highscore);;
}
//center on screen
x=room_width/2;
In the
Step Event, put the following code that will allow the player to restart the game:
/// @description Restart Game on Left Mouse Button Pressed
if mouse_check_button_pressed(mb_left)
{
game_restart();//restart game
}
and in a
Draw Event, put
/// @description Draw Logo & Game Data
draw_self();//draw assigned sprite
draw_set_font(font_info);//set font
//set allignment
draw_set_halign(fa_center);
draw_set_valign(fa_middle);
draw_set_colour(c_black);//set text colour
draw_text(x,450,"Your Score "+string(score));
draw_text(x,480,score_message);
draw_text(x,510,"Shots Fired "+string(global.shots_fired));
draw_text(x,540,"Shots Hit "+string(global.hits));
draw_text(x,570,"Kills "+string(global.kills));
draw_text(x,630,"Left Mouse Button To Restart");
You’ll probably want to test that this all works as expected, but wondering how you could do this without an actual game to test yet. What I tend to do is create an object for testing with some random data. Create a new object
obj_testing and put the following code in the
Step Event:
/// @description For testing
if keyboard_check(ord("X"))
{
score=irandom(1000);
global.shots_fired=irandom(1000);
global.hits=irandom(1000);
global.kills=irandom(1000);
room_goto(room_gameover);
}
Pop an instance of this object in
room_menu. I usually put such instances in the top left of the room. It will appear as a grey circle, as shown in Figure
A-12. This grey circle will not appear in game, it just shows in the IDE.
Before you go any further, let’s tidy up the resources tree a bit. Create a new folder in the Objects section by right clicking with mouse and selecting Create Group, as shown in Figure
A-13.
Name it as Menu. You can now select the objects and drag them into this folder.
Set Up First Level
Create a room, room_game. This will be the room where the main game action will take place.
First we’ll make the background with the parallax effect. Load in the backgrounds, bg_far setting as a size of 1366x1104, bg_sky as 728x768, and bg_near as its current size of 518x136.
Create a background layer for each of
bg_far,
bg_sky,
bg_near and an Instance Layer, as shown in Figure
A-14.
Set up layer
far as shown in Figure
A-15.
Set up
sky layer as shown in Figure
A-16, noting the Horizontal Speed of -2.
For the
near layer, set as shown in Figure
A-17, again noting the Horizontal speed setting of -3.
Create a sprite
spr_water and import all the water subimages, as shown in Figure
A-18.
Create an object,
obj_water and assign this sprite to it. On the water Instances layer, place these objects along the bottom of the room, as shown in Figure
A-19.
As a final stage we’ll create some clouds that will move across the room, helping to break the otherwise boring background.
Create an object
obj_cloud and load in the three cloud images to
spr_cloud, changing the size to 220x107 and setting the origin as middle center, as shown in Figure
A-20.
Pop the following code in the Create Event:
/// @description Choose a random position, speed, and cloud
x=room_width+sprite_width;//create to the right of room
y=irandom_range(50,600);//choose a random y position in top part of the room
hspeed=random_range(-5,-2);//choose a random speed to move left
image_speed=0;//prevent animation
image_index=irandom(2);//choose a random subimage
In a
Step Event, put the following code. This will destroy the instance when the game is done with it.
/// @description detroy when off screen to the left
if x<0-sprite_width
{
instance_destroy();
}
Create a new Instance Layer named
clouds, in the position shown in Figure
A-21.
Next we’ll create an object that will spawn these clouds. Create an object
obj_spawn_clouds and put the following in the
Create Event:
/// @description Set initial alarm
alarm[0]=game_get_speed(gamespeed_fps);
and in an
Alarm 0 Event:
/// @description Spawn cloud and restart the alarm
instance_create_layer(x,y,"clouds",obj_cloud);
alarm[0]=game_get_speed(gamespeed_fps)*random(4);
There is no sprite for this object. Pop an instance of this object in the top left of the game room, as shown in Figure
A-22, upon the cloud layer.
Create HUD
Next we’ll set up the HUD. We’ll do this mainly through drawing code, but we’ll use a sprite to show the number of lives that the player has.
Go ahead and load in a sprite from the Player Plane folder, naming as
spr_lives and resize as shown in Figure
A-23, also setting the origin as center so drawing it later is easier.
Also set up another sprite,
spr_hud_special, as shown in Figure
A-24.
We’ll also need a font to draw data, let’s use something that’s easy to read – Arial Black size 22 would work well. Set up a new font
font_hud as shown in Figure
A-25.
Create an object, obj_HUD and put the following in the
Draw GUI Event. Drawing in this event ensures that the HUD
text and images will be drawn above everything else within the game.
/// @description Draw The HUD
///draw a border
draw_set_colour(c_white);
draw_roundrect(20,698,1346,748,true);
var middle=723;//this is the middle y position - will use to make code below easier
//draw lives as images
for (var pos=0;pos<lives;pos++)
{
draw_sprite(spr_lives,0,1120+(pos*45),middle);
}
//draw special weapon as images
for (var pos=0;pos<global.special_weapon;pos++)
{
draw_sprite(spr_hud_special,0,770+(pos*45),middle);
}
//draw the health bar
draw_healthbar(40,middle-20,400,middle+20,health,c_white,c_gray,c_gray,0,true,true);
//Set Font and text settings
draw_set_font(font_info);//set font
draw_set_halign(fa_left);
draw_set_valign(fa_middle);
draw_set_colour(c_blue);//set text colour
//draw text
draw_text(980,middle,"LIVES:");
draw_text(610,middle,"SPECIAL:");
//format code with leading zeros
draw_text(410,middle,"SCORE: "+string_repeat("0",3-string_length(string(score)))+string(score));
//Change font allignment
draw_set_halign(fa_center);
draw_text(220,middle,"HEALTH");
We’ll also use this object to keep track of the player’s lives and health, and take the player to the gameover room if they run out. We’ll also add a few lines so you can reduce the health for testing purposes. Place the following code in a
Step Event:
/// @description Control
if keyboard_check(vk_left)
{
health--;
}
//Cap health
//reduce lives if out of health
if health<=0 && lives>=1
{
lives--;
health=100;
}
if health<0 and lives=0
{
room_goto(room_gameover);
}
Now is a great place to save and test your game. You’ll now be able to see the HUD and adjust your health.
Create Main Player
As the room has been set up to give the illusion of moving to the left, all you need to do control-wise is move the player object up and down, and perhaps slightly change the image angle to make it look a bit better.
Next we’ll set up the player object’s main movement and assign a sprite. First create the sprite
spr_player, which consists of two subimages. Resize to 192x131 and set the origin as center, as shown in Figure
A-26.
Create an object, obj_player and assign this sprite.
In the
Create Event pop the following:
/// @description Set Up
midpoint=275;//set middle and starting point
y=midpoint;//start player in middle
And in the
Step Event place the code:
/// @description Movement control
if (keyboard_check(ord("W"))) {y-=2;}
if (keyboard_check(ord("S"))) {y+=2;}
y=clamp(y,50,600);//keep player in fixed range
//angle control
var angle=y-midpoint;//get difference
image_angle=0-(angle/20);//slightly angle plane
We’ll come back to this event and add more code in the next section.
Create a new Instance Layer named player and put at the top in the room layers, as shown in Figure
A-27, and place an instance of
obj_player as shown. Now is a great point to save and test. You’ll be able to move your player around, but won’t be able to leave the room via the top or bottom.
Create Player Weapons
The player will have two type of weapons, a standard gun bullet that they can fire once every second, and a special weapon that can also fire once per second but the player has only a limited number available – though they can collect crates for extra ammo.
First we’ll create the standard bullet. You don’t need to resize this, but do set the origin as center. Create a sprite,
spr_player_bullet as shown in Figure
A-28.
Next create an object
obj_player_bullet, assign the sprite and add an Outside Room Event, which can be found, as shown in Figure
A-29.
Pop the following code into this event. This will destroy the instance when it’s outside the room, helping to prevent a memory leak:
/// @description Destroy
instance_destroy();
That’s all for this object.
Next create a new sprite
spr_player_special and load in the sprite as shown in Figure
A-30, resizing to 25x34 and setting the origin as center.
Create an object,
obj_player_special and assign this sprite, and also make an
Outside Room Event with the same code:
/// @description Destroy
instance_destroy();
That’s both weapons set up.
Next we’ll set the player up to shoot them.
Open up
obj_player, and change the
Create Event to that shown in the following. These additions add two flags that will be used to determine whether the player can shoot or not.
/// @description Set Up
midpoint=275;//set middle and starting point
y=midpoint;//start player in middle
can_shoot_bullet=true;//allow bullet shot
can_shoot_special=true;//allow special shot
Add an
Alarm 0 Event with
/// @description Allow shooting bullet
can_shoot_bullet=true;
And an
Alarm 1 Event with
can_shoot_special=true;//allow special shot
We’ll use a new layer,
bullets for spawning the bullets and special weapons, so create that now and order the layers as shown in Figure
A-31.
Next we’ll add a
Global Left Button Pressed Event to
obj_player, which can be found as shown in Figure
A-32.
Pop the following code into this event:
/// @description Shooting bullet control
if can_shoot_bullet
{
var bullet=instance_create_layer(x,y,"bullets",obj_player_bullet);//spawn the bullet
bullet.speed=4;//set the speed of bullet
bullet.direction=image_angle;//match direction to that of the plane
bullet.image_angle=image_angle;//make the bullet point to direction of movement
can_shoot_bullet=false;
alarm[0]=game_get_speed(gamespeed_fps);
global.shots_fired++;//update shot count
}
The use of var in the preceding block tells GameMaker that it is a local variable and only needed for that specific block of code.
Next create a
Global Right Button Pressed Event with the following code, which will check if the player can shoot, and check if there is ammo available. If available, it will spawn seven special bullets in a spread.
/// @description Special bullet control
if can_shoot_special && global.special_weapon>0
{
global.special_weapon--;//reduce available weapons
start_angle=-30;
can_shoot_special=false;//set flag so cant shoot again
alarm[1]=game_get_speed(gamespeed_fps);set alarm to allow shooting again
repeat(7)
{
var bullet=instance_create_layer(x,y,"bullets",obj_player_special);//spawn the bullet
bullet.speed=4;//set the speed of bullet
bullet.direction=image_angle-start_angle;//match direction to that of the plane
bullet.image_angle=image_angle-start_angle;//make the bullet point to direction of movement
start_angle+=10;
global.shots_fired++;//update shot count
}
}
Also add a Key X Pressed Event and put the following code into it, which will readd the number of special weapons available – you can use this for testing:
/// @description testing
global.special_weapon=5;
Create Bonuses
In this game there is one bonus that will replenish the player’s special weapon.
Create a new sprite spr_bonus and assign the sprite and set the origin as the center.
Create an object
obj_bonus and assign this sprite, as shown in Figure
A-33.
In a
Step Event pop the following:
/// @description rotate
image_angle+=2;
//destroy if off left of screen
if x<0-sprite_width
{
instance_destroy();
}
Next set up a
Collision Event with obj_player, as shown in Figure
A-34.
Next, we’ll make an object that spawns this item. Create an object
obj_spawn_bonus and put the following in the Create Event:
/// @description Set an alarm
alarm[0]=game_get_speed(gamespeed_fps)*8;set for 8 seconds
and the following in the
Alarm 0 Event. This will reset the alarm, and with a 1 in 4 chance will create a
bonus instance:
/// @description Spawning control
alarm[0]= game_get_speed(gamespeed_fps)*8;//reset for 8 seconds
var select=irandom(3);//choose either 0 1 2 or 3
if select=0// has a 1 in 4 chance of happening
{
varr xpos=room_width+sprite_width;//just off right of screen
var ypos=irandom_range(50,600);//random y position - in a range that player can move to
var bonus=instance_create_layer(xpos,ypos,"bullets",obj_bonus);
bonus.hspeed=random_range(-8,-3);//set movement within a range
}
Pop an instance of this on the room’s bullet layer, as shown in Figure
A-35.
Now is an ideal place to save and test your game. You’ll now be able to move, shoot, and collect the bonus for more special weapons.
Create Enemies
We’ll create three enemy types, each of which behaves in a slightly different manner with regards to movement and shooting of its weapons. We’ll create a separate object for each enemy to keep it simple. The enemies will share a bullet object.
First we’ll set up the sprites. We’ll use a different color sprite for each: red, orange, and blue.
Create a new sprite
spr_enemy_blue and import the sprite. Resize to 192x167 and set the origin as Middle Center, as shown in Figure
A-36.
As the
enemies will be coming from the right side of the screen, we’ll need to have the plane pointing to the left. Click Edit Image as shown in Figure
A-37.
Next click where shown in Figure
A-38 to mirror the image so it points to the left.
Next create new sprite
spr_enemy_red and load in the subimages, as shown in Figure
A-39.
Resize as 192x83 and set the origin as center.
Also edit this image so it points to the left.
The final sprite is
spr_enemy_orange. Import the subimages, resize as 192x80, set the origin as center and mirror the image to point to the left. When done, it will look like that in Figure
A-40.
Next we’ll set up the bullet for the enemies, obj_enemy_bullet.
Create this object and assign a bullet sprite
spr_enemy_bullet, as shown in Figure
A-41, pointing to the right and origin as the center.
Make an object, obj_enemy_bullet and assign this sprite.
Make an Outside Room Event and put this code in:
/// @description Destroy when outside
instance_destroy();
as shown in Figure
A-42.
We’ll set the first enemy to move across the screen from the right to the left and shoot bullets straight across the room.
Create an object obj_enemy_1 and assign the blue sprite spr_enemy_1.
In the
Create Event put
/// @description Start alarm for shooting && set health
alarm[0]=game_get_speed(gamespeed_fps)*6;
hp=4;
max_hp=hp;
hspeed=-1;
In an
Alarm 0 Event, pop in the code in the next block. This will reset the alarm and spawn a bullet, setting the image angle and direction:
/// @description Create bullet and restart alarm
alarm[0]=game_get_speed(gamespeed_fps)*3;
//create bullet and set direction, image angle and speed
var bullet=instance_create_layer(x,y,"bullets",obj_enemy_bullet);
bullet.direction=180;
bullet.image_angle=180;
bullet.speed=3;
We’ll also set up drawing a small healthbar above the instance so the player can see what hp the enemy has.
Make a
Draw Event and put in the following:
/// @description Draw self & healthbar
draw_self();
draw_healthbar(x-30,y-50,x+30,y-30,(100/max_hp)*hp,c_red,c_red,c_green,0,true,true);
We’ll also add a Step Event to keep track of hp of the enemy and destroy it if it has run out. This can be done with
/// @description Check hp
if hp<=0 instance_destroy();
When done, this enemy will look like that shown in Figure
A-43.
Next create the second enemy, obj_enemy_2, which has the sprite spr_enemy_red assigned. We’ll make this instance move into the room and then move up and down.
It has a
Create Event with
/// @description Start alarm for shooting && set health && set up moving
alarm[0]=game_get_speed(gamespeed_fps)*6;
hp=3;
max_hp=hp;
hspeed=-3;
active=false;
and a
Step Event with
/// @description HP & movement control
if hp<=0 instance_destroy();
//set as sctive if on screen at position
if x<1100 && active==false
{
hspeed=0;
vspeed=-3;
active=true;
}
//move direction if reached top area
if active && y<100
{
vspeed=3;
}
//move direction if reached bottom area
if active && y>600
{
vspeed=-3;
}
And an
Alarm 0 Event for spawning the bullet. This setup spawns the bullet that moves to the player’s plane position – meaning they will have to dodge the bullet.
/// @description Create bullet and restart alarm
alarm[0]=game_get_speed(gamespeed_fps)*3;
//create bullet and set direction, image angle and speed
var bullet=instance_create_layer(x,y,"bullets",obj_enemy_bullet);
var dir=point_direction(x,y,obj_player.x,obj_player.y);
bullet.direction=dir;
bullet.image_angle=dir;
bullet.speed=3;Fixe
And finally, a
Draw Event to draw the sprite and healthbar:
/// @description Draw self & healthbar
draw_self();
draw_healthbar(x-30,y-50,x+30,y-30,(100/max_hp)*hp,c_red,c_red,c_green,0,true,true);
The third and final enemy plane will move in while following a path, move around a bit and then leave the window and then destroy itself.
Let’s create a path
path_enemy_3, as shown in Figure
A-44.
Next create a new path layer in the room, you can name it
enemy_path as shown in Figure
A-45.
Next create a path that looks something like that shown in Figure
A-46. Note, in the settings on the left, that the selected path is
path_enemy_3 and set as a smooth curve.
Next create the last enemy object,
obj_enemy_3 and set the sprite
spr_enemy_orange. In the
Create Event, you set up the alarm for shooting, set the hp, and start on the path you just created.
/// @description Start alarm for shooting && set health
alarm[0]= game_get_speed(gamespeed_fps)*3;
hp=4;
max_hp=hp;
path_start(path_enemy_3,4,path_action_stop,true);
In a
Step Event, put the following so the game destroys the instance when it runs out its health
hp:
/// @description HP & movement control
if hp<=0 instance_destroy();
Make an
Alarm 0 Event with the following code. This resets the alarm and creates a spread of bullets:
/// @description Create bullet and restart alarm
alarm[0]= game_get_speed(gamespeed_fps)5;
//create bullet and set direction, image angle and speed
var start_angle=-20;
repeat(5)
{
Var bullet=instance_create_layer(x,y,"bullets",obj_enemy_bullet);//spawn the bullet
bullet.speed=4;//set the speed of bullet
bullet.direction=image_angle-start_angle+180;//match direction to that of the plane
bullet.image_angle=image_angle-start_angle+180;//make the bullet point to direction of movement
start_angle+=10;
}
We’ll also add a
Path Ended Event, which can be found, where shown in Figure
A-47.
Pop the following code into this event:
/// @description Destroy on path end
instance_destroy();
As before, pop the following into a
Draw Event:
/// @description Draw self & healthbar
draw_self();
draw_healthbar(x-30,y-50,x+30,y-30,(100/max_hp)*hp,c_red,c_red,c_green,0,true,true);
Create Enemy Spawning System
You now require some kind of system to spawn the enemies.
This first thing that may come to mind is to use the choose function to select a random object to spawn, something like
to_spawn=choose(obj_enemy_1,obj_enemy_2,obj_enemy_3);
The problem with this approach is that it may not spawn instances in a balanced way, for example, there could five instances of obj_enemy_1 spawned in succession.
Another method that we’ll use is to use a ds_list to store the objects, shuffle the order, and pick the top value, making a note of it, removing it from the list, and then spawning that object. This keeps an even distribution of the planes.
To keep this tidy, we’ll create a function that can be called. You’ll see in the following that we get the current size of the list, and repopulate if empty, then it will make a selection. Add this function now, as shown in Figure
A-48.
//Function spawn enemy
function spawn_enemy()
{
//check size of current list
//if empty repopulate with planes
if ds_list_size(global.enemy_list)==0
{
repeat(6)
{
//add to list
ds_list_add(global.enemy_list,obj_enemy_1,obj_enemy_2,obj_enemy_3);
}
//mix them up so random order
ds_list_shuffle(global.enemy_list);
}
///grab the top element
var to_spawn=global.enemy_list[|0];
//remove from list
ds_list_delete(global.enemy_list,0);
//spawn this enemy
plane=instance_create_layer(room_width+140,irandom_range(50,600),"enemy",to_spawn);
}
Next set up an object that will be used to call this function,
obj_spawn_enemy. In a
Create Event, put
/// @description Set initial alarm
alarm[0]= game_get_speed(gamespeed_fps);
and an
Alarm 0 Event with
/// @description Call script and reset alarm
spawn_enemy();
alarm[0]= game_get_speed(gamespeed_fps)*5;
Pop an instance of this in the room, where shown in Figure
A-49.
Now is an ideal time to save and test your game. You’ll now have enemies appearing that you can shoot at.
Collisions, Effects, and Audio
The next stage is to add some interaction between the objects, and do things like increasing the score, creating explosions and effects. I generally do this as the last thing in a game’s creation, as I now know what all the objects are, what they do, and how they’ll interact with each other.
First we’ll set up some audio, create the sounds in the following list, choosing what you think is a suitable sound from the
Collection By SubSpaceAudio sub
folder and from the
YoYo Games folder.
snd_bonus_pick_up
snd_enemy_bullet
snd_enemy_bullet_big
snd_enemy_hit
snd_enemy_is_dead
snd_hit_water
snd_menu_click
snd_menu_hover
snd_menu_leave
snd_metal
snd_no_ammo
snd_player_bullet
snd_player_hit
snd_player_special
We’ll need one additional object that you’ll use to create an effect when a bullet (player’s or enemy’s) hits the water.
Create an object
obj_water_effect and assign a sprite
spr_water_effect assigning the sprite subimages, as shown in Figure
A-50, this time we’ll set the origin as Bottom Center.
It has a Create Event with the following code. This makes it move to the left at the same speed as the
near background layer. If you didn’t do this, it would just look weird.
/// @description Start moving to match parallax effect
hspeed=-3;
It also has an
Animation End Event, which can be found, as shown in Figure
A-51.
Add the following block of code, which will destroy the instance once it has animated through all subimages once.
/// @description destroy
instance_destroy();
Let’s now start adding in the sounds, effects, and collisions to the game.
We’ll start by adding some audio interaction to the menu buttons.
Create a new object,
obj_menu_parent. It does not need a sprite assigned to. Make a
Mouse Enter Event, as shown in Figure
A-52.
Pop in the following code, which will play the given each time the mouse cursor enters the button area:
/// @description play a sound
audio_play_sound(snd_menu_hover,1,false);
Next we’ll do something similar, create a Mouse Leave Event, which is below the Mouse Enter Event shown back in Figure A-52.
The code for this Event is
/// @description play a sound
audio_play_sound(snd_menu_leave,1,false);
The final event is a Mouse Left Pressed Event, which can be located, as shown in Figure
A-53.
We’ll also add some code that will play a sound when the mouse clicks over the object. Pop in the following into this
Mouse Left Pressed Event:
/// @description play a sound
audio_play_sound(snd_menu_click,1,false);
That’s all for this object. Now you need to assign this as a Parent of the menu buttons.
Open up
obj_menu_play, and click
Parent then select the object
obj_menu_parent that you just created, as shown in the Figure
A-54.
Repeat the exact same process for object obj_menu_info and obj_menu_quit.
That’s all the parents set. If you now reopen
obj_menu_parent, so you’ll be able to see it’s children objects that it is the parent of, as shown in Figure
A-55.
Now is a great point to save and test. You’ll now have a functioning menu, and some basic effects.
Next we’ll set up enemy planes to have a parent object. Create a new object
obj_enemy_parent. Then open each of the three
enemy objects,
obj_enemy_1,
obj_enemy_2, and
obj_enemy_3 have this as their parent. This is the same process as you just did. When done,
obj_enemy_parent will have its children as shown in Figure
A-56.
Next we’ll repeat this process by creating a parent object for the player’s bullet objects.
Create a new object obj_player_bullet_parent. Open up obj_player_bullet and set the parent object, then do the same with obj_player_special.
When done,
obj_player_bullet_parent will look like that shown in Figure
A-57.
Next we’ll set up the enemy being shot by the player. Open up the parent object,
obj_enemy_parent and make a
Collision Event with obj_player_bullet_parent, as shown in Figure
A-58. This event will be triggered when any enemy is hit by any player projectile – all nice and tidy in one place, thanks to the power of parents.
Pop in the following code. This will increase the player’s hit tally, destroy the colliding projectile instance, create an effect, play a sound, increase the score, and reduce the hp. It will then check the hp value to see if the plane is dead, if it is dead, more points are added, a sound is played, and a few effects created, then destroy the instance.
/// @description Hit control
global.hits++;//increase hit count
effect_create_above(ef_explosion,x,y,5,c_red);
audio_play_sound(snd_enemy_hit,1,false);
score++;//increase the player's score
with (other) instance_destroy();//destroy colliding bullet
hp--;//reduce the enemies hp
//check hp and react accordingly
if hp<=0
{
global.kills++;//increase kill count
score+=10;//add some more points;
audio_play_sound(snd_enemy_is_dead,1,false);//play an additional sound
//create some more effects
effect_create_above(ef_smoke,x-20,y+20,5,c_yellow);
effect_create_above(ef_smoke,x-20,y-20,5,c_yellow);
effect_create_above(ef_smoke,x+20,y+20,5,c_yellow);
effect_create_above(ef_smoke,x+20,y-20,5,c_yellow);
instance_destroy();//destroy self
}
That’s all for this object.
Next open up
obj_player and make a
Collision Event with obj_enemy_bullet, and pop in this code:
/// @description Hit by enemy bullet
with (other) instance_destroy();//destroy colliding bullet
health-=5;//reduce player's health
audio_play_sound(snd_player_hit,1,false);
effect_create_above(ef_smokeup,x,y,5,c_red);//create an effect
When done, this will look like Figure
A-59.
Next open up
obj_enemy_bullet and add a
Collision Event with obj_player_bullet_parent with the following code:
/// @description hit control
audio_play_sound(snd_metal,1,false);
//calculate mid point between this and colliding instance
var pos_x=(x+other.x)/2;
var pos_y=(y+other.y)/2;
effect_create_above(ef_ring,pos_x,pos_y,5,c_gray);//do effect
//destroy self & colliding instance
with (other) instance_destroy();
instance_destroy();
When added in, it will look like that shown in Figure
A-60.
That’s all for this object.
Next we’ll make an effect for when a player’s projectile hits the water.
Open
obj_water and make a
Collision Event with obj_player_bullet_parent, with the following code:
/// @description water
instance_create_layer(other.x,700,"water",obj_water_effect);
with (other) instance_destroy();//destroy the bullet
which when added will look like that shown in Figure
A-61.
That’s all for this object.
Open up object
obj_enemy_1 and change the
Alarm 0 Event code to the following, then add an additional line of code to play a sound when an enemy bullet is created.
/// @description Create bullet and restart alarm
alarm[0]=game_get_speed(gamespeed_fps)*3;
//create bullet and set direction, image angle and speed
bullet=instance_create_layer(x,y,"bullets",obj_enemy_bullet);
bullet.direction=180;
bullet.image_angle=180;
bullet.speed=3;
audio_play_sound(snd_enemy_bullet,1,false);
Also open up
obj_enemy_2 and change the
Alarm 0 Event, again adding an extra line to play a sound:
/// @description Create bullet and restart alarm
alarm[0]=game_get_speed(gamespeed_fps)*3;
//create bullet and set direction, image angle and speed
var bullet=instance_create_layer(x,y,"bullets",obj_enemy_bullet);
var dir=point_direction(x,y,obj_player.x,obj_player.y);
bullet.direction=dir;
bullet.image_angle=dir;
bullet.speed=3;
audio_play_sound(snd_enemy_bullet,1,false);
Finally open up object
obj_enemy_3’s
Alarm 0 Event, and change it to that shown as follows, which will play a different sound when enemy creates it’s volley of bullets:
/// @description Create bullet and restart alarm
alarm[0]=game_get_speed(gamespeed_fps)*5;
//create bullet and set direction, image angle and speed
var start_angle=-20;
repeat(5)
{
var bullet=instance_create_layer(x,y,"bullets",obj_enemy_bullet);//spawn the bullet
bullet.speed=4;//set the speed of bullet
bullet.direction=image_angle-start_angle+180;//match direction to that of the plane
bullet.image_angle=image_angle-start_angle+180;//make the bullet point to direction of movement
start_angle+=10;
}
audio_play_sound(snd_enemy_bullet_big,1,false);
The final object to update for audio is
obj_player. Open the
Global Left Mouse Button Pressed Event and change it so it also plays the bullet sound when it fires a bullet:
/// @description Shooting bullet control
if can_shoot_bullet
{
bullet=instance_create_layer(x,y,"bullets",obj_player_bullet);//spawn the bullet
bullet.speed=4;//set the speed of bullet
bullet.direction=image_angle;//match direction to that of the plane
bullet.image_angle=image_angle;//make the bullet point to direction of movement
can_shoot_bullet=false;
alarm[0]= game_get_speed(gamespeed_fps);
global.shots_fired++;//update shot count
audio_play_sound(snd_player_bullet,1,false);
}
And then open up the
Global Right Mouse Button Pressed Event and set the code so that it first checks whether the player can shoot and, if no ammo available, it plays a small no ammo sound. It will then spawn the bullets and play the ammo sound if the player has bullets available to shoot.
/// @description Special bullet control
if can_shoot_special && global.special_weapon<00
{
audio_play_sound(snd_no_ammo,1,false);
}
if can_shoot_special && global.special_weapon>0
{
audio_play_sound(snd_player_special,1,false);
global.special_weapon--;//reduce available weapons
var start_angle=-30;
can_shoot_special=false;//set flag so cant shoot again
alarm[1]= game_get_speed(gamespeed_fps);//set alarm to allow shooting again
repeat(7)
{
var bullet=instance_create_layer(x,y,"bullets",obj_player_special);//spawn the bullet
bullet.speed=4;//set the speed of bullet
bullet.direction=image_angle-start_angle;//match direction to that of the plane
bullet.image_angle=image_angle-start_angle;//make the bullet point to direction of movement
start_angle+=10;
global.shots_fired++;//update shot count
}
}
Finally open up
obj_bonus and its
Collision Event code to
/// @description Reset special weapon
global.special_weapon=5;
audio_play_sound(snd_bonus_pick_up,1,false);
instance_destroy();
Now is an ideal point to save and test the game so far. You now have a functioning weapon system and some audio effects when shooting.
So go ahead and play the game name and check that everything is working as planned and expected.
Check if
Menu buttons all work and interact with the mouse as they should.
Player can fire projectiles (if available).
Enemy’s flying in the pattern they should.
Enemy’s bullet spawn and point in the right direction.
Sound effects are played when they should.
Effects spawn in the correct position.
Enemies can lose hp when hit by player’s projectiles.
Player can lose health, lives, and go to gameover room when out of lives.
Gameover room shows player stats.
Game difficulty is set at right level to make it fun, yet challenging.
You should find that everything is correct and working as expected, except the final item. You’ll find that it spawns the enemies far too quickly, so let’s correct that now.
Open up object
obj_spawn_enemy and change its
Alarm 0 Event so it spawns a plane every 14 seconds, as shown in the following:
/// @description Call script and reset alarm
spawn_enemy();
alarm[0]= game_get_speed(gamespeed_fps)*14;
If you save and test again, you’ll find it’s more manageable to play.
Congratulations – You’ve Just Made Your First Game!