class GameScene: def __init__(self): self.__sprite_list = pygame.sprite.Group() self.__wall_sprite_list = pygame.sprite.Group() self.__obstacle_sprite_list = pygame.sprite.Group() self.__particles_list = pygame.sprite.Group() self.__slot_1 = 255 self.__slot_2 = 330 self.__slot_3 = 406 self.__slot_4 = 481 self.__slots = [self.__slot_1, self.__slot_2, self.__slot_3, self.__slot_4] self.__obstacle_sprites = [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "obstacle_01.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "obstacle_02.png"))] self.__particle_sprites = [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "explosion_01.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "explosion_02.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "explosion_03.png"))] self.__boulder_sprites = [[pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back.png"))], [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_ML.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_ML.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_ML.png"))], [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_M.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_M.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_M.png"))], [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_SM.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_SM.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_SM.png"))], [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_S.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_S.png")), pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_S.png"))]] self.__background = pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "world.png")) # the camera's x position should never change as the boulder will never venture off the course self.__camera_y_offset = 0 def start(self): #pygame.mixer.music.load("Assets\\level_music.ogg") #pygame.mixer.music.play(-1) self.__sprite_list.empty() self.__wall_sprite_list.empty() self.__obstacle_sprite_list.empty() self.__particles_list.empty() self._create_player() self._create_world() self.__boulder.change_velocity([0, 0.33]) def _create_player(self): boulder_x_pos = GameConstants.SCREEN_WIDTH / 2 boulder_y_pos = 0 self.__boulder = Boulder([boulder_x_pos, boulder_y_pos], GameConstants.BOULDER_SIZE, GameConstants.BOULDER_HEALTH, self.__boulder_sprites) self.__sprite_list.add(self.__boulder) def _create_world(self): wall_size = [GameConstants.WALL_THICKNESS, GameConstants.MAP_HEIGHT] wall_sprite = pygame.Surface(wall_size) wall_sprite.fill(GameConstants.BLUE) left_wall = Wall([240, 0], wall_size, wall_sprite, LEFT_WALL_ID) right_wall = Wall([550, 0], wall_size, wall_sprite, RIGHT_WALL_ID) self.__walls = [left_wall, right_wall] # re-enable to see the wall boundaries #self.__sprite_list.add(left_wall) #self.__sprite_list.add(right_wall) self.__wall_sprite_list.add(left_wall) self.__wall_sprite_list.add(right_wall) self.__world_shift = 0 self._create_obstacles() def _create_obstacles(self): # no obstacles can be created in the first 5 rows row = 6 # 250 rows = 20 (800 x 600) screens while row < GameConstants.MAP_ROWS: # maybe make these vars to adjust difficulty to_create = randint(0, 1) number_of_obstacles = 0 obstacles_for_row = [] while number_of_obstacles < to_create: obstacles_for_row.append(self._create_obstacle(row, obstacles_for_row)) number_of_obstacles += 1 for obstacle in obstacles_for_row: self.__obstacle_sprite_list.add(obstacle) row += 1 def _create_obstacle(self, row, obstacles_for_row): position = self._compute_unique_obstacle_coordinates(row, obstacles_for_row) sprite = self.__obstacle_sprites[self._get_random_obstacle_sprite_index()] obstacle = Obstacle(position, GameConstants.CHUNK_SIZE, GameConstants.OBSTACLE_HEALTH, sprite) return obstacle def _get_random_obstacle_sprite_index(self): sprite_index = randint(0, (len(self.__obstacle_sprites) - 1)) return sprite_index def _compute_unique_obstacle_coordinates(self, row, obstacles_in_row): unique_coordinates_found = False coordinates = None if len(obstacles_in_row) < 1: coordinates = self._compute_obstacle_coordinates(row) unique_coordinates_found = True while not unique_coordinates_found: coordinates = self._compute_obstacle_coordinates(row) for obstacle in obstacles_in_row: x_diff = coordinates[0] != obstacle.get_position()[0] y_diff = coordinates[1] != obstacle.get_position()[1] if x_diff or y_diff: unique_coordinates_found = True return coordinates def _compute_obstacle_coordinates(self, row): slot = randint(0, (len(self.__slots)-1)) y_pos = row*GameConstants.ROW_HEIGHT # map is broken up into 64 x 64 chunks x_pos = self.__slots[slot] return [x_pos, y_pos] def _check_for_collisions(self): self._check_for_wall_collisions() self._check_for_obstacle_collisions() def _check_for_wall_collisions(self): wall_collisions = pygame.sprite.spritecollide(self.__boulder, self.__wall_sprite_list, False) boulder_position = self.__boulder.get_position() boulder_direction_vector = self.__boulder.get_velocity() original_position = boulder_position[:] for wall in wall_collisions: # apply a speed reduction to the boulder_velocity on a collision x_speed_reduction = 0.75*abs(boulder_direction_vector[0]) if boulder_direction_vector[0] < 0 and wall.get_id() == LEFT_WALL_ID: boulder_position[0] = wall.rect.right self.__boulder.update_position(boulder_position) boulder_direction_vector[0] += x_speed_reduction elif boulder_direction_vector[0] > 0 and wall.get_id() == RIGHT_WALL_ID: boulder_position[0] = wall.rect.left - self.__boulder.get_size()[0] self.__boulder.update_position(boulder_position) boulder_direction_vector[0] -= x_speed_reduction # take the velocity of the boulder and reverse the direction boulder_direction_vector[0] *= -1 self.__boulder.change_velocity(boulder_direction_vector) self._deal_damage_to_boulder(GameConstants.WALL_DAMAGE, original_position) def _deal_damage_to_boulder(self, damage, position): particle = Particles(position, self.__particle_sprites) self.__particles_list.add(particle) self.__boulder.take_damage(damage) def _check_for_obstacle_collisions(self): obstacle_collisions = pygame.sprite.spritecollide(self.__boulder, self.__obstacle_sprite_list, False) for obstacle in obstacle_collisions: """boulder_direction_vector = self.__boulder.get_velocity() #self._adjust_boulder_from_collision(obstacle) # apply a speed reduction to the boulder_velocity on a collision x_speed_reduction = 0.75*abs(boulder_direction_vector[0]) y_speed_reduction = 0.90*abs(boulder_direction_vector[1]) if boulder_direction_vector[0] < 0: boulder_direction_vector[0] += x_speed_reduction elif boulder_direction_vector[0] > 0: boulder_direction_vector[0] -= x_speed_reduction boulder_direction_vector[1] -= y_speed_reduction # take the velocity of the boulder and reverse the direction boulder_direction_vector[0] *= -1 self.__boulder.change_velocity(boulder_direction_vector)""" self._deal_damage_to_boulder(self.__boulder.get_health(), obstacle.get_position()) def _adjust_boulder_from_collision(self, obstacle): boulder_x = self.__boulder.get_position()[0] boulder_y = self.__boulder.get_position()[1] intersect_on_boulder_left = (obstacle.rect.left <= self.__boulder.rect.left) and (self.__boulder.rect.left <= obstacle.rect.right) intersect_on_boulder_right = (obstacle.rect.left <= self.__boulder.rect.right) and (self.__boulder.rect.right <= obstacle.rect.right) intersect_on_boulder_top = (obstacle.rect.top <= self.__boulder.rect.top) and (self.__boulder.rect.top <= obstacle.rect.bottom) intersect_on_boulder_bottom = (obstacle.rect.top <= self.__boulder.rect.bottom) and (self.__boulder.rect.bottom <= obstacle.rect.bottom) if intersect_on_boulder_bottom and intersect_on_boulder_left: boulder_x = obstacle.rect.right boulder_y = obstacle.rect.top - self.__boulder.get_size()[1] elif intersect_on_boulder_top and intersect_on_boulder_left: boulder_x = obstacle.rect.right boulder_y = obstacle.rect.bottom elif intersect_on_boulder_bottom and intersect_on_boulder_right: boulder_x = obstacle.rect.left - self.__boulder.get_size()[0] boulder_y = obstacle.rect.top - self.__boulder.get_size()[1] elif intersect_on_boulder_top and intersect_on_boulder_right: boulder_x = obstacle.rect.left - self.__boulder.get_size()[0] boulder_y = obstacle.rect.bottom elif intersect_on_boulder_right: boulder_x = obstacle.rect.left - self.__boulder.get_size()[0] elif intersect_on_boulder_left: boulder_x = obstacle.rect.right elif intersect_on_boulder_top: boulder_y = obstacle.rect.bottom elif intersect_on_boulder_bottom: boulder_y = obstacle.rect.top - self.__boulder.get_size()[1] self.__boulder.update_position([boulder_x, boulder_y]) def _check_particles_have_dissipated(self): index_list = [] for index, particles in enumerate(self.__particles_list): if particles.have_dissipated(): index_list.append(index) for index in index_list: del self.__particles_list[index] def draw(self, screen): # blue layer screen.fill(GameConstants.BLUE) # background layer screen.blit(self.__background, (0, 0-self.__boulder.get_position()[1])) for obstacle in self.__obstacle_sprite_list: display_coordinates = obstacle.get_position() display_coordinates[1] -= self.__boulder.get_velocity()[1] screen.blit(obstacle.image, (display_coordinates[0], display_coordinates[1])) # print('Number of particles: ' + str(len(self.__particles_list))) for particles in self.__particles_list: if not particles.have_dissipated(): screen.blit(particles.image, (particles.get_position()[0], 27)) # entities layer screen.blit(self.__boulder.image, (self.__boulder.get_position()[0], 25)) def update(self): self.__camera_y_offset = self.__boulder.get_position()[1] # quirk using this method -- need to figure out how to get the most recently pressed key and use that one keys_pressed = pygame.key.get_pressed() if keys_pressed[pygame.K_LEFT]: self.__boulder.move_left() elif keys_pressed[pygame.K_RIGHT]: self.__boulder.move_right() elif keys_pressed[pygame.K_UP]: self.__boulder.move_up() elif keys_pressed[pygame.K_DOWN]: self.__boulder.move_down() elif keys_pressed[pygame.K_ESCAPE]: return GameStatus.STATUS_QUIT self._check_for_collisions() # temporary disabled the clean up of the dissipated particles -- optimize this later # self._check_particles_have_dissipated() if self.__boulder.is_dead(): #pygame.mixer.music.stop() return GameStatus.TRANSITION_GAME_OVER # signal the game over screen # has the boulder crossed the finish line if self.__boulder.get_position()[1] > GameConstants.MAP_HEIGHT: #pygame.mixer.music.stop() self.__boulder.stop_rolling() if self.__boulder.get_health() > 2*GameConstants.BOULDER_HEALTH/3: return GameStatus.TRANSITION_GOOD_ENDING else: return GameStatus.TRANSITION_BAD_ENDING self.__sprite_list.update() self.__obstacle_sprite_list.update() self.__particles_list.update()
class GameScene(Scene): def __init__(self, controller): super(GameScene, self).__init__(controller) self.__sprite_list = pygame.sprite.Group() self.__wall_sprite_list = pygame.sprite.Group() self.__obstacle_sprite_list = pygame.sprite.Group() self.__particles_list = pygame.sprite.Group() self.__slot_1 = 255 self.__slot_2 = 330 self.__slot_3 = 406 self.__slot_4 = 481 self.__slots = [self.__slot_1, self.__slot_2, self.__slot_3, self.__slot_4] self.__background = Images.WORLD_01 self.__obstacleTypes = [Rock(), TwinRock()] # the camera's x position should never change as the boulder will never venture off the course self.__camera_y_offset = 0 self.__boulder_rolling_sound = Sounds.ROCK_ROLLING self.__rock_breaking_sound = Sounds.ROCK_BREAKING def reset(self): super(GameScene, self).reset() pygame.mixer.music.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "level_music.ogg")) pygame.mixer.music.play(-1) self.__sprite_list.empty() self.__wall_sprite_list.empty() self.__obstacle_sprite_list.empty() self.__particles_list.empty() self.__create_player() self.__create_world() self.__boulder.velocity = Boulder.INITIAL_VELOCITY.copy() self.__boulder_rolling_sound.play(-1) def __create_player(self): self.__boulder = Boulder(Vector((GameConstants.SCREEN_WIDTH - Boulder.INITIAL_SIZE.x)//2, 0)) self.__sprite_list.add(self.__boulder) def __create_world(self): left_wall = Wall(Vector(240, 0), LEFT_WALL_ID) right_wall = Wall(Vector(550, 0), RIGHT_WALL_ID) self.__walls = [left_wall, right_wall] self.__wall_sprite_list.add(left_wall) self.__wall_sprite_list.add(right_wall) self.__world_shift = 0 self.__create_obstacles() def __create_obstacles(self): # no obstacles can be created in the first 5 rows row = 6 # 250 rows = 20 (800 x 600) screens while row < GameConstants.MAP_ROWS: # maybe make these vars to adjust difficulty to_create = randint(0, 2) number_of_obstacles = 0 obstacles_for_row = [] while number_of_obstacles < to_create: obstacles_for_row.append(self.__create_obstacle(row, obstacles_for_row)) number_of_obstacles += 1 for obstacle in obstacles_for_row: self.__sprite_list.add(obstacle) self.__obstacle_sprite_list.add(obstacle) row += 1 def __create_obstacle(self, row, obstacles_for_row): position = self.__compute_unique_obstacle_coordinates(row, obstacles_for_row) obstacle_type = self.__obstacleTypes[self.__get_random_obstacle_sprite_index()] obstacle = Obstacle(position, obstacle_type, "obstacle_not_unique") return obstacle def __get_random_obstacle_sprite_index(self): sprite_index = randint(0, len(self.__obstacleTypes) - 1) return sprite_index def __compute_unique_obstacle_coordinates(self, row, obstacles_in_row): unique_coordinates_found = False coordinates = None if len(obstacles_in_row) < 1: coordinates = self.__compute_obstacle_coordinates(row) unique_coordinates_found = True while not unique_coordinates_found: coordinates = self.__compute_obstacle_coordinates(row) for obstacle in obstacles_in_row: if coordinates != obstacle.position: unique_coordinates_found = True return coordinates def __compute_obstacle_coordinates(self, row): slot = randint(0, (len(self.__slots)-1)) y_pos = row*GameConstants.ROW_HEIGHT # map is broken up into 64 x 64 chunks x_pos = self.__slots[slot] return Vector(x_pos, y_pos, 0) def __check_for_collisions(self): if self.__check_for_wall_collisions() or self.__check_for_obstacle_collisions(): self.__rock_breaking_sound.play(0) def __check_for_wall_collisions(self): wall_collisions = pygame.sprite.spritecollide(self.__boulder, self.__wall_sprite_list, False) original_position = self.__boulder.position.copy() for wall in wall_collisions: # apply a speed reduction to the boulder_velocity on a collision x_speed_reduction = 0.75*abs(self.__boulder.velocity.x) if self.__boulder.velocity.x < 0 and wall.get_object_id() == LEFT_WALL_ID: self.__boulder.position.x = wall.rect.right self.__boulder.velocity.x += x_speed_reduction self.__particles_list.add(Particles(Vector(wall.rect.right-Boulder.INITIAL_SIZE.x//4, original_position.y), "particles_not_unique")) elif self.__boulder.velocity.x > 0 and wall.get_object_id() == RIGHT_WALL_ID: self.__boulder.position.x = wall.rect.left - self.__boulder.get_size().x self.__boulder.velocity.x -= x_speed_reduction self.__particles_list.add(Particles(Vector(wall.rect.left-Boulder.INITIAL_SIZE.x//4, original_position.y), "particles_not_unique")) # take the velocity of the boulder and reverse the direction self.__boulder.velocity.x *= -1 self.__deal_damage_to_boulder(Wall.DAMAGE) return True return False def __deal_damage_to_boulder(self, damage): self.__boulder.take_damage(damage) def __check_for_obstacle_collisions(self): obstacle_collisions = pygame.sprite.spritecollide(self.__boulder, self.__obstacle_sprite_list, True, self.__is_boulder_obstacle_collision) collision_count = len(obstacle_collisions) if collision_count > 0: self.__particles_list.add(Particles(self.__boulder.position.copy(), "particles_not_unique")) self.__deal_damage_to_boulder(5*collision_count) return True return False def __is_boulder_obstacle_collision(self, boulder, obstacle): return rectangle_intersection(boulder.rect, obstacle.get_bounding_box()) def __check_particles_have_dissipated(self): to_remove = [] for particles in self.__particles_list: if particles.have_dissipated(): to_remove.append(particles) for particles in to_remove: self.__particles_list.remove(particles) def __get_object_display_coordinates(self, game_object): display_coordinates = game_object.position.copy() display_coordinates.y -= (self.__boulder.position.y - GameConstants.BOULDER_DRAW_OFFSET) return display_coordinates.x, display_coordinates.y def draw(self, screen): super(GameScene, self).draw(screen) screen.blit(self.__background, (0, 0-self.__boulder.position.y)) # sort the game_objects based on y-position so that they render properly game_objects = [self.__boulder] + [obstacle for obstacle in self.__obstacle_sprite_list] game_objects = sorted(game_objects, key=lambda game_object: game_object.rect.centery) for sprite in game_objects: screen.blit(sprite.image, (self.__get_object_display_coordinates(sprite))) """ # -- DEBUG -- display obstacle bounding boxes bounding_box = sprite.get_bounding_box() collision_box = pygame.Surface([bounding_box.width, bounding_box.height]) collision_box.fill(GameConstants.BLUE) collision_box_position_x = bounding_box.x collision_box_position_y = bounding_box.y - self.__boulder.position.y + 25 screen.blit(collision_box, (collision_box_position_x, collision_box_position_y))""" for particles in self.__particles_list: if not particles.have_dissipated(): screen.blit(particles.image, (particles.position.x, GameConstants.BOULDER_DRAW_OFFSET + 2)) """ # -- DEBUG -- screen.blit(self.__boulder.image, (self.__boulder.position.x, 25)) for wall in self.__wall_sprite_list: display_coordinates = wall.position display_coordinates.y -= self.__boulder.velocity.y screen.blit(wall.image, (display_coordinates.x, display_coordinates.y)) """ def __apply_friction(self): x_friction = 0.005 # -- apply vertical friction if self.__boulder.velocity.y >= Boulder.MIN_Y_VELOCITY: self.__boulder.velocity.y -= 0.005 if (self.__boulder.velocity.x > 0 and self.__boulder.velocity.x - x_friction < 0) or \ (self.__boulder.velocity.x < 0 and self.__boulder.velocity.x + x_friction > 0): self.__boulder.velocity.x = 0 elif self.__boulder.velocity.x > 0: self.__boulder.velocity.x -= x_friction elif self.__boulder.velocity.x < 0: self.__boulder.velocity.x += x_friction def __update_boulder(self): if self._controller.raise_move_backward(): self.__boulder.decelerate() if self._controller.raise_move_forward(): self.__boulder.accelerate() if self._controller.raise_move_left(): self.__boulder.move_left() if self._controller.raise_move_right(): self.__boulder.move_right() def update(self, events): super(GameScene, self).update(events) self.__camera_y_offset = self.__boulder.position.y # testing the boulder with some friction self.__apply_friction() if self._controller.raise_transition_escape(): return GameStatus.TRANSITION_MAIN_MENU self.__update_boulder() self.__check_for_collisions() # temporary disabled the clean up of the dissipated particles -- optimize this later self.__check_particles_have_dissipated() if self.__boulder.is_dead(): pygame.mixer.music.stop() self.__boulder_rolling_sound.stop() return GameStatus.TRANSITION_GAME_OVER # signal the game over screen # has the boulder crossed the finish line if self.__boulder.position.y > GameConstants.MAP_HEIGHT: pygame.mixer.music.stop() self.__boulder_rolling_sound.stop() if self.__boulder.get_health() > 2*Boulder.INITIAL_HEALTH/3: return GameStatus.TRANSITION_GOOD_ENDING else: return GameStatus.TRANSITION_BAD_ENDING self.__sprite_list.update() self.__obstacle_sprite_list.update() self.__particles_list.update()