Monday 11 April 2016

Assessment 2: 3 in 1 gaming

Space Invaders clone game


Sprites:

These are the sprites in which I have created for the game. All of the sprites vary in sizes due to the mechanics of the game. For example the spr_earth is very large as it is displayed at the beginning of the game and it covers most of the bottom of the window. The player lives are smaller than the player sprite as they are placed in the bottom left corner of the screen and there are more of the same image of the unicorn than the player.


Sounds:


This screenshot displays all of the sounds in which I have created for the game. There are 4 sound effects and 3 pieces of music altogether. The file names simply explain what they are used for. There are 3 variations to the "Boss Grunt" sound. The reason for this is so that when the player hits the boss with a bullet, the boss doesn't always make the same sound. There are also 3 music tracks which adds variety to the game.

Paths:

There is only one path I have made for this game. The reason behind why I have made this path is so that the boss has an animation. It makes it looks a bit more interesting instead of it just bouncing around the room at the end. The screenshots show what the paths name is and what the path looks like. In the video later on it will show you what the boss looks like when it follows the path.



Objects:

This screenshot displays all of the objects that I have used within my game. The objects with the blank white squares are objects which cannot be seen in game however, they run code in the background which adds lots of features to the game. For example, the controller runs through all of the variables in game which are then used in the other objects, the obj_menu object runs code which brings up the menu in the game, the one you see at the start of the game and the one you see in the pause menu and the obj_starfield object makes the animated background you see appear in every room of the game. This objects adds a nice visual feature to the game.


Obj_Player:

Here is a screenshot of all the events which are contained within the player object. The create event sets out a variable called "bulletdelay" to 0 and "image_speed" to 0.05. The player sprite has a certain number of images in the image index to appear animated in game so this was a suitable speed for the image index to play through. The bulletdelay variable is used in the alarm event. In the alarm event is one line of code which is "bulletdelay = 0". In the space key event is a few lines of code.

Space Key Event

if (keyboard_check_pressed(vk_space) && (bulletdelay = 0)) && (room != menu) then
    {
        instance_create(x,y,obj_player_bullet)
        bulletdelay = 1
        alarm[0] = 30
    }

What this is doing is checking whether the player has pressed the spacebar, the bulletdelay is set to 0 and checking whether they are still in the menu, if they are not in the menu then what happens is a bullet will spawn, then bulletdelay will be changed to 1, acting like a switch, and then an alarm is activated. This alarm is the delay.

Left and Right Key event

if keyboard_check(vk_left) && room != menu
{
x -= 4
}

if keyboard_check(vk_right) && room != menu
{
x += 4
}

This code checks whether the left or right arrow key has been pressed on the keyboard and the player is not on the menu then it either moves to the left at the speed of 4 pixels per script run.

Collision Event (Enemy):

if global.dead = true { 
    lives = -1
    effect_create_above(ef_explosion, x, y, 2, c_red);
    instance_destroy()
    if lives < 0 {
        global.myscore = score
        show_message("You let the asteroids invade Earth!")
        room_goto(hiscore)
    }
}
}  

If the player collides with an enemy (so the enemy has reached near the bottom of the screen) then the player dies. When the player is dead, it creates an explosion effect and takes you to the "hiscore" room.

Collision Event (Bullet):

In this event is a drag and drop "Destroy the instance" with "other" selected. What this does is destroys the obj_bullet.

Collision Event (Wall):

In this event is a drag and drop "Bounce against solid objects". The reason behind why I have done this is so that the obj_player is not able to go through the wall.


Obj_enemy:

Here is a screenshot displaying the events within the enemy object.











Create Event:
In the create event are two actions. The first action is a code action containing:

image_speed = 0.45

if room = menu then {
    global.enemyspeed = 0
    global.enemyshoot = 0
}


If the player is on the menu then the enemy doesn't move or shoot.

The second action is a drag and drop "Start moving in a direction" with the speed being "global.enemyspeed". This variable is a pre-set variable made in the controller event.

Step Event:

if irandom(global.enemyshoot) = 1 && (room != menu) then {
    instance_create(self.x, self.y - 5, obj_bullet)
}

If 1 is chosen out of 2001 (global.enemyshoot for room0 (the first room we go in) and the room is not the menu room then a bullet spawns in where the enemy is.

Collision Event (Wall):

There are two actions within this event, "Reverse horizontal direction" and "Jump to position(0,10). What these do is check to see whether the enemy has made contact with the wall, if it has then it reverses direction horizontally and moves down as it hits the wall.

obj_player_bullet:

Here is a screenshot which displays the 4 events used within the player bullet object. I am going to talk about three of these events fully and the create event briefly. All the create event does is set the vertical speed of the bullet and contains one line of code which tells the game to play a sound when the bullet is shot.



Collision Event (Enemy):

Within the collision event for the enemy are 4 drag and drops, "Set the score relative to 10" which adds 10 score everytime the bullet collides with the enemy, "Destroy the instance" which destroy the player bullet, "Destroy the instance" which destroys the enemy and "Create a firework at (0,0)" which creates and effect in the place where the enemy was destroyed.

Collision Event (MiniBoss):
This collision event is with the mini boss which spawns in the last two rooms before the player is confronted with the main boss. In this event is one action, code.

global.minihealth -= 5
score += 2
instance_destroy()

if global.minihealth <= 0 then
    {
        with obj_miniboss instance_destroy()
    }
   
What this code does is, when the player bullet comes in contact with the miniboss, the miniboss loses 5 health, the player gains 2 points added on to their score and the bullet is destroyed. The if statement checks whether the miniboss health is equal to 0 and if it is then miniboss is destroyed.

Collision Event (Boss):


if global.bossmoving = 1 then {
if irandom(25) = 0 then {
    health -= 10
    score += 15
    instance_destroy()
} else {
    health -= 1
    score += 2
    instance_destroy()
}
} else if global.bossmoving = 0 then {
    instance_destroy()
}

The first if statement checks whether the boss is still following the path made for it previously, if it is then the bullet is just destroyed when it hits the boss without it losing any health. If the boss is not following the path anymore then when the player bullet hits the boss, it has a 1 in 26 chance of a "Critical hit", this dealing 10 damage ( taking 10 health of the boss) and adding 15 score to the players score, every other time the random number chosen is not 0 then when the player bullet hits the boss it deal 1 damage and gives the player 2 score.


var hitsound = irandom(2) // 0, 1 or 2 (3 sounds)

switch(hitsound) {
    case 0:
        audio_play_sound(BossGrunt1, 0, 0)
    break
    case 1:
        audio_play_sound(BossGrunt2, 0, 0)
    break
    case 2:
        audio_play_sound(BossGrunt3, 0, 0)
    break
}

This section of code focuses purely on the sound effects. A random number is chosen, 0, 1 or 2. If the number 1 is chosen, it will go through the switch statement until it reaches case 1, then it will play that sound effect.

Obj_Boss:

Within the boss are 4 events, create, step, collision and an other event. Within the create event are two actions, code and setting a path drag and drop. The code action contains two variables set to 0 and the path drag and drop contains which path is selected for the object to follow and which speed it follows it.  




Step Event:

if global.bossmoving = 1 then {
    if irandom(9) = 1 motion_set(random(360), 1 + random(3))
    if irandom(50/global.runthrough) = 1 then {
        instance_create(self.x, self.y - 5, obj_bullet)
    }
}

If the boss is not following the path anymore then, if 1 is chosen from 0 - 9, the boss has a chance to move in any direction which is chosen randomly from 0 - 360 (in this circumstance would be a full circle) and then the speed is chosen at what rate it moves at by picking a number from 0 to 3. The reason why the "1 +" is put in just before it picks a number is so that if the number chosen was 0 then 1 would be added which means it will always be moving and not stationary. The next if statement decides on when the bullet should be fired. If the number 1 is chosen out of 0 to 50 then it creates a bullet. The global.runthrough is a variable which is used when the player has gone through all the room and defeated the boss and it lets the player go through all the rooms again but is harder. Global.runthrough at first would = 1 and then through the second runthrough it will = 2. With this piece of code above, when it is the second runthrough of the levels, the boss will have double the amount of chance of firing a bullet than before.


Obj_ Controller:

There are 3 events within the controller object. The first event which is the create event contains only one action which is to execute a piece of code.








Create Event:

if room != menu && room != hiscore {
    instance_create(room_width/2, 560, obj_player)
    global.enemyspeed = 0
    global.enemyshoot = 0
}

if room = room0 then {
    global.enemyspeed = 0.5*(global.runthrough)
    global.enemyshoot = 2000 // 1 in 2001 chance to shoot
}

if room = room1 then {
    global.enemyspeed = 1*(global.runthrough)
    global.enemyshoot = 1500 // 1 in 1501 chance to shoot
}
if room = room2 then {
    global.enemyspeed = 1.5*(global.runthrough)
    global.enemyshoot = 1000 // 1 in 1001 chance to shoot
}
if room = room3 then {
    global.enemyspeed = 2*(global.runthrough)
    global.enemyshoot = 750 // 1 in 751 chance to shoot
}


What this large section of code does is decides on how difficult the each room is. You can see that it gets progressively difficult due to the higher the room number the higher the chance it is for the enemy to shoot a bullet. Furthermore, the enemyspeed is increased by the number (for example 1 from room1) times by the global.runthrough (for example, 2nd runthrough = 2), the speed of enemy for room 1 would be then be 2.
Step Event:

if audio_is_playing(InGameMusic) && room = boss then {
    audio_stop_all()
    audio_play_sound(BossMusic, 0, 200)
    audio_sound_gain(BossMusic, global.musicvol, 1)
}

if audio_is_playing(BossMusic) && room != boss then {
    audio_stop_all()
    audio_play_sound(InGameMusic, 0, 200)
    audio_sound_gain(InGameMusic, global.musicvol, 1)
}


This section of code above tells the game when to play a certain music track or not. For example, the first if statement checks to see whether the InGameMusic is playing and checking whether the player is in the boss room. If the player is in the boss room and the InGameMusic is playing then it stops all music together. Then it starts to play the boss music instead. The second if statement follows a similar pattern.

if (instance_exists(obj_miniboss) = 0) && (instance_exists(obj_enemy) = 0) && (room != menu) && (room != boss) && (room != hiscore) then {
    room_goto_next()
}


This next section of code checks to see whether any of the enemies have been left in the room, if some of them are still left and the player is not in the hiscore room, menu or boss room then the player gets automatically moved to the next room.


if keyboard_check_pressed(vk_escape) then {
    instance_create(0, 64, obj_menu)
    audio_stop_all()
    audio_play_sound(MenuMusic, 0, 200)
    audio_sound_gain(MenuMusic, global.musicvol, 400)
    with obj_menu instance_deactivate_all(true)
}

This last section of code checks whether "ESC" has been pressed, if it has then it stops all of the music and then plays the menu music. Furthermore, when the menu is up it deactivates all objects except the menu.

Draw Event:

There are 2 actions within the draw event in the controller, "Draw the lives as image" which displays the amount of lives I want on screen as the spr_lives, "Execute a piece of code" which contains code on the colour of the score font and displays the word "score" in the chosen font.

Hiscore_room:


Here is a screenshot which shows the 3 events contained within the hiscore object. These 3 event help to display the hiscore at the end of the game and saves the previous hiscores obtained by players.







Create Event:

scoredraw = 0
get_string_async("Enter your name...", "Noob")

This section of code brings up the user input box with the message "Enter your name..." and the default text within the input box is "Noob".

Dialog Event:


highscore_add(string(ds_map_find_value(async_load, "result")), global.myscore)

This section of code above is basically saving your score and name.

for (k = 1; k <= 5; k+=1) {
    names[k] = highscore_name(k)
    scores[k] = highscore_value(k)
   
    if (names[k] = "<nobody>") then {
        names[k] = "--"
        scores[k] = "--"
    }
}

scoredraw = 1

This section of code takes what was already saved in the hiscore leaderboards and puts it as a local variable and the last section just replaces the word "nobody" to a "--".


Draw Event:

The draw event in this object contains 3 actions. These 3 actions all work well with one another to display the hiscores in the hiscore room.


Boss Controller:


This object is for the final boss in the last room. It contains 3 events. The create event holds one event which gives the boss a health bar of 100.







Step Event:

if (health <= 0) then {
    with obj_boss instance_destroy()
    room_goto(room0)
    global.runthrough = 2
    global.myscore = score
}


This if statement checks whether the boss health is equal to or less than 0, if it is then the boss is destroyed, the player can run through all the levels again but this time a bit harder. The global.runthrough is now changed to 2 which is like changing the difficulty setting in a game.

Draw Event:

if (global.drawhealth = 1) then {
    draw_healthbar((room_width/2)-256, 50, (room_width/2)+256, 70, health, c_black, c_red, c_green, 180, true, true)
}



All this section of code does is draws the health bar in a certain position in the room and with the colours that I wanted it to use.

Obj_menu:

The object menu has 3 events inside, create, step and draw. Within the Create event is one action, "execute a piece of code".







Create Event:

if room = menu {
    global.play = 0
    global.bulletdelay = 1
    global.options = 0
    global.runthrough = 1
    menuSelPos = 0
    score = 0
    global.myscore = 0
    global.playerspeed = 4
    global.dead = false
    lives = 3
    global.musicvol = 0.5
    drawres = 0
    drawvol = 0
    global.minihealth = 50
} else if room != menu {
    global.bulletdelay = 0
    global.options = 0
    menuSelPos = 0
}

audio_play_sound(MenuMusic, 0, 200)
audio_sound_gain(MenuMusic, global.musicvol, 1)


The reason behind why all the variables are defined within this section is so that, when the player goes through all of the levels again, the variables are not reset. For example; if lives = 3 was actually in room1 and then carried out through all the rooms, when the player goes back to room1 they will have 3 lives again. The audio section at the bottom just simply tells the game to play a certain track when obj_menu is open.
MenuSelPos = The top position. Example; Play or Fullscreen.
Global.runthrough = First runthrough = * 1 , Second runthrough = * 2

Step Event:

if keyboard_check_pressed(vk_up) && menuSelPos > 0 {
    menuSelPos -= 1
} else if keyboard_check_pressed(vk_up) && menuSelPos = 0 {
    menuSelPos = 3
}
if keyboard_check_pressed(vk_down) && menuSelPos < 3 {
    menuSelPos += 1
} else if keyboard_check_pressed(vk_down) && menuSelPos = 3 {
    menuSelPos = 0
}

This section above is how I managed to make the player be able to move the menu selector cursor up and down.

// Menu selections
if global.options = 0 then {
if keyboard_check_pressed(vk_enter) {
switch(menuSelPos) {
    case 0:
        if room = menu then {
            global.play = 1
            audio_sound_gain(MenuMusic, 0, 400) // Fade menu music
            if room != boss then {
                audio_play_sound(InGameMusic, 0, 200)
                audio_sound_gain(InGameMusic, global.musicvol, 1)
            } else if room = boss then {
                audio_play_sound(BossMusic, 0, 200)
                audio_sound_gain(BossMusic, global.musicvol, 1)
            }
            instance_destroy()
        }
    break
    case 1:
        if room != menu then {
            instance_activate_all()
            audio_sound_gain(MenuMusic, 0, 400) // Fade menu music
            if room != boss then {
                audio_play_sound(InGameMusic, 0, 200)
                audio_sound_gain(InGameMusic, global.musicvol, 1)
            } else if room = boss then {
                audio_play_sound(BossMusic, 0, 200)
                audio_sound_gain(BossMusic, global.musicvol, 1)
            }
            instance_destroy()
        }
    break
    case 2:
        global.options = 1
    break
    case 3:
        game_end()
    break
}
}

This section of code above contains a switch statement. The switch statement contains 4 cases, each case represents a section on the menu, Case 0 = play, Case 1 = continue, Case 2 = options and Case 3 = quit. When case 1 is selected then music is stopped and then another music track is played (the in game music track), the second case follows a similar pattern. If Case 2 is chosen then global.options is turned on which is another menu and when Case 3 is chosen then the game stops running.

}
// Menu-Options selections
if global.options = 1 then {
if keyboard_check_pressed(vk_enter) {
switch(menuSelPos) {
    case 0:
        if window_get_fullscreen() = true then {
            window_set_fullscreen(false)
        } else {
            window_set_fullscreen(true)
        }
    break
    case 3:
        global.options = 0
    break
}
}
switch(menuSelPos) {
    case 0:
        drawres = 0
        drawvol = 0
    break
    case 1:
        drawres = 1
        drawvol = 0
        if keyboard_check_pressed(vk_right)
    break
    case 2:
        drawres = 0
        drawvol = 1
        if keyboard_check_pressed(vk_right) && audio_get_master_gain(0) < 1 {
            audio_set_master_gain(0, audio_get_master_gain(0) + 0.1)
        }
        if keyboard_check_pressed(vk_left) && audio_get_master_gain(0) > 0 {
            audio_set_master_gain(0, audio_get_master_gain(0) - 0.1)
        }
    break
    case 3:
        drawres = 0
        drawvol = 0
    break
}
}

This last section of the code is another switch statement similar to the one further above. It contains cases in which allows the player to choose what they want. However, with this options menu, the code contains 2 switch statements but they are the same. One switch statement requires you to press enter which is the first switch statement for Case 1 (Which is allowing the player to select fullscreen when pressing enter). Then the second switch statement is a switch statement which doesn't allow you to press enter on any of options (you don't need to press enter to access any of the cases). The only case which does something in the second switch statement is 2. What this section of code lets the player do is, when they press left or right on the keyboard, the volume either decreases or increases in volume, 0.1 = 10% and 1 = 100%.


Draw Event:

For the draw event, it contains two 1D arrays. The first 1D array is for the main menu and the 2nd 1D array is for the option menu. The rest of the code which is contained within the draw event is deciding on what colour font the words are going to be in the menu. The colours are either white or grey, grey meaning they cannot be used.


Rooms:


Here are the rooms in which I have created for the game. There are 4 rooms which contain the smaller enemies and mini boss then there is a boss/final room. Once the player has defeated the final boss they are able to go back through the levels again at a higher difficulty. The hiscore and menu rooms are bonus rooms to the game.






































No comments:

Post a Comment