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…