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.

Lets put the game instruction online instead

In the previous article we have successfully created an about page which contains both game instruction as well as game credit, however it is better to put the game instruction into it’s own page to make our game looks more professional. In this article we are going to create an online game manual which will open up once the player has clicked on the manual button on the main game page.

Lets go ahead and create the game manual button on the start scene class just like we did previously for the about button. We will also create an offline manual page for this game just like how we create the about page but we are not going to use it at the moment, this page is for the future use just in case we want to bring the manual page offline. Here is the modify version of the start scene class with a new manual button.

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.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.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

        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()

Next we will modify the game manager class, we will create a new game state for the game manual but we are not going to use it now because we will directly open up a web browser with the help of the python’s webbrowser module. Here is the modify version of the game manager class.

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())

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

        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()

    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):
            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)):
                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 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()

That is it, when you have clicked on the manual button on the home scene the online manual page will open up!

Sound on Sound off

In this article we will create a mechanism to turn the soundtrack of our pygame project on and off whenever we click on the sound on’s or sound off’s icon on the main game page. First of all we will need to include the sound on and sound off icon in 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.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.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_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.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_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.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.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.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_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
            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

        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()
Next we will need to edit the game manager class to pause the soundtrack as well as switch the sound on icon to the sound off icon whenever a user has turned off the sound.
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.ABOUT = 5

        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):

            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 or self.state == self.ABOUT):
            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_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_about.collidepoint(x, y) and self.state == self.LOAD):
                self.state = self.ABOUT
            elif (self.rect_back.collidepoint(x, y) and self.state == self.ABOUT):
                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() # pause the music
                else:
                    self.start_scene.soundon = True
                    pygame.mixer_music.unpause()


    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()
Here is the outcome.
sound on
sound off

We will further modify and decorate the main game scene in the future, now lets move on, you might want to read the previous chapters if you miss out the previous tutorials.

Create the about scene for pygame project

In this article we are going to create an about scene which will introduce the game, provides game instructions as well as gives credit to the game creator. The about page will also have a back button which will lead the player back to the main home page.

We will create this about scene under the start scene class and we will also change the home page buttons to three, which is the play button, about button and the score button, we will leave the score button alone now and come back later to create the score scene. For now we will only deal with the about scene.

First let us edit the start scene class by including in more buttons.

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.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.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, 89, 74)

        self.sprite = BgSprite(self.bg_image, self.bg_rect)
        self.sprite_about = BgSprite(self.about_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_button = GameSprite(self.button_image, self.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.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.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.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_back_button_pos = mt.Vector2(0, 586)

    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
        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

        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()

Next we will edit the game manager page by including in an extra state, the about 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

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.ABOUT = 5

        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):

            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 or self.state == self.ABOUT):
            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_back = Rect(0, 586, 89, 74)  # the position of the back 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_about.collidepoint(x, y) and self.state == self.LOAD):
                self.state = self.ABOUT
            elif (self.rect_back.collidepoint(x, y) and self.state == self.ABOUT):
                self.state = self.LOAD


    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()

This is what the about scene looks like…

We will further decorate those scenes in the next chapter so stay tune.

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 enemy missiles within the Enemy object

In this article we are going to edit a few game’s classes that we have created earlier, our main objective here is to detach the enemy missiles from the enemy missile manager, which means instead of putting all the enemy missiles under a single missile list inside the enemy missile manager as we have done previously, we are going to create a separate missile list and a separate missile pool object for each enemy object so we will be able to create a missile timer variable to control the missile’s generating speed as well as to release lot of burdens from the missile manager class which is now no longer needs to keep a missile list and a missile pool object likes before.

Here is the modify version of the enemy missile manager class.

from GameSprite import GameSprite
from pygame.locals import *
from Enemymissile import Enemymissile

class EnemyMissileManager(object):

    def __init__(self):

        self.image = 'Asset/e_missile.png'
        self.width = 20
        self.height = 20
        self.rect = Rect(0, 0, self.width, self.height)

        # initialize game sprite object
        self.sprite = GameSprite(self.image, self.rect)
        self.missile_surface = self.sprite.getImage()

    def create_missile(self, x, y, pool, missile_list):
        if(pool == None):
            missile_list.append(Enemymissile(self.missile_surface, x, y))
        else:
            missile_list.append(pool.obtain_missile(x, y))

As you can see the class now only has one method which is the create missile method which will receive a missile pool object and a missile list from the enemy class.

The enemy class will now take over the previous role of the enemy missile manager class which is to keep it’s own missile list and missile pool object. Now it will also responsible to create a new missile (with time control), update the missiles position and draw the missiles on the scene besides just update it’s own position likes before.

from pygame import math as mt
from Objectpool import Objectpool

class Enemy(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.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):
        self.y += 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 + 5, self.y + 4, self.missile_object_pool, self.missile_list)
            else:
                enemy_missile_manager.create_missile(self.x + 5, self.y + 4, None, self.missile_list)

        else:

            self.missile_timer += 1

We also need to do slightly modification on the enemy missile class by including a game boundary checking in it’s update method.

from pygame import math as mt

class Enemymissile(object):

    def __init__(self, missile_surface, x, y):
        self.on = True
        self.missile_surface = missile_surface
        self.x = x
        self.y = y
        self.height = 20
        self.width = 20
        self.missile_pos = mt.Vector2(self.x, self.y)

    def update(self):

        self.y += 1
        self.missile_pos = mt.Vector2(self.x, self.y)

        if(self.y >= 660):
            self.on = False

    def draw(self, scene):
        scene.blit(self.missile_surface, self.missile_pos)

Next we will need to update the enemy manager class which will call the enemy class’s update, create enemy missile and draw methods.

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):

        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.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)

        # initialize game sprite object
        self.sprite = GameSprite(self.image, 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:
                self.enemy_surface = self.sprite.getImage()
                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)

Then we need to remove the enemy missile manager object from the game manager class because we no longer need the enemy missile manager class in the game manager class.

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(0)

        #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)

        if(self.game_level == 0):
            self.player = Player(self.scene)
            self.enemy_manager = EnemyManager(self.scene, self.player)
            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()

Finally we need to edit the overlap class because now we will need to loop through the missile list inside the individual enemy object instead of from the enemy missile manager likes before.

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 >= 30):
                            gm.level_manager.increase_level()

With that we are now ready to move on to the next stage which is to create the enemy type based on the game level.

Hello people, I have just created my own personal website and if you like me and do want to follow my daily life then kindly visit this site (through this link) which will talk about the daily life of a game developer.

Create a win scene and the level manager class for pygame project

In this article we will create a win scene which will be shown after the player has won the final level of the game, the win scene will then ask the player whether he wants to start that game from the beginning or not? We will also create a framework for the level manager class which we will further add in more features in the next chapter.

First of all, we will modify the start scene class again to include a win scene graphic.

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.button_image = 'Asset/button_play.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.bg_rect = Rect(0, 0, 660, 660)
        self.button_rect = Rect(0, 0,  306, 112)
        self.sprite = BgSprite(self.bg_image, 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_button = GameSprite(self.button_image, self.button_rect)
        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.surface = self.sprite.getImage()  # get the start scene sprite surface
        self.button_surface = self.sprite_button.getImage() # get the button sprite surface
        self.draw_pos = mt.Vector2(0, 0)
        self.draw_button_pos = mt.Vector2(177, 274)

    def draw(self, state):

        if(state == 0):
            self.scene.blit(self.surface, self.draw_pos) # draw a start scene 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

        self.scene.blit(self.button_surface, self.draw_button_pos)  # draw a button sprite
        pygame.display.flip()

Next we will modify the overlap class where we will call the level manager to update the game status instead of directly call the game manager to do the same thing whenever the player has reached a new level.

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_missile_manager.missile_list)): # is enemy missile hits player

            self.em_rect = Rect(em.enemy_missile_manager.missile_list[i].x, em.enemy_missile_manager.missile_list[i].y, em.enemy_missile_manager.width, em.enemy_missile_manager.height)
            if (self.player_rect.colliderect(self.em_rect)):
                em.enemy_missile_manager.missile_list[i].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 >= 30):
                            gm.level_manager.increase_level()

Next We will need to create an instance of the level manager class within the game manager class and check the game level in game manager’s setup method. Besides that we will also create a win state game variable to represent the winning state of the game.

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(0)

        #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)

        if(self.game_level == 0):
            self.player = Player(self.scene)
            self.enemy_manager = EnemyManager(self.scene, self.player)
            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()

As you can see the setup method will check which game level is that and do the game objects setup according to that level. There is only one level at the moment so we will need to further modify the program in the next chapter, for example we will need to modify the enemy manager class because we will need to create different enemies at different level of the game!

Now here is the level manager class.

class LevelManager(object):

    def __init__(self, gm):

        self.game_manager = gm
        self.level = 0

    def increase_level(self):

        self.level += 1
        if(self.level > 1):
            self.game_manager.state = self.game_manager.WIN
            self.level = 0
            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

The class is very simple at the moment, the increase level method either increase the game level or reset the level to 0 if the player has won the game! We can now concentrate on the level manager class in the next chapter and come back again to create a few more scenes like the about scene which contains game instructions as well as the credit scene which gives credit to the game creator. If you like this post don’t forget to click on the red bell button below this post to subscribe to the post’s notification or you also can subscribe to the individual rss feed from the sidebar of this website. There are more articles related to the game creation topic coming out almost everyday so make sure to subscribe to this website to get the latest daily game development article!

Create a next level scene for pygame project

In this article we will create a next level scene which will be displayed whenever the player has gained 30 points. Just like the previous chapter we are going to use back the start scene class as well as modify the game manager class and the overlap class

First of all we will create the next scene surface and draw it on the game scene whenever the state of the game has changed to the next level state.

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.button_image = 'Asset/button_play.png'
        self.bg_image = 'Asset/start.png'
        self.over_image = 'Asset/ove.png'
        self.next_image = 'Asset/next.png'
        self.bg_rect = Rect(0, 0, 660, 660)
        self.button_rect = Rect(0, 0,  306, 112)
        self.sprite = BgSprite(self.bg_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_button = GameSprite(self.button_image, self.button_rect)
        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.surface = self.sprite.getImage()  # get the start scene sprite surface
        self.button_surface = self.sprite_button.getImage() # get the button sprite surface
        self.draw_pos = mt.Vector2(0, 0)
        self.draw_button_pos = mt.Vector2(177, 274)

    def draw(self, state):

        if(state == 0):
            self.scene.blit(self.surface, self.draw_pos) # draw a start scene 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

        self.scene.blit(self.button_surface, self.draw_button_pos)  # draw a button sprite
        pygame.display.flip()

Next we will edit the game manager class by creating the next level game state variable and including it in the relevant methods.

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 *
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.setup()

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

        self.state = self.LOAD

    def setup(self):

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

    def loop(self):

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

            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):
            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 final step is to edit the overlap class where we will change the game state to the next level state if the player has gained 30 points.

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()

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

            self.em_rect = Rect(em.enemy_missile_manager.missile_list[i].x, em.enemy_missile_manager.missile_list[i].y, em.enemy_missile_manager.width, em.enemy_missile_manager.height)
            if (self.player_rect.colliderect(self.em_rect)):
                em.enemy_missile_manager.missile_list[i].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()

        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 >= 30):
                            gm.state = gm.NEXT
                            gm.setup()

We have not started to create the game level class yet because we still need to create the final scene where that scene will pop up telling the player he has won the game and we will do that on the next chapter.

Create a game over scene for pygame project

In this article we are going to create a game over scene for the pygame project, we will use back the start scene class which we have created previously to render our game over scene by slightly modify it for the multi-scenes uses. Here is the modify version of the start scene class, as you can see we have passed in a state variable to the start scene class’s draw method which will be used to determine which scene to be rendered on the screen.

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.button_image = 'Asset/button_play.png'
        self.bg_image = 'Asset/start.png'
        self.over_image = 'Asset/ove.png'
        self.bg_rect = Rect(0, 0, 660, 660)
        self.button_rect = Rect(0, 0,  306, 112)
        self.sprite = BgSprite(self.bg_image, self.bg_rect)
        self.sprite_over = BgSprite(self.over_image, self.bg_rect)
        self.sprite_button = GameSprite(self.button_image, self.button_rect)
        self.over_surface = self.sprite_over.getImage()  # get the game over 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.draw_pos = mt.Vector2(0, 0)
        self.draw_button_pos = mt.Vector2(177, 274)

    def draw(self, state):
        
        if(state == 0):
            self.scene.blit(self.surface, self.draw_pos) # draw a start scene sprite
        else:
            self.scene.blit(self.over_surface, self.draw_pos)  # draw a game over sprite
        self.scene.blit(self.button_surface, self.draw_button_pos)  # draw a button sprite
        pygame.display.flip()

Next we will need to modify the Game Manager class again by including an extra game over state variable and a new setup method into it.

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 *
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.setup()

        #game state
        self.LOAD = 0
        self.GAME = 1
        self.OVER = 2

        self.state = self.LOAD

    def setup(self):

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

    def loop(self):

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

            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):
            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()

We will pass in this class as the reference into the isOverlap method of the overlap manager which then will be used to access the state variable as well as reset the game objects to their original state by calling the newly created setup method of this class.

The last thing we need to do 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()

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

            self.em_rect = Rect(em.enemy_missile_manager.missile_list[i].x, em.enemy_missile_manager.missile_list[i].y, em.enemy_missile_manager.width, em.enemy_missile_manager.height)
            if (self.player_rect.colliderect(self.em_rect)):
                em.enemy_missile_manager.missile_list[i].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()

        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)

The game over scene will be loaded and the game objects will be recreated whenever the player collides with the enemy ship or the player strength falls below zero.

With that done we are now ready to move on to the next stage of the project, which is to create a level manager class to manage all the game levels in this game. If you have not yet subscribed to this website then do make sure to click on the red bell button below this post so you will get a first hand notification whenever a new article has been posted. This pygame project is not yet complete and there are still more articles related to this topic will be posted soon so make sure you subscribe to this website by clicking on that red bell button below if you want to read more articles about the pygame subject!

If you are following this tutorial from the beginning then your pycharm file explorer area should now has all these files below.

The game graphic area
All the game graphics

All the game project files
All the game project files

It is time to move on to the next chapter, see you there!