def __init__(self, screen): """__init__ constructor method @param self @param screen The surface object to render sprites on @desc Ran when a new game object is created. Loads background and other required sprites Stores all relevant/required variables in class scope @exception ResourceException Thrown if the background image cannot be found """ self.screen = screen self.bg = GameBackground(self.screen) self.player = Player(screen) asteroidGroup = pygame.sprite.Group() rewardGroup = pygame.sprite.Group() self.rewardBlit = False i = 3 while i > 0: # Create the 3 instances of the asteroid that exist throughout the game a = Asteroid(screen, asteroidGroup) asteroidGroup.add(a) i -= 1 self.asteroidGroup = asteroidGroup self.rewardGroup = rewardGroup self.playerGroup = pygame.sprite.Group(self.player) # time the game in order to calculate the score self.time = time() self.score = 0 self.rewards = 0 self.multiplier = 1 self.counter = random.randint(0, 750)
class Game: __instance = None @staticmethod def getInstance(screen): """static getInstance @param screen Passed to constructor. See __init__() @return Game Instance @desc Implements the singleton design pattern. Only allows 1 instantation of the game class by returning the existing instance if one exists (and creating a new one if it doesnt). """ if Game.__instance == None: Game.__instance = Game(screen) return Game.__instance def __init__(self, screen): """__init__ constructor method @param self @param screen The surface object to render sprites on @desc Ran when a new game object is created. Loads background and other required sprites Stores all relevant/required variables in class scope @exception ResourceException Thrown if the background image cannot be found """ self.screen = screen self.bg = GameBackground(self.screen) self.player = Player(screen) asteroidGroup = pygame.sprite.Group() rewardGroup = pygame.sprite.Group() self.rewardBlit = False i = 3 while i > 0: # Create the 3 instances of the asteroid that exist throughout the game a = Asteroid(screen, asteroidGroup) asteroidGroup.add(a) i -= 1 self.asteroidGroup = asteroidGroup self.rewardGroup = rewardGroup self.playerGroup = pygame.sprite.Group(self.player) # time the game in order to calculate the score self.time = time() self.score = 0 self.rewards = 0 self.multiplier = 1 self.counter = random.randint(0, 750) def update(self): """update method @param self @return None @desc Update the game state, reload sprites, maintain game state, throw game over when completed, respawn sprites as necessary""" # Decrease the counter for quasi-random reward spawning self.counter -= 1 if self.counter == 0: # Create a new reward when the counter is 0, and reset the timer. This creates a semi-random, none predictable method of rewards spawning self.counter = random.randint(0, 750) self.rewardGroup.add(Reward(self.screen, self.rewardGroup, self.asteroidGroup)) self.bg.update() state = self.player.update(self.asteroidGroup) if state == "Game Over": # Throw the game over event onto the event queue, to be handled in the main while loop event = pygame.event.Event(pygame.USEREVENT, {"name": "Game Over", "score": self.score}) pygame.event.post(event) for asteroid in self.asteroidGroup: asteroid.update() for reward in self.rewardGroup: rewardObtained = reward.update(self.playerGroup, self.asteroidGroup) if rewardObtained == True: # Destroy the reward if the user has obtained it, and increase the multiplier. This leads to steady increase of player score when they collect multiple rewards in a row (without missing one) self.rewards += 20 * self.multiplier self.multiplier += 1 reward.kill() self.rewardBlit = 20 else: # If the player has missed the reward, and its gone of the screen they should lose the multiplier they have already obtained self.multipler = 1 # Update the display self.screen.blit(self.bg.image, (self.bg.rect.x, self.bg.rect.y)) self.screen.blit(self.player.image, (self.player.rect.x, self.player.rect.y)) for asteroid in self.asteroidGroup: self.screen.blit(asteroid.image, (asteroid.rect.x, asteroid.rect.y)) for reward in self.rewardGroup: self.screen.blit(reward.image, (reward.rect.x, reward.rect.y)) # Calculate the current score based on the time they've been playing, and the rewards they've collected self.score = math.floor(((time() - self.time) * 4) + self.rewards) # Display the current score to the user, so they know how they're doing font = pygame.font.Font(None, 20) text = font.render(str(self.score), True, (255, 255, 255), (0, 0, 0)) textrect = text.get_rect() self.screen.blit(text, textrect) # If the player has recently got a reward, display the amount of points they have if self.rewardBlit != False: font = pygame.font.Font(None, 20) text = font.render(("+" + str(20 * (self.multiplier - 1))), True, (255, 255, 255), (0, 0, 0)) textrect = text.get_rect() textrect.x = self.player.rect.x textrect.y = self.player.rect.y - 20 self.screen.blit(text, (textrect.x, textrect.y)) self.rewardBlit -= ( 1 ) # When the player gets an award, rewardBlit is set to the amount of frames it should show for. This is decreased every frame here, triggering the end of the if statement (which executes every loop) def movePlayerUp(self): """movePlayerUp @param self @return None @desc Abstraction method to prevent main while loop from handling all sprites as well as levels. Moves the player up using the player sprite instance""" self.player.moveUp() def movePlayerDown(self): """movePlayerDown @param self @return None @desc Abstraction method to move the player down, so the main while loop doesn't have to handle all sprites individually. Passes to the moveDown method in player instance""" self.player.moveDown() def endGame(self): """endGame @param self @return None @desc Deletes the reference to the class in the singleton design pattern, so a new instance is created next time its requested. The current instance should be garbage collected as there are no remaining references to it This allows a new game to be started without the old state existing, meaning a new score, player position etc """ Game.__instance = None