Create an exit button on the main game menu scene

In this article we will continue with our previous pygame project by creating an exit button on the main game menu scene which when the user clicks on it the program will close the game. We will create the score scene in the next chapter as I have promised in the previous article but in this article we will create the exit button which is also very important for this project. This button will only appear on the main scene. First we will need to edit the start scene class by including this button in it.

from BgSprite import BgSprite
from GameSprite import GameSprite
from pygame.locals import *
from pygame import math as mt
import pygame

class StartScene(object):

    def __init__(self, scene):
        self.scene = scene
        self.play_button = 'Asset/play.png'
        self.about_button  = 'Asset/about.png'
        self.exit_button = 'Asset/exit.png'
        self.score_button = 'Asset/score.png'
        self.home_button = 'Asset/back.png'
        self.button_image = 'Asset/button_play.png'
        self.manual_button = 'Asset/manual.png'
        self.bg_image = 'Asset/start.png'
        self.over_image = 'Asset/ove.png'
        self.next_image = 'Asset/next.png'
        self.win_image = 'Asset/winn.png'
        self.general_image = 'Asset/general.png'
        self.about_page = 'Asset/about_page.png'
        self.manual_page = 'Asset/manual_page.png'
        self.soundon = 'Asset/sound.png'
        self.soundoff = 'Asset/soundoff.png'
        self.home_button_image = 'Asset/home.png'
        self.bg_rect = Rect(0, 0, 660, 660)
        self.button_rect = Rect(0, 0,  306, 112)
        self.home_button_rect = Rect(0, 0, 200, 53)
        self.back_button_rect = Rect(0, 0, 40, 30)
        self.sound_button_rect = Rect(0, 0, 29, 30)

        self.sprite = BgSprite(self.bg_image, self.bg_rect)
        self.sprite_about = BgSprite(self.about_page, self.bg_rect)
        self.sprite_manual = BgSprite(self.manual_page, self.bg_rect)
        self.sprite_win = BgSprite(self.win_image, self.bg_rect)
        self.sprite_over = BgSprite(self.over_image, self.bg_rect)
        self.sprite_next = BgSprite(self.next_image, self.bg_rect)
        self.sprite_pause = BgSprite(self.general_image, self.bg_rect)

        self.soundon_button = GameSprite(self.soundon, self.sound_button_rect)
        self.soundoff_button = GameSprite(self.soundoff, self.sound_button_rect)

        self.soundoff_button_surface = self.soundoff_button.getImage()  # get the button sprite surface
        self.soundon_button_surface = self.soundon_button.getImage()  # get the button sprite surface

        self.sprite_button = GameSprite(self.button_image, self.button_rect)
        self.sprite_home_pause_button = GameSprite(self.home_button_image, self.home_button_rect)
        self.sprite_manual_button = GameSprite(self.manual_button, self.home_button_rect)
        self.sprite_exit_button = GameSprite(self.exit_button, self.home_button_rect)
        self.sprite_play_button = GameSprite(self.play_button, self.home_button_rect)
        self.sprite_about_button = GameSprite(self.about_button, self.home_button_rect)
        self.sprite_score_button = GameSprite(self.score_button, self.home_button_rect)
        self.sprite_home_button = GameSprite(self.home_button, self.back_button_rect)
        self.about_surface = self.sprite_about.getImage()  # get the about sprite surface
        self.manual_surface = self.sprite_manual.getImage()  # get the manual sprite surface
        self.win_surface = self.sprite_win.getImage()  # get the win sprite surface
        self.next_surface = self.sprite_next.getImage()  # get the next level sprite surface
        self.over_surface = self.sprite_over.getImage()  # get the game over sprite surface
        self.pause_surface = self.sprite_pause.getImage()  # get the pause sprite surface
        self.surface = self.sprite.getImage()  # get the start scene sprite surface
        self.button_surface = self.sprite_button.getImage() # get the button sprite surface
        self.home_pause_button_surface = self.sprite_home_pause_button.getImage() # get the button sprite surface
        self.play_button_surface = self.sprite_play_button.getImage()  # get the button sprite surface
        self.about_button_surface = self.sprite_about_button.getImage()  # get the button sprite surface
        self.score_button_surface = self.sprite_score_button.getImage()  # get the button sprite surface
        self.manual_button_surface = self.sprite_manual_button.getImage()  # get the button sprite surface
        self.home_button_surface = self.sprite_home_button.getImage()  # get the button sprite surface
        self.exit_button_surface = self.sprite_exit_button.getImage()  # get the button sprite surface
        self.draw_pos = mt.Vector2(0, 0)
        self.draw_button_pos = mt.Vector2(177, 274)
        self.draw_play_button_pos = mt.Vector2(229, 200)
        self.draw_about_button_pos = mt.Vector2(229, 263)
        self.draw_home_button_pos = mt.Vector2(229, 263)
        self.draw_score_button_pos = mt.Vector2(229, 328)
        self.draw_manual_button_pos = mt.Vector2(229, 393)
        self.draw_exit_button_pos = mt.Vector2(229, 456)
        self.draw_back_button_pos = mt.Vector2(10, 620)

        self.draw_sound_button_pos = mt.Vector2(10, 620)

        self.soundon = True

    def draw(self, state):

        if(state == 0):
            self.scene.blit(self.surface, self.draw_pos) # draw a start scene sprite
            self.scene.blit(self.play_button_surface, self.draw_play_button_pos)  # draw a button sprite
            self.scene.blit(self.about_button_surface, self.draw_about_button_pos)  # draw a button sprite
            self.scene.blit(self.score_button_surface, self.draw_score_button_pos)  # draw a button sprite
            self.scene.blit(self.manual_button_surface, self.draw_manual_button_pos)  # draw a button sprite
            self.scene.blit(self.exit_button_surface, self.draw_exit_button_pos)  # draw a button sprite
            if(self.soundon == True):
                self.scene.blit(self.soundon_button_surface, self.draw_sound_button_pos)  # draw a button sprite
            else:
                self.scene.blit(self.soundoff_button_surface, self.draw_sound_button_pos)  # draw a button sprite
        elif(state == 2):
            self.scene.blit(self.over_surface, self.draw_pos)  # draw a game over sprite

        elif (state == 3):
            self.scene.blit(self.next_surface, self.draw_pos)  # draw a next level sprite

        elif(state == 4):
            self.scene.blit(self.win_surface, self.draw_pos)  # draw a win sprite

        elif (state == 5):
            self.scene.blit(self.about_surface, self.draw_pos)  # draw a about sprite
            self.scene.blit(self.home_button_surface, self.draw_back_button_pos)  # draw a button sprite

        elif (state == 6):
            self.scene.blit(self.manual_surface, self.draw_pos)  # draw a manual sprite
            self.scene.blit(self.home_button_surface, self.draw_back_button_pos)  # draw a button sprite

        elif (state == 7):
            self.scene.blit(self.pause_surface, self.draw_pos)  # draw a pause sprite
            self.scene.blit(self.play_button_surface, self.draw_play_button_pos)  # draw a button sprite
            self.scene.blit(self.home_pause_button_surface, self.draw_home_button_pos)  # draw a button sprite

        if(state == 2 or state == 3 or state == 4):
            self.scene.blit(self.button_surface, self.draw_button_pos)  # draw a button sprite

        pygame.display.flip()

The next step is to edit the game manager class which will terminate the game after the user has clicked on this button.

from Player import Player
from Background import Background
from EnemyManager import EnemyManager
from Overlap import Overlap
from ExplosionManager import ExplosionManager
from Score import Score
from StartScene import StartScene
from pygame.locals import *
from LevelManager import LevelManager
import pygame
import webbrowser
import os

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.start_scene = StartScene(scene)
        self.load_music()
        self.play_music()
        self.overlap_manager = Overlap()
        self.level_manager = LevelManager(self)

        self.setup(self.level_manager.get_level())

        self.pause = False # flag to pause the game

        #game state
        self.LOAD = 0
        self.GAME = 1
        self.OVER = 2
        self.NEXT = 3
        self.WIN = 4
        self.ABOUT = 5
        self.MANUAL = 6
        self.PAUSE = 7

        self.state = self.LOAD

    def setup(self, game_level):

        self.game_level = game_level
        self.score_manager = Score(self.scene)
        self.background = Background(self.scene)
        self.player = Player(self.scene)
        self.enemy_manager = EnemyManager(self.scene, self.player, game_level)
        self.explosion_manager = ExplosionManager(self.scene)

    def loop(self):

        if(self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.ABOUT or self.state == self.MANUAL):

            self.start_scene.draw(self.state)

        elif(self.state == self.GAME):

            self.update()
            self.draw()

        elif(self.state == self.PAUSE):

            self.start_scene.draw(self.state)

    def isAreaClick(self, pos):
        if (self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.ABOUT or self.state == self.MANUAL or self.state == self.PAUSE):
            self.rect = Rect(177, 274, 306, 112) # the position of the play button on the scene
            self.rect_play = Rect(229, 200, 200, 53)  # the position of the play button on the home scene
            self.rect_about = Rect(229, 263, 200, 53)  # the position of the about button on the home scene
            self.rect_exit = Rect(229, 456, 200, 53)  # the position of the about button on the home scene
            self.rect_pause_home = Rect(229, 263, 200, 53)  # the position of the home button on pause scene
            self.rect_manual = Rect(229, 393, 200, 53)  # the position of the manual button on the home scene
            self.rect_back = Rect(10, 620, 40, 30)  # the position of the back button on the home scene
            self.rect_sound = Rect(10, 620, 29, 30) # the position of the sound button on the home scene
            x, y = pos
            if(self.rect.collidepoint(x, y) and (self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN)):
                self.state = self.GAME
            elif(self.rect_play.collidepoint(x,y) and self.state == self.LOAD):
                self.state = self.GAME
            elif (self.rect_play.collidepoint(x, y) and self.state == self.PAUSE):
                self.state = self.GAME
            elif (self.rect_about.collidepoint(x, y) and self.state == self.LOAD):
                self.state = self.ABOUT
            elif (self.rect_exit.collidepoint(x, y) and self.state == self.LOAD):
                exit()
            elif (self.rect_pause_home.collidepoint(x, y) and self.state == self.PAUSE):

                self.state = self.LOAD
                self.setup(self.level_manager.get_level())

                try:
                    f = open("level.txt", "w+")
                    try:
                        f.write(str(self.level_manager.get_level()))
                    finally:
                        f.close()
                except IOError:
                    print ('cannot open a file')

            elif (self.rect_manual.collidepoint(x, y) and self.state == self.LOAD):
                webbrowser.open_new('http://gamingdirectional.com/')
            elif (self.rect_back.collidepoint(x, y) and (self.state == self.ABOUT or self.state == self.MANUAL)):
                self.state = self.LOAD
            elif (self.rect_sound.collidepoint(x, y) and self.state == self.LOAD):

                if(self.start_scene.soundon == True):
                    self.start_scene.soundon = False
                    pygame.mixer_music.pause()
                else:
                    self.start_scene.soundon = True
                    pygame.mixer_music.unpause()

    def set_pause(self, pause):

        self.pause = pause

        if(self.pause == True):

            self.state = self.PAUSE

    def load_music(self):
        pygame.mixer_music.load('Music/winternight.ogg')

    def play_music(self):
        pygame.mixer_music.play(-1) #play the music infinite time

    def set_player_x(self, _x):
        if (self.state == self.GAME):
            self.player.setX(_x)

    def set_player_y(self, _y):
        if (self.state == self.GAME):
            self.player.setY(_y)

    def set_missile_strike(self, strike):
        if (self.state == self.GAME):
            self.player.setStrike(strike)

    def update(self):
        self.player.update()
        self.enemy_manager.update()
        self.isOverlap()
        self.explosion_manager.explosion_update()

    # check for player, enemy, missiles overlap
    def isOverlap(self):
        self.overlap_manager.isOverlap(self.player, self.enemy_manager, self.explosion_manager, self.score_manager, self)

    def draw(self):
        if(self.state == self.GAME):
            self.background.draw()
            self.player.draw()
            self.enemy_manager.draw()
            self.explosion_manager.draw()
            self.score_manager.draw()
            pygame.display.flip()

That is it, the game manager class will take care of the button click events and will terminate the game if the exit button has been clicked. The development of this game will continue on the next chapter, if you have missed out the previous tutorials then don’t forget to read them all.

Open a text file then write the game level onto it with python

Welcome back, let us continue with our previous pygame project. In this article we will first create a text file which will be used to store the game level. This text file will be called level.txt which will be stored within the project folder. We need to create this text file by hand and type in 1 which is the first level of the game onto it, this level number will be rewritten each and every time the user has pressed the home button on the pause scene to go back to the homepage of the game. The reason why we are not letting the program to create the level.txt file for us is because when we have installed the game on our pc later on our computer’s os might not let the game program to create that text file on it’s won folder due to the security reason, therefore it is always the best practice for the game developer to create our own text file even if it is an empty file so the program will be able to edit that file later on. OK so much for that, lets create the text file first and write the starting level onto it.

pycharm's interface
pycharm’s interface

The next thing we need to do is to edit the level manager class. Now the level variable will directly get the level number from the text file.

import os

class LevelManager(object):

    def __init__(self, gm):

        self.game_manager = gm

        try:
            f = open("level.txt", "r")
            try:
                if f.mode == 'r':
                    f1 = f.readlines()
                    for x in f1:
                        self.level = int(x)
            finally:
                f.close()
        except IOError:
            print('Error')

        self.MAX_LEVEL = 3

    def increase_level(self):

        self.level += 1
        if(self.level >  self.MAX_LEVEL):
            self.game_manager.state = self.game_manager.WIN
            self.level = 1
            self.game_manager.setup(self.level)
        else:
            self.game_manager.state = self.game_manager.NEXT
            self.game_manager.setup(self.level)

    def get_level(self):

        return self.level

One thing you might have noticed from the top program is that we actually do not need to create loop when reading the content of the text file because it only consists of one line, the reason why we have created a loop is because we will have more than one line of game level in the future so we do need this loop and we will further modify this loop content in the future.

The last part is to edit the game manager class which will rewrite the game level to the text file each time the user has clicked on the home button on the pause scene to get back to the main game page.

from Player import Player
from Background import Background
from EnemyManager import EnemyManager
from Overlap import Overlap
from ExplosionManager import ExplosionManager
from Score import Score
from StartScene import StartScene
from pygame.locals import *
from LevelManager import LevelManager
import pygame
import webbrowser
import os

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.start_scene = StartScene(scene)
        self.load_music()
        self.play_music()
        self.overlap_manager = Overlap()
        self.level_manager = LevelManager(self)

        self.setup(self.level_manager.get_level())

        self.pause = False # flag to pause the game

        #game state
        self.LOAD = 0
        self.GAME = 1
        self.OVER = 2
        self.NEXT = 3
        self.WIN = 4
        self.ABOUT = 5
        self.MANUAL = 6
        self.PAUSE = 7

        self.state = self.LOAD

    def setup(self, game_level):

        self.game_level = game_level
        self.score_manager = Score(self.scene)
        self.background = Background(self.scene)
        self.player = Player(self.scene)
        self.enemy_manager = EnemyManager(self.scene, self.player, game_level)
        self.explosion_manager = ExplosionManager(self.scene)

    def loop(self):

        if(self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.ABOUT or self.state == self.MANUAL):

            self.start_scene.draw(self.state)

        elif(self.state == self.GAME):

            self.update()
            self.draw()

        elif(self.state == self.PAUSE):

            self.start_scene.draw(self.state)

    def isAreaClick(self, pos):
        if (self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.ABOUT or self.state == self.MANUAL or self.state == self.PAUSE):
            self.rect = Rect(177, 274, 306, 112) # the position of the play button on the scene
            self.rect_play = Rect(229, 200, 200, 53)  # the position of the play button on the home scene
            self.rect_about = Rect(229, 263, 200, 53)  # the position of the about button on the home scene
            self.rect_pause_home = Rect(229, 263, 200, 53)  # the position of the home button on pause scene
            self.rect_manual = Rect(229, 393, 200, 53)  # the position of the manual button on the home scene
            self.rect_back = Rect(10, 620, 40, 30)  # the position of the back button on the home scene
            self.rect_sound = Rect(10, 620, 29, 30) # the position of the sound button on the home scene
            x, y = pos
            if(self.rect.collidepoint(x, y) and (self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN)):
                self.state = self.GAME
            elif(self.rect_play.collidepoint(x,y) and self.state == self.LOAD):
                self.state = self.GAME
            elif (self.rect_play.collidepoint(x, y) and self.state == self.PAUSE):
                self.state = self.GAME
            elif (self.rect_about.collidepoint(x, y) and self.state == self.LOAD):
                self.state = self.ABOUT
            elif (self.rect_pause_home.collidepoint(x, y) and self.state == self.PAUSE):

                self.state = self.LOAD
                self.setup(self.level_manager.get_level())

                try:
                    f = open("level.txt", "w+")
                    try:
                        f.write(str(self.level_manager.get_level()))
                    finally:
                        f.close()
                except IOError:
                    print ('cannot open the file')

            elif (self.rect_manual.collidepoint(x, y) and self.state == self.LOAD):
                webbrowser.open_new('http://gamingdirectional.com/')
            elif (self.rect_back.collidepoint(x, y) and (self.state == self.ABOUT or self.state == self.MANUAL)):
                self.state = self.LOAD
            elif (self.rect_sound.collidepoint(x, y) and self.state == self.LOAD):

                if(self.start_scene.soundon == True):
                    self.start_scene.soundon = False
                    pygame.mixer_music.pause()
                else:
                    self.start_scene.soundon = True
                    pygame.mixer_music.unpause()

    def set_pause(self, pause):

        self.pause = pause

        if(self.pause == True):

            self.state = self.PAUSE

    def load_music(self):
        pygame.mixer_music.load('Music/winternight.ogg')

    def play_music(self):
        pygame.mixer_music.play(-1) #play the music infinite time

    def set_player_x(self, _x):
        if (self.state == self.GAME):
            self.player.setX(_x)

    def set_player_y(self, _y):
        if (self.state == self.GAME):
            self.player.setY(_y)

    def set_missile_strike(self, strike):
        if (self.state == self.GAME):
            self.player.setStrike(strike)

    def update(self):
        self.player.update()
        self.enemy_manager.update()
        self.isOverlap()
        self.explosion_manager.explosion_update()

    # check for player, enemy, missiles overlap
    def isOverlap(self):
        self.overlap_manager.isOverlap(self.player, self.enemy_manager, self.explosion_manager, self.score_manager, self)

    def draw(self):
        if(self.state == self.GAME):
            self.background.draw()
            self.player.draw()
            self.enemy_manager.draw()
            self.explosion_manager.draw()
            self.score_manager.draw()
            pygame.display.flip()

That is basically it, in the next chapter we will work on the score scene where the program will list out 5 highest levels that the player has achieved with the help of the level.text file which we have just created earlier in this chapter.

Create the pause scene for the game

In this article we will create a pause scene for our ongoing pygame project. When a user presses the p key on his keyboard during the game, the game will pause and the pause scene will appear. When he presses the play button on the pause scene the game will resume. We need to modify three files in order to include this scene into the game. The first file to edit is the main project file which we will include in an extra mechanism to detect the p key pressed event.

import sys, pygame
from pygame.locals import *
from GameManager import GameManager

pygame.init()

size = width, height = 660, 660
pygame.display.set_caption("Air Strike") # set the title of the window
screen = pygame.display.set_mode(size)

game_manager = GameManager(screen)

while True:

    for event in pygame.event.get():

        if event.type == pygame.QUIT:
            pygame.mixer_music.stop()
            sys.exit()

        # detect key press event
        if event.type == KEYDOWN:

            if(game_manager.state == 1):
                if event.key == K_LEFT:
                    game_manager.set_player_x(-0.1)
                elif event.key == K_RIGHT:
                    game_manager.set_player_x(0.1)

                if event.key == K_UP:
                    game_manager.set_player_y(-0.1)
                elif event.key == K_DOWN:
                    game_manager.set_player_y(0.1)

                if event.key == K_SPACE:
                    game_manager.set_missile_strike(True)

                if event.key == K_p :
                    game_manager.set_pause(True) # set the pause state to true

        elif event.type == KEYUP:

            if (game_manager.state == 1):
                if event.key == K_LEFT:
                    game_manager.set_player_x(0)
                elif event.key == K_RIGHT:
                    game_manager.set_player_x(0)

                if event.key == K_UP:
                    game_manager.set_player_y(0)
                elif event.key == K_DOWN:
                    game_manager.set_player_y(0)

                if event.key == K_SPACE:
                    game_manager.set_missile_strike(False)

        elif event.type == pygame.MOUSEBUTTONDOWN:
            # 1 is the left mouse button
            if event.button == 1:
                game_manager.isAreaClick(event.pos)

    game_manager.loop()

The next file needs to edit is the start scene file where we will include in the pause scene to the start scene class.

from BgSprite import BgSprite
from GameSprite import GameSprite
from pygame.locals import *
from pygame import math as mt
import pygame

class StartScene(object):

    def __init__(self, scene):
        self.scene = scene
        self.play_button = 'Asset/play.png'
        self.about_button  = 'Asset/about.png'
        self.score_button = 'Asset/score.png'
        self.home_button = 'Asset/back.png'
        self.button_image = 'Asset/button_play.png'
        self.manual_button = 'Asset/manual.png'
        self.bg_image = 'Asset/start.png'
        self.over_image = 'Asset/ove.png'
        self.next_image = 'Asset/next.png'
        self.win_image = 'Asset/winn.png'
        self.general_image = 'Asset/general.png'
        self.about_page = 'Asset/about_page.png'
        self.manual_page = 'Asset/manual_page.png'
        self.soundon = 'Asset/sound.png'
        self.soundoff = 'Asset/soundoff.png'
        self.bg_rect = Rect(0, 0, 660, 660)
        self.button_rect = Rect(0, 0,  306, 112)
        self.home_button_rect = Rect(0, 0, 200, 53)
        self.back_button_rect = Rect(0, 0, 40, 30)
        self.sound_button_rect = Rect(0, 0, 29, 30)

        self.sprite = BgSprite(self.bg_image, self.bg_rect)
        self.sprite_about = BgSprite(self.about_page, self.bg_rect)
        self.sprite_manual = BgSprite(self.manual_page, self.bg_rect)
        self.sprite_win = BgSprite(self.win_image, self.bg_rect)
        self.sprite_over = BgSprite(self.over_image, self.bg_rect)
        self.sprite_next = BgSprite(self.next_image, self.bg_rect)
        self.sprite_pause = BgSprite(self.general_image, self.bg_rect)

        self.soundon_button = GameSprite(self.soundon, self.sound_button_rect)
        self.soundoff_button = GameSprite(self.soundoff, self.sound_button_rect)

        self.soundoff_button_surface = self.soundoff_button.getImage()  # get the button sprite surface
        self.soundon_button_surface = self.soundon_button.getImage()  # get the button sprite surface

        self.sprite_button = GameSprite(self.button_image, self.button_rect)
        self.sprite_manual_button = GameSprite(self.manual_button, self.home_button_rect)
        self.sprite_play_button = GameSprite(self.play_button, self.home_button_rect)
        self.sprite_about_button = GameSprite(self.about_button, self.home_button_rect)
        self.sprite_score_button = GameSprite(self.score_button, self.home_button_rect)
        self.sprite_home_button = GameSprite(self.home_button, self.back_button_rect)
        self.about_surface = self.sprite_about.getImage()  # get the about sprite surface
        self.manual_surface = self.sprite_manual.getImage()  # get the manual sprite surface
        self.win_surface = self.sprite_win.getImage()  # get the win sprite surface
        self.next_surface = self.sprite_next.getImage()  # get the next level sprite surface
        self.over_surface = self.sprite_over.getImage()  # get the game over sprite surface
        self.pause_surface = self.sprite_pause.getImage()  # get the pause sprite surface
        self.surface = self.sprite.getImage()  # get the start scene sprite surface
        self.button_surface = self.sprite_button.getImage() # get the button sprite surface
        self.play_button_surface = self.sprite_play_button.getImage()  # get the button sprite surface
        self.about_button_surface = self.sprite_about_button.getImage()  # get the button sprite surface
        self.score_button_surface = self.sprite_score_button.getImage()  # get the button sprite surface
        self.manual_button_surface = self.sprite_manual_button.getImage()  # get the button sprite surface
        self.home_button_surface = self.sprite_home_button.getImage()  # get the button sprite surface
        self.draw_pos = mt.Vector2(0, 0)
        self.draw_button_pos = mt.Vector2(177, 274)
        self.draw_play_button_pos = mt.Vector2(229, 200)
        self.draw_about_button_pos = mt.Vector2(229, 263)
        self.draw_score_button_pos = mt.Vector2(229, 328)
        self.draw_manual_button_pos = mt.Vector2(229, 393)
        self.draw_back_button_pos = mt.Vector2(10, 620)

        self.draw_sound_button_pos = mt.Vector2(10, 620)

        self.soundon = True

    def draw(self, state):

        if(state == 0):
            self.scene.blit(self.surface, self.draw_pos) # draw a start scene sprite
            self.scene.blit(self.play_button_surface, self.draw_play_button_pos)  # draw a button sprite
            self.scene.blit(self.about_button_surface, self.draw_about_button_pos)  # draw a button sprite
            self.scene.blit(self.score_button_surface, self.draw_score_button_pos)  # draw a button sprite
            self.scene.blit(self.manual_button_surface, self.draw_manual_button_pos)  # draw a button sprite
            if(self.soundon == True):
                self.scene.blit(self.soundon_button_surface, self.draw_sound_button_pos)  # draw a button sprite
            else:
                self.scene.blit(self.soundoff_button_surface, self.draw_sound_button_pos)  # draw a button sprite
        elif(state == 2):
            self.scene.blit(self.over_surface, self.draw_pos)  # draw a game over sprite

        elif (state == 3):
            self.scene.blit(self.next_surface, self.draw_pos)  # draw a next level sprite

        elif(state == 4):
            self.scene.blit(self.win_surface, self.draw_pos)  # draw a win sprite

        elif (state == 5):
            self.scene.blit(self.about_surface, self.draw_pos)  # draw a about sprite
            self.scene.blit(self.home_button_surface, self.draw_back_button_pos)  # draw a button sprite

        elif (state == 6):
            self.scene.blit(self.manual_surface, self.draw_pos)  # draw a manual sprite
            self.scene.blit(self.home_button_surface, self.draw_back_button_pos)  # draw a button sprite

        elif (state == 7):
            self.scene.blit(self.pause_surface, self.draw_pos)  # draw a game over sprite

        if(state == 2 or state == 3 or state == 4 or state == 7):
            self.scene.blit(self.button_surface, self.draw_button_pos)  # draw a button sprite

        pygame.display.flip()

The final class which we need to edit is the game manager class where we need to include in the pause game state as well as to adjust the related methods to match this new state, for example shows the pause scene when the game is in the pause state.

from Player import Player
from Background import Background
from EnemyManager import EnemyManager
from Overlap import Overlap
from ExplosionManager import ExplosionManager
from Score import Score
from StartScene import StartScene
from pygame.locals import *
from LevelManager import LevelManager
import pygame
import webbrowser

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.start_scene = StartScene(scene)
        self.load_music()
        self.play_music()
        self.overlap_manager = Overlap()
        self.level_manager = LevelManager(self)

        self.setup(self.level_manager.get_level())

        self.pause = False # flag to pause the game

        #game state
        self.LOAD = 0
        self.GAME = 1
        self.OVER = 2
        self.NEXT = 3
        self.WIN = 4
        self.ABOUT = 5
        self.MANUAL = 6
        self.PAUSE = 7

        self.state = self.LOAD

    def setup(self, game_level):

        self.game_level = game_level
        self.score_manager = Score(self.scene)
        self.background = Background(self.scene)

        self.player = Player(self.scene)
        self.enemy_manager = EnemyManager(self.scene, self.player, game_level)
        self.explosion_manager = ExplosionManager(self.scene)

    def loop(self):

        if(self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.ABOUT or self.state == self.MANUAL):

            self.start_scene.draw(self.state)

        elif(self.state == self.GAME):

            self.update()
            self.draw()

        elif(self.state == self.PAUSE):

            self.draw()

    def isAreaClick(self, pos):
        if (self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.ABOUT or self.state == self.MANUAL or self.state == self.PAUSE):
            self.rect = Rect(177, 274, 306, 112) # the position of the play button on the scene
            self.rect_play = Rect(229, 200, 200, 53)  # the position of the play button on the home scene
            self.rect_about = Rect(229, 263, 200, 53)  # the position of the about button on the home scene
            self.rect_manual = Rect(229, 393, 200, 53)  # the position of the manual button on the home scene
            self.rect_back = Rect(10, 620, 40, 30)  # the position of the back button on the home scene
            self.rect_sound = Rect(10, 620, 29, 30) # the position of the sound button on the home scene
            x, y = pos
            if(self.rect.collidepoint(x, y) and (self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN or self.state == self.PAUSE)):
                self.state = self.GAME
            elif(self.rect_play.collidepoint(x,y) and self.state == self.LOAD):
                self.state = self.GAME
            elif (self.rect_about.collidepoint(x, y) and self.state == self.LOAD):
                self.state = self.ABOUT
            elif (self.rect_manual.collidepoint(x, y) and self.state == self.LOAD):
                #self.state = self.MANUAL
                webbrowser.open_new('http://gamingdirectional.com/')
            elif (self.rect_back.collidepoint(x, y) and (self.state == self.ABOUT or self.state == self.MANUAL)):
                self.state = self.LOAD
            elif (self.rect_sound.collidepoint(x, y) and self.state == self.LOAD):

                if(self.start_scene.soundon == True):
                    self.start_scene.soundon = False
                    pygame.mixer_music.pause()
                else:
                    self.start_scene.soundon = True
                    pygame.mixer_music.unpause()

    def set_pause(self, pause):

        self.pause = pause

        if(self.pause == True):

            self.state = self.PAUSE

    def load_music(self):
        pygame.mixer_music.load('Music/winternight.ogg')

    def play_music(self):
        pygame.mixer_music.play(-1) #play the music infinite time

    def set_player_x(self, _x):
        if (self.state == self.GAME):
            self.player.setX(_x)

    def set_player_y(self, _y):
        if (self.state == self.GAME):
            self.player.setY(_y)

    def set_missile_strike(self, strike):
        if (self.state == self.GAME):
            self.player.setStrike(strike)

    def update(self):
        self.player.update()
        self.enemy_manager.update()
        self.isOverlap()
        self.explosion_manager.explosion_update()

    # check for player, enemy, missiles overlap
    def isOverlap(self):
        self.overlap_manager.isOverlap(self.player, self.enemy_manager, self.explosion_manager, self.score_manager, self)

    def draw(self):
        if(self.state == self.GAME):
            self.background.draw()
            self.player.draw()
            self.enemy_manager.draw()
            self.explosion_manager.draw()
            self.score_manager.draw()
        elif(self.state == self.PAUSE):
            self.start_scene.draw(self.state)
        pygame.display.flip()

That is it, now we are ready for the next lesson which will further deal with the game scenes.

Create the third level for this pygame project

In this article we are going to create the third level for our pygame project after we have created the previous two levels, the reason I create the third game level in this chapter is because this level is different from the second level which is only using the same enemy class to generate different type of enemy ship. In this chapter we are going to create a new enemy class which will act differently as compared with the previous enemy class.

We will create a new enemy object which moves from side to side and shoots three missiles at the same time, we can add in more features to this new enemy class later on but for now lets just create a simple one first.

from pygame import math as mt
from Objectpool import Objectpool

class Enemy1(object):

    def __init__(self, enemy_surface, x, y):

        self.on = True
        self.enemy_surface = enemy_surface
        self.x = x
        self.y = y
        self.hit = False
        self.direction = True
        self.enemy_pos = mt.Vector2(self.x, self.y)
        self.missile_count = 10
        self.missile_timer = 0
        self.missile_object_pool = Objectpool(self.missile_count)
        self.missile_list = []

    def update(self):
        if(self.direction == True):
            self.x += 0.1
        else:
            self.x -= 0.1
        self.enemy_pos = mt.Vector2(self.x, self.y)
        self.missile_update(self.missile_object_pool)

    def missile_update(self, pool):

        for item in list(self.missile_list):
            if (item.on == False):
                self.missile_list.remove(item)
                pool.recycle(item)
            else:
                item.update()

    def missile_draw(self, scene): # draw enemy missiles on game scene
        for item in list(self.missile_list):
            scene.blit(item.missile_surface, item.missile_pos)


    def create_enemy_missile(self, enemy_missile_manager):

        if(self.missile_timer > 300):

            self.missile_timer = 0

            if (self.missile_object_pool.getSize() > 0):
                enemy_missile_manager.create_missile(self.x + 3, self.y + 100, self.missile_object_pool, self.missile_list)
                enemy_missile_manager.create_missile(self.x + 50, self.y + 100, self.missile_object_pool, self.missile_list)
                enemy_missile_manager.create_missile(self.x + 100, self.y + 100, self.missile_object_pool, self.missile_list)
            else:
                enemy_missile_manager.create_missile(self.x + 3, self.y + 100, None, self.missile_list)
                enemy_missile_manager.create_missile(self.x + 50, self.y + 100, None, self.missile_list)
                enemy_missile_manager.create_missile(self.x + 100, self.y + 100, None, self.missile_list)

        else:

            self.missile_timer += 1

Next is to edit the enemy manager class by adding in the level three scene’s objects.

from Enemy import Enemy
from GameSprite import GameSprite
from pygame.locals import *
from EnemyMissileManager import EnemyMissileManager
import random
from Objectpool import Objectpool
from Enemy1 import Enemy1

class EnemyManager(object):

    def __init__(self, scene, player, game_level):

        self.enemy_missile_manager = EnemyMissileManager()
        self.scene = scene
        self.player = player
        self.enemy_count = 10
        self.horizontal_enemy_count = 1
        self.missile_count = 60
        self.enemy_list = []
        self.horizontal_enemy_list = []
        self.image = 'Asset/enemy0.png'
        self.image1 =  'Asset/enemy1.png'
        self.image2 = 'Asset/enemy2.png'
        self.width = 30
        self.height = 30
        self.width1 = 130
        self.height1 = 130
        self.rect = Rect(0, 0, self.width, self.height)
        self.rect1 = Rect(0, 0, self.width1, self.height1)
        self.more_enemy = 0
        self.y = -50
        self.boundary_width = 660
        self.boundary_height = 660
        self.object_pool = Objectpool(self.enemy_count)
        self.horizontal_object_pool = Objectpool(self.horizontal_enemy_count)
        self.next_enemy = 0
        self.level = game_level

        # initialize game sprite object
        self.sprite = GameSprite(self.image, self.rect)
        self.sprite1 = GameSprite(self.image1, self.rect)
        self.sprite2 = GameSprite(self.image2, self.rect1)

    def create_enemy(self, x, y):

        if(self.enemy_count > 0):

            if(self.object_pool.getSize() > 0): # get the ship from object pool if the pool is not empty
                self.enemy_list.append(self.object_pool.obtain())
            else: # objects setup based on the level of the game
                if(self.level == 1):
                    self.enemy_surface = self.sprite.getImage()
                elif(self.level == 2 or self.level == 3):
                    if(self.next_enemy == 0):
                        self.enemy_surface = self.sprite.getImage()
                        self.next_enemy += 1
                    elif(self.next_enemy == 1):
                        self.enemy_surface = self.sprite1.getImage()
                        self.next_enemy = 0
                self.enemy_list.append(Enemy(self.enemy_surface, x, y))
            self.enemy_count -= 1

    def create_horizontal_enemy(self, x, y):

        if (self.horizontal_enemy_count > 0):

            if (self.horizontal_object_pool.getSize() > 0):  # get the ship from object pool if the pool is not empty
                self.horizontal_enemy_list.append(self.horizontal_object_pool.obtain())
            else:  # objects setup based on the level of the game
                if (self.level == 3):
                    self.enemy_surface1 = self.sprite2.getImage()
                self.horizontal_enemy_list.append(Enemy1(self.enemy_surface1, x, y))
            self.horizontal_enemy_count -= 1


    def update(self):

        if (self.level == 1 or self.level == 2):

            if (self.more_enemy > 600):
                self.more_enemy = 0
                x = random.randint(30, self.boundary_width - 50)
                self.create_enemy(x , self.y)  # create more enemy
            else:
                self.more_enemy += 1 # increase time

        elif(self.level == 3):

            if (self.more_enemy > 600):
                self.more_enemy = 0
                x = random.randint(30, self.boundary_width - 50)
                self.create_enemy(x , self.y)  # create more enemy
            else:
                self.more_enemy += 1 # increase time

            if(self.horizontal_enemy_count > 0):
                self.create_horizontal_enemy(-130, 200)  # create new enemy


        self.enemy_update()
        self.check_boundary()
        self.create_enemy_missile()

    def create_enemy_missile(self):

        for item in list(self.enemy_list):

            if(self.player.pos.y - item.y  < 100 and abs(self.player.pos.x - item.x) < 60 ):

                item.create_enemy_missile(self.enemy_missile_manager)

        if(self.level == 3):

            for item in list(self.horizontal_enemy_list):
                item.create_enemy_missile(self.enemy_missile_manager)

    def enemy_update(self):

        for item in list(self.enemy_list):

            if(item.on == False):
                self.enemy_list.remove(item)
                self.enemy_count += 1
                item.y = self.y
                item.on = True
                self.object_pool.recycle(item)
            else:
                 item.update()

        if (self.level == 3):

            for item in list(self.horizontal_enemy_list):
                if (item.on == False):
                    self.horizontal_enemy_list.remove(item)
                    self.horizontal_enemy_count += 1
                    item.y = 220
                    item.x = -130
                    item.on = True
                    self.horizontal_object_pool.recycle(item)
                else:
                    item.update()

    # check the boundary of the enemy ship with the game scene area
    def check_boundary(self):

        for i in range(len(self.enemy_list)):
            if (self.enemy_list[i].y > self.boundary_height):
                self.enemy_list[i].on = False

        if (self.level == 3):
            for i in range(len(self.horizontal_enemy_list)):
                if (self.horizontal_enemy_list[i].x > self.boundary_width):
                    self.horizontal_enemy_list[i].direction = False
                elif(self.horizontal_enemy_list[i].x <= -130):
                    self.horizontal_enemy_list[i].direction = True

    def draw(self):

        # blit the enemy and enemy missiles on  the scene
        for i in range(len(self.enemy_list)):
            self.scene.blit(self.enemy_list[i].enemy_surface, self.enemy_list[i].enemy_pos)
            self.enemy_list[i].missile_draw(self.scene)

        if(self.level == 3):
            for i in range(len(self.horizontal_enemy_list)):
                self.scene.blit(self.horizontal_enemy_list[i].enemy_surface, self.horizontal_enemy_list[i].enemy_pos)
                self.horizontal_enemy_list[i].missile_draw(self.scene)

We also need to edit the overlap class which will now take level three into consideration.

from pygame.locals import *

class Overlap(object):

    def __init__(self):
        pass # nothing here

    # is player and enemy, player missile, enemy missile overlap
    def isOverlap(self, player, em, ex, score, gm):

        self.checkOverlap(em.enemy_list, player, ex, gm, score, em.width, em.height, em.enemy_missile_manager.width, em.enemy_missile_manager.height, None)

        if(gm.level_manager.get_level() == 3):
            self.checkOverlap(em.horizontal_enemy_list, player, ex, gm, score, em.width1, em.height1, em.enemy_missile_manager.width, em.enemy_missile_manager.height, gm.level_manager.get_level())

    def checkOverlap(self, e_list, player, ex, gm, score, width, height, m_width, m_height, level):

        self.player_rect = Rect(player.pos.x, player.pos.y, player.width, player.height)

        for i in range(len(e_list)):  # is player collides with enemy

            self.em_rect = Rect(e_list[i].x, e_list[i].y, width, height)
            if (self.player_rect.colliderect(self.em_rect)):
                e_list[i].on = False
                if (e_list[i].hit == False):
                    ex.create_explosion(player.pos.x + 2, player.pos.y + 2)
                    e_list[i].hit = True
                    gm.state = gm.OVER
                    gm.setup(gm.level_manager.get_level())

        for i in range(len(e_list)):  # is enemy missile hits player

            for j in range(len(e_list[i].missile_list)):
                self.em_rect = Rect(e_list[i].missile_list[j].x, e_list[i].missile_list[j].y,
                                    m_width, m_height)
                if (self.player_rect.colliderect(self.em_rect)):
                    e_list[i].missile_list[j].on = False
                    ex.create_explosion(player.pos.x + 2, player.pos.y + 2)
                    score.set_score(-1)
                    if (score.score < 0):
                        gm.state = gm.OVER
                        gm.setup(gm.level_manager.get_level())

        for i in range(len(e_list)):  # is player missile hits enemy

            self.em_rect = Rect(e_list[i].x, e_list[i].y, width, height)

            for j in range(len(player.getMissileManager().missile_list)):

                self.mm_rect = Rect(player.getMissileManager().missile_list[j].x,
                                    player.getMissileManager().missile_list[j].y, player.getMissileManager().width,
                                    player.getMissileManager().height)

                if (self.em_rect.colliderect(self.mm_rect)):
                    e_list[i].on = False
                    player.getMissileManager().missile_list[j].on = False
                    if (e_list[i].hit == False):
                        ex.create_explosion(e_list[i].x, e_list[i].y + 2)
                        e_list[i].hit = True

                        if(level == 3):
                            score.set_score(10)
                        else:
                            score.set_score(1)
                        if (score.score >= gm.level_manager.get_level() * 30):
                            gm.level_manager.increase_level()

Here is what level 3 looks like.

Now we have concluded the game level related topics and we will create the about scene and the credit scene in the next chapter.

Lets move on to the next game level!

Welcome back, in this article we will create the next level setup scene for our pygame project. Yesterday I had a deep thought about the method which we should use to do the game objects setup for each game level and had finally decided to leave the game object managers to do their own game object setups. The reasons why I have selected to let game object managers to manage their own game object setup for each level are because:-

1) The level manager class is not suitable to do all the game objects setup for each new level and should always stay away from dealing directly with the game object.
2) The game manager class is also not a place where we will want to do the game objects setup because this will increase the complexity of this class and thus makes the game developer really hard to follow the code when he needs to modify the program in the future.

Thus leaves all the individual game object related topic to the object manager class is the best choice and that is also what the object manager class should do after all.

OK lets get started, we are not going to edit anything in the background manager, the enemy missile manager and the player manager because there is nothing changed each time a new level has been reached. The only class we will update is the enemy manger class which we will add in the new type of enemy spaceship on each level of the game. Here is the modify version of the enemy manager class.

from Enemy import Enemy
from GameSprite import GameSprite
from pygame.locals import *
from EnemyMissileManager import EnemyMissileManager
import random
from Objectpool import Objectpool

class EnemyManager(object):

    def __init__(self, scene, player, game_level):

        self.enemy_missile_manager = EnemyMissileManager()
        self.scene = scene
        self.player = player
        self.enemy_count = 10
        self.missile_count = 60
        self.enemy_list = []
        self.image = 'Asset/enemy0.png'
        self.image1 =  'Asset/enemy1.png'
        self.width = 30
        self.height = 30
        self.rect = Rect(0, 0, self.width, self.height)
        self.more_enemy = 0
        self.y = -50
        self.boundary_width = 660
        self.boundary_height = 660
        self.object_pool = Objectpool(self.enemy_count)
        self.next_enemy = 0
        self.level = game_level

        # initialize game sprite object
        self.sprite = GameSprite(self.image, self.rect)
        self.sprite1 = GameSprite(self.image1, self.rect)

    def create_enemy(self, x, y):

        if(self.enemy_count > 0):

            if(self.object_pool.getSize() > 0): # get the ship from object pool if the pool is not empty
                self.enemy_list.append(self.object_pool.obtain())
            else: # objects setup based on the level of the game
                if(self.level == 1):
                    self.enemy_surface = self.sprite.getImage()
                elif(self.level == 2):
                    if(self.next_enemy == 0):
                        self.enemy_surface = self.sprite.getImage()
                        self.next_enemy += 1
                    elif(self.next_enemy == 1):
                        self.enemy_surface = self.sprite1.getImage()
                        self.next_enemy = 0
                self.enemy_list.append(Enemy(self.enemy_surface, x, y))
            self.enemy_count -= 1


    def update(self):

        if (self.more_enemy > 600):
            self.more_enemy = 0
            x = random.randint(30, self.boundary_width - 50)
            self.create_enemy(x , self.y)  # create more enemy
        else:
            self.more_enemy += 1 # increase time

        self.enemy_update()
        self.check_boundary()
        self.create_enemy_missile()

    def create_enemy_missile(self):

        for item in list(self.enemy_list):

            if(self.player.pos.y - item.y  < 100 and abs(self.player.pos.x - item.x) < 60 ):

                item.create_enemy_missile(self.enemy_missile_manager)

    def enemy_update(self):

        for item in list(self.enemy_list):
            if(item.on == False):
                self.enemy_list.remove(item)
                self.enemy_count += 1
                item.y = self.y
                item.on = True
                self.object_pool.recycle(item)
            else:
                item.update()

    def check_boundary(self):
        for i in range(len(self.enemy_list)):
            if (self.enemy_list[i].y > self.boundary_height):
                self.enemy_list[i].on = False

    def draw(self):

        # blit the enemy and enemy missiles on  the scene
        for i in range(len(self.enemy_list)):
            self.scene.blit(self.enemy_list[i].enemy_surface, self.enemy_list[i].enemy_pos)
            self.enemy_list[i].missile_draw(self.scene)

At the moment we are only dealing with two levels, the code will get more complicated when the level increases, at this moment when a player has reached level 2 the enemy manager will create two type of enemy ships and which type of enemy ship will the enemy manager generates will depend on the next enemy variable which acts like a switch to switch from one enemy type to another. The enemies that the enemy manager has created have the same type of game properties but they will act differently starting from level 3 onward, the most important thing here is to get the level logic correct first then we can further modify the game code if we need to in the future.

Next thing we need to do is to modify the game manager file.

from Player import Player
from Background import Background
from EnemyManager import EnemyManager
from Overlap import Overlap
from ExplosionManager import ExplosionManager
from Score import Score
from StartScene import StartScene
from pygame.locals import *
from LevelManager import LevelManager
import pygame

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.start_scene = StartScene(scene)
        self.load_music()
        self.play_music()
        self.overlap_manager = Overlap()
        self.level_manager = LevelManager(self)

        self.setup(self.level_manager.get_level())

        #game state
        self.LOAD = 0
        self.GAME = 1
        self.OVER = 2
        self.NEXT = 3
        self.WIN = 4

        self.state = self.LOAD

    def setup(self, game_level):

        self.game_level = game_level
        self.score_manager = Score(self.scene)
        self.background = Background(self.scene)

        self.player = Player(self.scene)
        self.enemy_manager = EnemyManager(self.scene, self.player, game_level)
        self.explosion_manager = ExplosionManager(self.scene)

    def loop(self):

        if(self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN):

            self.start_scene.draw(self.state)

        elif(self.state == self.GAME):

            self.update()
            self.draw()

    def isAreaClick(self, pos):
        if (self.state == self.LOAD or self.state == self.OVER or self.state == self.NEXT or self.state == self.WIN):
            self.rect = Rect(177, 274, 306, 112) # the position of the play button on the scene
            x, y = pos
            if(self.rect.collidepoint(x, y)):
                self.state = self.GAME


    def load_music(self):
        pygame.mixer_music.load('Music/winternight.ogg')

    def play_music(self):
        pygame.mixer_music.play(-1) #play the music infinite time

    def set_player_x(self, _x):
        if (self.state == self.GAME):
            self.player.setX(_x)

    def set_player_y(self, _y):
        if (self.state == self.GAME):
            self.player.setY(_y)

    def set_missile_strike(self, strike):
        if (self.state == self.GAME):
            self.player.setStrike(strike)

    def update(self):
        self.player.update()
        self.enemy_manager.update()
        self.isOverlap()
        self.explosion_manager.explosion_update()

    # check for player, enemy, missiles overlap
    def isOverlap(self):
        self.overlap_manager.isOverlap(self.player, self.enemy_manager, self.explosion_manager, self.score_manager, self)

    def draw(self):
        self.background.draw()
        self.player.draw()
        self.enemy_manager.draw()
        self.explosion_manager.draw()
        self.score_manager.draw()
        pygame.display.flip()

The only thing you need to take note is the program has passed in the game level variable to the enemy manager class which will be used to decide what type of enemy we need for different level of the game.

The next thing we need to do is to modify the level manager class.

class LevelManager(object):

    def __init__(self, gm):

        self.game_manager = gm
        self.level = 1
        self.MAX_LEVEL = 2

    def increase_level(self):

        self.level += 1
        if(self.level >  self.MAX_LEVEL):
            self.game_manager.state = self.game_manager.WIN
            self.level = 1
            self.game_manager.setup(self.level)
        else:
            self.game_manager.state = self.game_manager.NEXT
            self.game_manager.setup(self.level)

    def get_level(self):

        return self.level

Nothing much here but just to tidy up the code and changes the start level to 1 instead of 0.

Last part is to modify the overlap class.

from pygame.locals import *

class Overlap(object):

    def __init__(self):
        pass # nothing here

    # is player and enemy, player missile, enemy missile overlap
    def isOverlap(self, player, em, ex, score, gm):

        self.player_rect = Rect(player.pos.x, player.pos.y, player.width, player.height)

        for i in range(len(em.enemy_list)): # is player collides with enemy

            self.em_rect = Rect(em.enemy_list[i].x, em.enemy_list[i].y, em.width, em.height)
            if (self.player_rect.colliderect(self.em_rect)):
                em.enemy_list[i].on = False
                if(em.enemy_list[i].hit == False):
                    ex.create_explosion(player.pos.x + 2, player.pos.y + 2)
                    em.enemy_list[i].hit = True
                    gm.state = gm.OVER
                    gm.setup(gm.level_manager.get_level())

        for i in range(len(em.enemy_list)): # is enemy missile hits player

            for j in range(len(em.enemy_list[i].missile_list)):
                self.em_rect = Rect(em.enemy_list[i].missile_list[j].x, em.enemy_list[i].missile_list[j].y, em.enemy_missile_manager.width, em.enemy_missile_manager.height)
                if (self.player_rect.colliderect(self.em_rect)):
                    em.enemy_list[i].missile_list[j].on = False
                    ex.create_explosion(player.pos.x + 2, player.pos.y + 2)
                    score.set_score(-1)
                    if(score.score < 0):
                        gm.state = gm.OVER
                        gm.setup(gm.level_manager.get_level())

        for i in range(len(em.enemy_list)): # is player missile hits enemy

            self.em_rect = Rect(em.enemy_list[i].x, em.enemy_list[i].y, em.width, em.height)

            for j in range(len(player.getMissileManager().missile_list)):

                self.mm_rect = Rect(player.getMissileManager().missile_list[j].x, player.getMissileManager().missile_list[j].y, player.getMissileManager().width, player.getMissileManager().height)

                if (self.em_rect.colliderect(self.mm_rect)):
                    em.enemy_list[i].on = False
                    player.getMissileManager().missile_list[j].on = False
                    if (em.enemy_list[i].hit == False):
                        ex.create_explosion(em.enemy_list[i].x, em.enemy_list[i].y + 2)
                        em.enemy_list[i].hit = True
                        score.set_score(1)
                        if(score.score >= gm.level_manager.get_level() * 30):
                            gm.level_manager.increase_level()

As you can see, you will need each level * 30 points to reach the next game level.

Now we will play the game…

Create a pool object for player missile

In this article we will create a pool object inside the player missile manager class which will then be used to recycle those player missile objects. Creating pool object for the player missile is a lot more complicated than creating pool object for the enemy missile because in order to use the player missile pool object we will need to amend a few classes such as the game manager class in the pygame project.

Before we create a player missile pool object we will need to do this first,

1) Put the player missile manager directly under the player object so that player object can control the player missile manager which will then control the player missiles.

Here is the edit version of the player class.

from pygame.locals import *
from GameSprite import GameSprite
from pygame import math as mt
from MissileManager import MissileManager
import pygame

class Player(object):

    def __init__(self, scene):

        self.image = 'Asset/player.png'
        self.scene = scene
        self.width = 40
        self.height = 40
        self.missile_count = 10
        self.direction_x = 0
        self.direction_y = 0
        self.rect = Rect(0, 0, self.width, self.height)
        self.sprite = GameSprite(self.image, self.rect)
        self.sprite_surface = self.sprite.getImage()  # get the player sprite surface
        self.bwidth, self.bheight = 660, 660
        self.pos = mt.Vector2(self.bwidth / 2, self.bheight / 2)  # initialize the position of the player sprite
        self.draw_pos = mt.Vector2(self.pos.x, self.pos.y)
        self.missile_manager = MissileManager(scene)

        pygame.display.set_icon(self.sprite_surface) # use the same player surface object as the icon for the game window

    def setX(self, _x):

        # set new x position and detect the boundary on the game scene
        self.direction_x = _x

    def setY(self, _y):

        # set new y position and detect the boundary on the game scene
        self.direction_y = _y

    def setStrike(self, strike):

        self.missile_manager.setStrike(strike)

    def update(self):

        if(self.direction_x == -0.1):
            if(self.pos.x > 0):
                self.pos.x += self.direction_x
        elif(self.direction_x == 0.1):
            if(self.pos.x + self.width <= self.bwidth):
                self.pos.x += self.direction_x
        if(self.direction_y == -0.1):
            if (self.pos.y > 0):
                self.pos.y += self.direction_y
        elif (self.direction_y == 0.1):
            if (self.pos.y + self.height <= self.bheight):
                self.pos.y += self.direction_y

        self.draw_pos = mt.Vector2(self.pos.x, self.pos.y)
        self.missile_manager.update(self.pos.x, self.pos.y)

    def get(self):
        return (self.pos.x, self.pos.y)


    def draw(self):
        self.scene.blit(self.sprite_surface,  self.draw_pos)
        self.missile_manager.draw()

    def getMissileManager(self):

        return self.missile_manager

Next we will need to edit the player missile manager class which will now include a new pool object to recycle the missile.

from Missile import Missile
from GameSprite import GameSprite
from pygame.locals import *
from Objectpool import Objectpool

class MissileManager(object):

    def __init__(self, scene):
        self.scene = scene
        self.missile_count = 10
        self.missile_list = []
        self.image = 'Asset/missile.png'
        self.width = 20
        self.height = 20
        self.rect = Rect(0, 0, self.width, self.height)
        self.strike = False
        self.strike_again = 0
        self.missile_object_pool = Objectpool(self.missile_count)
        # initialize game sprite object
        self.sprite = GameSprite(self.image, self.rect)

    def setStrike(self, strike):

        # set the missile strike flag
        self.strike = strike
        self.strike_again += 1

    def create_missile(self, x, y):
        if(self.missile_count >= 0):

            if (self.missile_object_pool.getSize() > 0):
                self.missile_list.append(self.missile_object_pool.obtain_missile(x, y))
            else:
                self.missile_surface = self.sprite.getImage()
                self.missile_list.append(Missile(self.missile_surface, x, y))
            self.missile_count -= 1

    def update(self,x,y):
        if (self.strike == True and self.strike_again > 1):
            self.strike_again = 0
            self.create_missile(x + 5, y - 8)  # create more missile
        self.missile_update()
        self.check_boundary()

    def missile_update(self):

        for item in list(self.missile_list):
            if(item.on == False):
                self.missile_list.remove(item)
                self.missile_count += 1
                self.missile_object_pool.recycle(item)
            else:
                item.update()

    def check_boundary(self):

        for i in range(len(self.missile_list)):
            if (self.missile_list[i].y < 0):
                self.missile_list[i].on = False

    def draw(self):

        # blit the missile on  the scene
        for i in range(len(self.missile_list)):
            self.scene.blit(self.missile_list[i].missile_surface, self.missile_list[i].missile_pos)

Finally we will edit the Game Manager class so it will directly call the player object to update as well as to set the strike’s Boolean variable to the True or False condition.

from Player import Player
from Background import Background
from MissileManager import MissileManager
from EnemyManager import EnemyManager
from Overlap import Overlap
import pygame

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.player = Player(self.scene)
        self.background = Background(self.scene)
        self.enemy_manager = EnemyManager(self.scene, self.player)
        self.overlap_manager = Overlap()
        self.load_music()
        self.play_music()

    def load_music(self):
        pygame.mixer_music.load('Music/winternight.ogg')

    def play_music(self):
        pygame.mixer_music.play(-1) #play the music infinite time

    def set_player_x(self, _x):
        self.player.setX(_x)

    def set_player_y(self, _y):
        self.player.setY(_y)

    def set_missile_strike(self, strike):
        self.player.setStrike(strike)

    def update(self):
        self.player.update()
        self.enemy_manager.update()
        self.isOverlap()

    # check for player, enemy, missiles overlap
    def isOverlap(self):
        self.overlap_manager.iseOverlap(self.player, self.enemy_manager)

    def draw(self):
        self.background.draw()
        self.player.draw()
        self.enemy_manager.draw()
        pygame.display.flip()

OK, with that we are now ready to create the exploration manager which will be used to create explosion on the game scene in the next chapter.