Create a game’s start scene for pygame project

In this article we are going to create a start scene for our pygame project. The start scene itself looks really simple but the process to render the start scene will involve the modification of a few files.

First of all, lets create the start scene class which will render a background and a play button on the game scene. When we click on that play button the game will start.

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/bg.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_button = GameSprite(self.button_image, self.button_rect)
        self.surface = self.sprite.getImage()  # get the background 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):
        self.scene.blit(self.surface, self.draw_pos) # draw a background sprite
        self.scene.blit(self.button_surface, self.draw_button_pos)  # draw a button sprite
        pygame.display.flip()

Next we will create an instance of the above class in our Game Manager class. Besides that we will do the following.

1) create a new loop method for the Game Manager class which will be called in the main game file to update and draw the objects on the game scene.
2) create the loading state and the game state constant variable together with the state variable which is used to determine whether the game manager should load the start scene or load the game scene.
3) create the isAreaClick method to determine whether the player has clicked on the start scene play button or not, if yes then the game state will change from loading state to gaming state and the game will start.

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.player = Player(self.scene)
        self.background = Background(self.scene)
        self.enemy_manager = EnemyManager(self.scene, self.player)
        self.overlap_manager = Overlap()
        self.explosion_manager = ExplosionManager(self.scene)
        self.score_manager = Score(self.scene)
        self.start_scene = StartScene(scene)
        self.load_music()
        self.play_music()

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

        self.state = self.LOAD

    def loop(self):

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

            self.start_scene.draw()

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

            self.update()
            self.draw()

    def isAreaClick(self, pos):
        if (self.state == self.LOAD): # if it is loading state then response to the click event
            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.iseOverlap(self.player, self.enemy_manager, self.explosion_manager, self.score_manager)

    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 last file we need to edit is the main file where we will add in the click event so we can find out whether the player has clicked on the play button or not. We will also remove the update and the draw method from the Game Manager class and replace them with a single loop method which we have talked about previously.

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

        elif event.type == KEYUP:
            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()

We will continue to modify the Game Manager class and create a game over scene class in the next chapter. If you want to know when will this website posts a new post then don’t forget to click on the bell notification button below this website.

Create a score manager class for pygame project

In this article we will create a score manager class to render the player score on the scene for our pygame project. At the moment we will only increase the score of the player each time the player’s missile hits the enemy ship and deduct the player score each time the player gets hit by the enemy missile, in the future we will introduce more features into the score manager class but for now lets just create a simple score manager class so we can edit it later on. Here is the score manager class.

import pygame.font as txt
from pygame.locals import *

class Score(object):

    def __init__(self, scene):

        self.score = 0
        self.scene = scene
        self.rect = Rect(20,20, 100, 30)
        self.font = txt.Font(None, 30)
        self.score_text = "Score : " + str(self.score)

    def set_score(self, score):

        self.score += score
        self.score_text = "Score : " + str(self.score)

    def draw(self):

        self.text = self.font.render(self.score_text, 1, (255, 255, 255))
        self.scene.blit(self.text, self.rect)

The set score method will be called in the overlap object’s iseOverlap method each time the player gets hit by the enemy missile or the enemy ship gets hit by the player missile. We will pass the score manager object into the iseOverlap method of the Overlap object in each game loop from the Game Manager class.

Lets edit the Game Manager class by creating a new score manager object and then pass the score manager object into the iseOverlap method of the Overlap object in each game loop. We will then call the score manager to draw the new score on the scene in the draw method of the Game Manager.

from Player import Player
from Background import Background
from EnemyManager import EnemyManager
from Overlap import Overlap
from ExplosionManager import ExplosionManager
from Score import Score
import pygame

class GameManager(object):

    def __init__(self, scene):

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

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

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

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

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

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

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

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

    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 Overlap method just needs slightly modification.

from pygame.locals import *

class Overlap(object):

    def __init__(self):
        pass # nothing here

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

        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

        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)

        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)

Here is the result from the above program.

score manager in action
score manager in action

We will revisit the score manager again when we need it but for now it is time to move on to the next topic.

Create an explosion manager and explosion class for pygame project

In this article we will create an explosion manager as well as an explosion class to manage the on scene explosions but first of all lets watch the raw video below which shows the explosions on the game scene.

Before we create the above classes, we will create one single explosion sprite sheet which contains many explosion stages. We will then render each part of the sprite sheet with the help of the timer variable inside the explosion class.

Here is the explosion sprite sheet which we are using in this game.

Explosion
Explosion Sprite Sheet

Here is the explosion class.

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

class Explosion(object):

    def __init__(self, ex_surface, x, y):
        self.on = True
        self.ex_surface = ex_surface
        self.x = x
        self.y = y
        self.ex_pos = mt.Vector2(self.x, self.y)
        self.count = 0
        self.width = 64
        self.height = 64
        self.rect = Rect(self.count * self.width, 0, self.width, self.height)
        self.timer = 0

    def update(self):

        if(self.count <= 9):
            
            if(self.timer > 16):
                self.count += 1
                self.rect = Rect(self.count * self.width, 0, self.width, self.height)
                self.ex_pos = mt.Vector2(self.x, self.y)
                self.timer = 0
            else:
                self.timer += 1
        else:
            self.count = 0
            self.on = False
            self.timer = 0
            self.rect = Rect(self.count * self.width, 0, self.width, self.height)

As you can see we will constantly update the rect variable which will be used by the scene object to draw only a portion of the sprite explosion’s stage from the sprite sheet until all the explosion’s stages have been drawn on the scene which we will then remove the explosion object from the explosion main list. The timer variable is uses to control the creation speed of the new rectangle object so we will see a smooth stage transformation process on the game scene.

And here is the explosion manager class.

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

class ExplosionManager(object):

    def __init__(self, scene):

        self.image = 'Asset/explosion.png'
        self.scene = scene
        self.width = 640
        self.height = 64
        self.explosion_count = 10
        self.count = 0
        self.rect = Rect(0, 0, self.width, self.height)
        self.sprite = GameSprite(self.image, self.rect)
        self.sprite_surface = self.sprite.getImage()  # get the player sprite surface
        self.explosion_object_pool = Objectpool(self.explosion_count)
        self.explosion_list = []

    def create_explosion(self, x, y):

        if (self.explosion_object_pool.getSize() > 0):
            self.explosion_list.append(self.explosion_object_pool.obtain_missile(x, y))
        else:
            self.explosion_list.append(Explosion(self.sprite_surface, x, y))

    def explosion_update(self):

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

    def draw(self):

        # blit the explosion on  the scene
        for i in range(len(self.explosion_list)):
            self.scene.blit(self.explosion_list[i].ex_surface, self.explosion_list[i].ex_pos, self.explosion_list[i].rect)

The methods within the above class are almost the same as the methods use by the other manager classes, as you can see we have created an explosion pool object to recycle the explosion object just like what we did before in the other manager classes.

Finally we need to modify the Game Manager class and the Overlap class.

from Player import Player
from Background import Background
from EnemyManager import EnemyManager
from Overlap import Overlap
from ExplosionManager import ExplosionManager
import pygame

class GameManager(object):

    def __init__(self, scene):

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

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

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

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

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

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

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

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

    def draw(self):
        self.background.draw()
        self.player.draw()
        self.enemy_manager.draw()
        self.explosion_manager.draw()
        pygame.display.flip()
from pygame.locals import *

class Overlap(object):

    def __init__(self):
        pass # nothing here

    # is player and enemy, player missile, enemy missile overlap
    def iseOverlap(self, player, em, ex):

        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

        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)

        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

Besides the above, we also need to include a hit variable in the enemy class and set it to false so the explosion will not happen twice due to many time of contact between the enemy ship and the same missile as well as the enemy ship and the player ship during the game. Also don’t forget to set the hit variable of the enemy to false again within the obtain method of the object pool class.

That is it, after passing the explosion manager into the Overlap class we can now create an explosion object whenever there is any contact happens between the game objects on the scene.

Create a pool object for player missile

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

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

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

Here is the edit version of the player class.

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

class Player(object):

    def __init__(self, scene):

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

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

    def setX(self, _x):

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

    def setY(self, _y):

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

    def setStrike(self, strike):

        self.missile_manager.setStrike(strike)

    def update(self):

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

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

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


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

    def getMissileManager(self):

        return self.missile_manager

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

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

class MissileManager(object):

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

    def setStrike(self, strike):

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

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

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

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

    def missile_update(self):

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

    def check_boundary(self):

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

    def draw(self):

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

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

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

class GameManager(object):

    def __init__(self, scene):

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

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

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

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

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

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

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

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

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

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

Create the pool object for Enemy Missile class

In this article we will continue to create a new missile pool object which will be used to recycle the enemy missile object just like the pool object which will be used to recycle the enemy ship object in the previous article. Basically we will use back the previous object pool class which we have created earlier and then add in a new obtain missile method which takes in the x and y coordinate of the enemy ship at that moment that we can later use as the starting point for the missile object returns from the pool. Here is the class.

class Objectpool(object):

    def __init__(self, size):

        self.size = size
        self.count = 0
        self.elist = [None]*size

    def recycle(self, item):

        if (self.count  < self.size):
            self.elist[self.count] = item
            self.count += 1

    def obtain(self):

        if (self.count > 0):
            self.count -= 1
            self.item = self.elist[self.count]
            self.elist[self.count] = None
            self.item.missile_count = 1
            return self.item

    def obtain_missile(self, x, y): # get the missile object from the pool

        if (self.count > 0):
            self.count -= 1
            self.item = self.elist[self.count]
            self.elist[self.count] = None
            self.item.x = x
            self.item.y = y
            self.item.on = True
            return self.item

    def getSize(self):

        return self.count

That is it, now lets amend the Enemy Missile Manager class by adding in the create missile from pool method which will be used to obtain the old missile object from the object pool.

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

class EnemyMissileManager(object):

    def __init__(self, scene, player):

        self.scene = scene
        self.player = player
        self.missile_count = 60
        self.missile_list = []
        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)

    def create_missile(self, x, y):

        if(self.missile_count >= 0):
            self.missile_surface = self.sprite.getImage()
            self.missile_list.append(Enemymissile(self.missile_surface, x, y))
            self.missile_count -= 1

    def create_missile_from_pool(self, x, y, pool):

        if (self.missile_count >= 0):
            self.missile_list.append(pool.obtain_missile(x, y))
            self.missile_count -= 1

    def update(self, pool):

        self.missile_update(pool)
        self.check_boundary()

    def missile_update(self, pool):

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

    def check_boundary(self):

        for i in range(len(self.missile_list)):
            if (self.missile_list[i].y + self.missile_list[i].height > 660):
                self.missile_list[i].on = False

    def draw(self):

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

We now need to do the last step which is to edit 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):

        self.enemy_missile_manager = EnemyMissileManager(scene, player)
        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)
        self.missile_object_pool = Objectpool(self.missile_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()
        self.enemy_missile_manager.update(self.missile_object_pool)

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

                if (item.missile_count > 0):

                    if (self.missile_object_pool.getSize() > 0):
                        self.enemy_missile_manager.create_missile_from_pool(item.x + 5, item.y + 4, self.missile_object_pool)

                    else:
                        self.enemy_missile_manager.create_missile(item.x + 5, item.y + 4)

                    item.missile_count -= 1

    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 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)
        # draw enemy missiles
        self.enemy_missile_manager.draw()

That is it! With that we will be able to reuse the old enemy missile object instead of creating a new one. The next article will still be dealing with the pool object and this time we will create an object pool for the player missile object!

Create a pool object for enemy ships

In this article we will start to create a pool object which will recycle the enemy ship and then we will create a new object pool for the missiles on the next article.

Before we can create a pool object we will need to create the Objectpool class first which has two important methods:-

1) Put the enemy ship to an object pool when it gets removed by the enemy list.
2) Return the ship again to the enemy list when the Enemy Manager needs to create a new ship.

Here is the Objectpool class.

class Objectpool(object):

    def __init__(self, size):

        self.size = size
        self.count = 0
        self.elist = [None]*size

    def recycle(self, item):

        if (self.count  < self.size):
            self.elist[self.count] = item
            self.count += 1

    def obtain(self):

        if (self.count > 0):
            self.count -= 1
            self.item = self.elist[self.count]
            self.elist[self.count] = None
            self.item.missile_count = 1
            return self.item

    def getSize(self):

        return self.count

In order to use the object pool class we need to initialize it first and make a few changes in 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):

        self.enemy_missile_manager = EnemyMissileManager(scene, player)
        self.scene = scene
        self.player = player
        self.enemy_count = 10
        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):
                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()
        self.enemy_missile_manager.update()

    def create_enemy_missile(self):
        for item in list(self.enemy_list):
            if(item.missile_count > 0):
                if(self.player.pos.y - item.y  < 100 and abs(self.player.pos.x - item.x) < 60 ):
                    self.enemy_missile_manager.create_missile(item.x + 5, item.y + 4)
                    item.missile_count -= 1

    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 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)
        # draw enemy missiles
        self.enemy_missile_manager.draw()

That is it, now we do not need to create a brand new enemy ship whenever we need one because we can use the old one from the object pool!

Create the Overlap class for Pygame project

Hello there, sorry for a little bit late today because I am busy setting up my old website which is now ready for me to add in more articles into it, if you are interested in more programming articles then do visit this site because I am going to create a brand new laptop application project with python and if you are a python lover then go ahead and bookmark this site and visit it starting from tomorrow!. Gaming Directional, which is the site we both are in now will only concentrate on writing game code so if you want to read other programming articles besides the game one then do visit the above mentioned site instead. OK so much for that lets look at another of the new class I have created for the previous pygame project, which is the Overlap class.

We will call the Overlap class object on every game loop to determine the following:-

1) Does the player ship gets hit by the enemy missile? If so then we will remove that enemy missile.
2) Does the player ship hits one of the enemy ship? If so then we will remove that enemy ship.
3) Does the enemy ship gets hit by a player missile? If so then remove both of them.

We will not going to do anything on the player ship yet even if it gets hit by the enemy ship or missile because we will deal with this issue later on.

Here is the entire 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 iseOverlap(self, player, em, pm):

        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

        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

        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(pm.missile_list)):

                self.pm_rect = Rect(pm.missile_list[j].x, pm.missile_list[j].y, pm.width, pm.height)

                if (self.em_rect.colliderect(self.pm_rect)):
                    em.enemy_list[i].on = False
                    pm.missile_list[j].on = False

We also need to edit the Game Manager class like this.

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

class GameManager(object):

    def __init__(self, scene):

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

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

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

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

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

    def set_missile_strike(self, strike):
        self. missile_manager.setStrike(True)

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

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

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

And here is the outcome

Alright then, after this class we will continue to create the recycle class tomorrow which we will use to recycle and then reuse back the game object so we do not need to create a new one which will increase the burden of the computer’s processor.

Create Enemy Missile and Enemy Missile Manager

In this article we will create two new classes, enemy missile class and the enemy missile manager class. The enemy missile manager class will be called during each game loop by the enemy manager class to create new enemy missile as well as to update the position of those missiles and draw them on the game scene.

First of all, we will create the enemy missile class which will be used by the enemy missile manager class to create the enemy missile object.

This enemy missile class looks simple and it indeed is simple.

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)

The only method which is matter in the above class is the update method which will be used to update the position of that enemy missile in every game loop.

The next class is the enemy missile manager class which will be used to create and to control the enemy missiles.

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

class EnemyMissileManager(object):
    
    def __init__(self, scene, player):

        self.scene = scene
        self.player = player
        self.missile_count = 60
        self.missile_list = []
        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)

    def create_missile(self, x, y):

        if(self.missile_count >= 0):
            self.missile_surface = self.sprite.getImage()
            self.missile_list.append(Enemymissile(self.missile_surface, x, y))
            self.missile_count -= 1

    def update(self):

        self.missile_update()
        self.check_boundary()

    def missile_update(self):

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

    def check_boundary(self):

        for i in range(len(self.missile_list)):
            if (self.missile_list[i].y + self.missile_list[i].height > 660):
                self.missile_list[i].on = False

    def draw(self):

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

The enemy missile manager class above has below methods:

1) create missile method will create more enemy missile and will be called by the enemy manager class.
2) the update method will call the missile update and the check missile boundary method that will update the position of those missiles and remove those missiles after they have crossed the game scene boundary.
3) the draw method will draw those missiles on the game scene.

Now after we have created those two new classes, we will need to modify the enemy manager class which we have created previously.

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

class EnemyManager(object):

    def __init__(self, scene, player):

        self.enemy_missile_manager = EnemyMissileManager(scene, player)
        self.scene = scene
        self.player = player
        self.enemy_count = 10
        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

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

    def create_enemy(self, x, y):
        if(self.enemy_count > 0):
            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()
        self.enemy_missile_manager.update()

    def create_enemy_missile(self):
        for item in list(self.enemy_list):
            if(item.missile_count > 0):
                if(self.player.pos.y - item.y  < 100 and abs(self.player.pos.x - item.x) < 60 ):
                    self.enemy_missile_manager.create_missile(item.x + 5, item.y + 4)
                    item.missile_count -= 1

    def enemy_update(self):

        for item in list(self.enemy_list):
            if(item.on == False):
                self.enemy_list.remove(item)
                self.enemy_count += 1
            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 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)
        # draw enemy missiles
        self.enemy_missile_manager.draw()

The create enemy missile method under the update method will create new enemy missiles during the each game loop update and the missile will only get created when the enemy ship is getting close enough to the player ship because there is no point to fire the missile if the distance between the player ship and the enemy ship is so far apart. We also will need to check for the total missiles an enemy ship can launch before this method can create a new missile from that particular enemy ship. We might want to assign each enemy with it’s own enemy missile manager object so we can better control the missile launching time later on but for now we will create only one enemy missile manager object which will be used by the enemy manager object to create new enemy missiles. The update method of the enemy manager also will call the update method of the enemy missile manager which we have already gone through previously.

Finally the enemy manager draw method will call the enemy missile manager’s draw method to draw those missiles on the game scene during each game loop.

As you can see no contact has been detected yet in this game, we will create the contact manager in our up and coming articles to find out whether the player has hit the enemy ship, the enemy ship has been hit by the player missile and the player has been hit by the enemy missile or not!

Create Enemy Manager Class and Enemy Class in a Pygame Project

Hello again, today chapter will be very short but rich in content. As I have mentioned earlier, we have finished the stage one of the game creation process yesterday and today we have entered the stage two of our game creation process, which is to create the enemy, enemy missile, level and the exploration classes, we certainly are getting very close now to finish up this latest pygame project.

In this short article you will read all the code for the Enemy Manager and Enemy Class plus I do have a special surprise for you at the end of this article so make sure you read it till the end.

This is the part one of the three part enemy’s class related article series and in this part we will create enemy spaceships that will come down from a certain distance above the game scene, the enemy object which get passed the bottom of the game scene will get removed by the Enemy Manager which will then create a new enemy object after it has removed the old one. We will generate the enemy object from a random x position within this game scene with it’s y position start off from a fix distance above the top of the game scene. Besides that, we have also included the time control variable in the Enemy Manager class so this class will generate the enemy at an acceptable speed. The contact detection between enemy and player object, recycle of the enemy object, enemy missile object and many more methods and classes will be created in the upcoming articles.

Below is the enemy class with an update method which will move the enemy to a new y position in every game loop.

from pygame import math as mt

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.enemy_pos = mt.Vector2(self.x, self.y)

    def update(self):
        self.y += 0.1
        self.enemy_pos = mt.Vector2(self.x, self.y)

Here is the enemy manager class which is used to control the enemy with the features I have mentioned above.

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

class EnemyManager(object):

    def __init__(self, scene, player):

        self.scene = scene
        self.player = player
        self.enemy_count = 10
        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

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

    def create_enemy(self, x, y):
        if(self.enemy_count > 0):
            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()

    def enemy_update(self):

        for item in list(self.enemy_list):
            if(item.on == False):
                self.enemy_list.remove(item)
                self.enemy_count += 1
            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 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)

After we have created these two classes it is time to modify the Game Manager class which will call the Enemy Manager Class to create more enemy as well as updates the position of those enemies on the game scene plus draws the enemy object on the scene.

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

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.player = Player(self.scene)
        self.background = Background(self.scene)
        self.missile_manager = MissileManager(self.scene, self.player)
        self.enemy_manager = EnemyManager(self.scene, self.player)
        self.load_music()
        self.play_music()

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

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

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

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

    def set_missile_strike(self, strike):
       self. missile_manager.setStrike(True)

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

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

Now run the main pygame program again and you will get the below outcome. As you can see the enemy ship will just get through the player ship with no contact with that player ship as well as no missile shooting which we will deal with in the next articles.

HERE IS THE BONUS:

Below is the game music which I am going to use in this new pygame project, besides a game developer I am also a soundtrack creator, you can subscribe and listen to those new musics on my soundcloud channel.

Game Manager Class and the modify version of the main pygame file

Finally I have finished linking everything together and get ready for the next stage. Here is the Game Manager class which is the only class we need in the main pygame project file. This class contains everything we need to control game objects, play background music and render the game graphics on the game scene.

from Player import Player
from Background import Background
from MissileManager import MissileManager
import pygame

class GameManager(object):

    def __init__(self, scene):

        self.scene = scene
        self.player = Player(self.scene)
        self.background = Background(self.scene)
        self.missile_manager = MissileManager(self.scene, self.player)
        self.load_music()
        self.play_music()

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

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

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

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

    def set_missile_strike(self, strike):
       self. missile_manager.setStrike(True)

    def update(self):
        self.player.update()
        self.missile_manager.update()

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

No new method has been introduced in this class, what we do is just to group the unorganized code in the previous main pygame file to a single Game Manager class. The main python file now looks really neat because all we need is just a single Game Manager class to control everything. Here is the modify version of the main pygame file.

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

        elif event.type == KEYUP:
            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)

    game_manager.update()
    game_manager.draw()

In the next few chapters we will create the enemy ship class as well as the explosion class and the game level class which will be used to control the level of the game.