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.