class GameLayer: """ Processes the main game logic """ INITIALIZATION = 0 GAME_LOOP = 1 GAME_EXIT = 2 GAME_WIN_SCREEN = 3 GAME_PAUSE_SCREEN = 4 def __init__(self, owner): self.owner = owner self.moving_right = False self.moving_left = False self.push_balls = False self.fps_clock = pygame.time.Clock() self.physics_world = physics.PhysicsWorld(STEP_TIME_INTEGRATE) self.game_status = GameLayer.INITIALIZATION self.current_map = MapSelector() self.bricks = [] self.paddles = [] self.balls = [] self.entities = [] self.bodies = [] on_ball_brick_event = self.physics_world.CallBack('ball', 'brick', self.on_ball_brick_collision) on_ball_paddle_event = self.physics_world.CallBack('ball', 'paddle', self.on_ball_paddle_collision) on_ball_bottom_wall_event = self.physics_world.CallBack('ball', 'bottom-wall', self.on_ball_bottom_wall_collision) on_ball_left_wall_event = self.physics_world.CallBack('ball', 'left-wall', self.on_ball_left_right_collision) on_ball_right_wall_event = self.physics_world.CallBack('ball', 'right-wall', self.on_ball_left_right_collision) self.physics_world.add_callback(on_ball_brick_event) self.physics_world.add_callback(on_ball_paddle_event) self.physics_world.add_callback(on_ball_bottom_wall_event) self.physics_world.add_callback(on_ball_left_wall_event) self.physics_world.add_callback(on_ball_right_wall_event) def initialize(self, previous_layer): self.moving_right = False self.moving_left = False self.push_balls = False self.game_status = GameLayer.INITIALIZATION self.current_map.initialize_current_map() self.update_map() def clear_game(self): self.bricks = [] self.paddles = [] self.balls = [] self.entities = [] self.bodies = [] self.physics_world.clear_bodies() self.push_balls = False def update_map(self): self.clear_game() m = self.current_map.get_next_map() for brick in m.bricks: self.register_entity(brick) self.bricks.append(brick) for ball in m.balls: self.register_entity(ball) self.balls.append(ball) for paddle in m.paddles: self.register_entity(paddle) self.paddles.append(paddle) for body in m.bodies: self.register_body(body) def register_body(self, new_body): if not new_body in self.bodies: self.physics_world.add_body(new_body) self.bodies.append(new_body) def register_entity(self, new_entity): if not new_entity in self.entities: self.physics_world.add_body(new_entity.body) self.entities.append(new_entity) def unregister_entity(self, entity): if entity in self.entities: self.physics_world.delete_body(entity.body) self.entities.remove(entity) def on_ball_brick_collision(self, ball_body, brick_body, normal): brick_ent = brick_body.tag_ent ball_ent = ball_body.tag_ent brick_ent.apply_damage(ball_ent.damage_points) ball_ent.body.set_velocity(min(self.physics_world.MAX_SPEED, ball_body.velocity * 1.1)) def on_ball_paddle_collision(self, ball_body, paddle_body, normal): # Adjusts the ball direction if the paddle is moving when the ball collides with it angle = math.acos(dot(normal, ball_body.direction)) # Angle between the reflected direction and the normal delta_angle = abs(((math.pi * 0.5) - angle) * 0.5) # Half the angle that remains if were to perform a 90 degree reflection if paddle_body.direction.x > 0: # Clockwise rotation because the paddle is moving to the right ball_body.direction = normalize(rotate(ball_body.direction, delta_angle)) elif paddle_body.direction.x < 0: # Counter-clockwise rotation because the paddle is moving to the left ball_body.direction = normalize(rotate(ball_body.direction, -delta_angle)) def on_ball_bottom_wall_collision(self, bottom_body, ball_body, normal): if len(self.balls) == 1: self.game_status = GameLayer.GAME_EXIT else: ball_ent = ball_body.tag_ent self.unregister_entity(ball_ent) def on_ball_left_right_collision(self, ball_body, wall_body, normal): angle = math.acos(dot(normal, ball_body.direction)) # Angle between the reflected direction and the normal # If the angle is too flat, add a small rotation to the reflected direction if angle < 0.1: delta_angle = 0.2 if ball_body.direction.y > 0: # Counter-clockwise rotation because the ball is moving downwards ball_body.direction = normalize(rotate(ball_body.direction, -delta_angle)) elif ball_body.direction.y <= 0: # Clockwise rotation because the ball is moving upwards ball_body.direction = normalize(rotate(ball_body.direction, delta_angle)) def update(self, step_time): def change_dir_vel(entities, direction, velocity): for entity in entities: entity.body.direction = direction entity.body.set_velocity(velocity) if(self.moving_left or self.moving_right): if self.moving_left: change_dir_vel(self.paddles, Vector2(-1,0), PADDLE_VELOCITY) if self.game_status == GameLayer.INITIALIZATION: change_dir_vel(self.balls, Vector2(-1,0), PADDLE_VELOCITY) else: change_dir_vel(self.paddles, Vector2(1,0), PADDLE_VELOCITY) if self.game_status == GameLayer.INITIALIZATION: change_dir_vel(self.balls, Vector2(1,0), PADDLE_VELOCITY) else: change_dir_vel(self.paddles, ZERO2, magnitude(ZERO2)) if self.game_status == GameLayer.INITIALIZATION: change_dir_vel(self.balls, ZERO2, magnitude(ZERO2)) if self.push_balls and self.game_status == GameLayer.INITIALIZATION: for ball in self.balls: if ball.body.is_static: ball.body.is_static = False v = Vector2(BALL_VELOCITY_X, BALL_VELOCITY_Y) change_dir_vel(self.balls, normalize(v), magnitude(v)) self.push_balls = False self.game_status = GameLayer.GAME_LOOP # Remove bricks that have been destroyed free_brick_list = [] for brick in self.bricks: if brick.health_points == 0: self.unregister_entity(brick) free_brick_list.append(brick) self.bricks = [ b for b in self.bricks if free_brick_list.count(b) == 0 ] for paddle in self.paddles: # Integrate paddle paddle.body.rect.position = sum(paddle.body.rect.position, mul(paddle.body.direction, paddle.body.velocity * step_time)) # Relocate paddle position to a valid position range paddle.body.rect.position.x = utils.clamp(paddle.body.rect.position.x, 0, WINDOW_WIDTH - PADDLE_WIDTH) paddle.body.rect.position.y = WINDOW_HEIGHT - PADDLE_HEIGHT - PADDLE_LINE_SPACING for ball in self.balls: if ball.body.is_static: pos_r = Vector2((PADDLE_WIDTH - BALL_WIDTH) * 0.5, - BALL_HEIGHT) ball.body.rect.position = sum(self.paddles[0].body.rect.position, pos_r) def run(self): """ Main game loop: processes inputs, updates the game status and renders the game scene """ last_update_time = pygame.time.get_ticks() accumulated = 0.0 while self.game_status == GameLayer.INITIALIZATION or self.game_status == GameLayer.GAME_LOOP: #Process inputs for event in pygame.event.get(): if event.type == pygame.QUIT: utils.terminate() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: self.moving_left = True elif event.key == pygame.K_RIGHT: self.moving_right = True elif event.key == pygame.K_a: self.push_balls = True elif event.key == pygame.K_p: self.game_status = GameLayer.GAME_PAUSE_SCREEN elif event.type == pygame.KEYUP: if event.key == pygame.K_ESCAPE: utils.terminate() elif event.key == pygame.K_RIGHT: self.moving_right = False elif event.key == pygame.K_LEFT: self.moving_left = False time = pygame.time.get_ticks() delta_time = time - last_update_time accumulated = accumulated + delta_time if accumulated > BALL_PUSH: self.push_balls = True accumulated = 0.0 while delta_time > 0: sim_step_time = STEP_TIME if delta_time - STEP_TIME < 0: sim_step_time = delta_time self.update(sim_step_time) self.physics_world.step_simulation(sim_step_time) if len(self.bricks) < 1: if not self.current_map.has_next_map(): self.game_status = GameLayer.GAME_WIN_SCREEN else: self.update_map() self.game_status = GameLayer.INITIALIZATION accumulated = 0 delta_time -= sim_step_time last_update_time = time graphics.clear_display_surf(BLUE) for entity in self.entities: dest_rect = pygame.Rect(entity.body.rect.position.x, entity.body.rect.position.y, entity.body.rect.w, entity.body.rect.h) graphics.draw(entity.surface, entity.surface_src, dest_rect) graphics.flip_display_surf() self.fps_clock.tick(MAX_FPS) def at_exit(self): """ Sets the next layer to execute: GameOverLayer or PauseLayer """ if self.game_status == GameLayer.GAME_PAUSE_SCREEN: self.owner.set_layer(self.owner.PAUSE_LAYER) self.game_status = GameLayer.GAME_LOOP elif self.game_status == GameLayer.GAME_WIN_SCREEN: self.owner.set_layer(self.owner.WIN_LAYER) else: self.owner.set_layer(self.owner.GAME_OVER_LAYER) def signal_exit(self): self.game_status = GameLayer.GAME_EXIT
class GameLayer: """ Processes the main game logic """ INITIALIZATION = 0 GAME_LOOP = 1 GAME_EXIT = 2 GAME_WIN_SCREEN = 3 GAME_PAUSE_SCREEN = 4 def __init__(self, owner): self.owner = owner self.moving_right = False self.moving_left = False self.push_balls = False self.fps_clock = pygame.time.Clock() self.physics_world = physics.PhysicsWorld(STEP_TIME_INTEGRATE) self.game_status = GameLayer.INITIALIZATION self.current_map = MapSelector() self.bricks = [] self.paddles = [] self.balls = [] self.entities = [] self.bodies = [] on_ball_brick_event = self.physics_world.CallBack( 'ball', 'brick', self.on_ball_brick_collision) on_ball_paddle_event = self.physics_world.CallBack( 'ball', 'paddle', self.on_ball_paddle_collision) on_ball_bottom_wall_event = self.physics_world.CallBack( 'ball', 'bottom-wall', self.on_ball_bottom_wall_collision) on_ball_left_wall_event = self.physics_world.CallBack( 'ball', 'left-wall', self.on_ball_left_right_collision) on_ball_right_wall_event = self.physics_world.CallBack( 'ball', 'right-wall', self.on_ball_left_right_collision) self.physics_world.add_callback(on_ball_brick_event) self.physics_world.add_callback(on_ball_paddle_event) self.physics_world.add_callback(on_ball_bottom_wall_event) self.physics_world.add_callback(on_ball_left_wall_event) self.physics_world.add_callback(on_ball_right_wall_event) def initialize(self, previous_layer): self.moving_right = False self.moving_left = False self.push_balls = False self.game_status = GameLayer.INITIALIZATION self.current_map.initialize_current_map() self.update_map() def clear_game(self): self.bricks = [] self.paddles = [] self.balls = [] self.entities = [] self.bodies = [] self.physics_world.clear_bodies() self.push_balls = False def update_map(self): self.clear_game() m = self.current_map.get_next_map() for brick in m.bricks: self.register_entity(brick) self.bricks.append(brick) for ball in m.balls: self.register_entity(ball) self.balls.append(ball) for paddle in m.paddles: self.register_entity(paddle) self.paddles.append(paddle) for body in m.bodies: self.register_body(body) def register_body(self, new_body): if not new_body in self.bodies: self.physics_world.add_body(new_body) self.bodies.append(new_body) def register_entity(self, new_entity): if not new_entity in self.entities: self.physics_world.add_body(new_entity.body) self.entities.append(new_entity) def unregister_entity(self, entity): if entity in self.entities: self.physics_world.delete_body(entity.body) self.entities.remove(entity) def on_ball_brick_collision(self, ball_body, brick_body, normal): brick_ent = brick_body.tag_ent ball_ent = ball_body.tag_ent brick_ent.apply_damage(ball_ent.damage_points) ball_ent.body.set_velocity( min(self.physics_world.MAX_SPEED, ball_body.velocity * 1.1)) def on_ball_paddle_collision(self, ball_body, paddle_body, normal): # Adjusts the ball direction if the paddle is moving when the ball collides with it angle = math.acos( dot(normal, ball_body.direction )) # Angle between the reflected direction and the normal delta_angle = abs( ((math.pi * 0.5) - angle) * 0.5 ) # Half the angle that remains if were to perform a 90 degree reflection if paddle_body.direction.x > 0: # Clockwise rotation because the paddle is moving to the right ball_body.direction = normalize( rotate(ball_body.direction, delta_angle)) elif paddle_body.direction.x < 0: # Counter-clockwise rotation because the paddle is moving to the left ball_body.direction = normalize( rotate(ball_body.direction, -delta_angle)) def on_ball_bottom_wall_collision(self, bottom_body, ball_body, normal): if len(self.balls) == 1: self.game_status = GameLayer.GAME_EXIT else: ball_ent = ball_body.tag_ent self.unregister_entity(ball_ent) def on_ball_left_right_collision(self, ball_body, wall_body, normal): angle = math.acos( dot(normal, ball_body.direction )) # Angle between the reflected direction and the normal # If the angle is too flat, add a small rotation to the reflected direction if angle < 0.1: delta_angle = 0.2 if ball_body.direction.y > 0: # Counter-clockwise rotation because the ball is moving downwards ball_body.direction = normalize( rotate(ball_body.direction, -delta_angle)) elif ball_body.direction.y <= 0: # Clockwise rotation because the ball is moving upwards ball_body.direction = normalize( rotate(ball_body.direction, delta_angle)) def update(self, step_time): def change_dir_vel(entities, direction, velocity): for entity in entities: entity.body.direction = direction entity.body.set_velocity(velocity) if (self.moving_left or self.moving_right): if self.moving_left: change_dir_vel(self.paddles, Vector2(-1, 0), PADDLE_VELOCITY) if self.game_status == GameLayer.INITIALIZATION: change_dir_vel(self.balls, Vector2(-1, 0), PADDLE_VELOCITY) else: change_dir_vel(self.paddles, Vector2(1, 0), PADDLE_VELOCITY) if self.game_status == GameLayer.INITIALIZATION: change_dir_vel(self.balls, Vector2(1, 0), PADDLE_VELOCITY) else: change_dir_vel(self.paddles, ZERO2, magnitude(ZERO2)) if self.game_status == GameLayer.INITIALIZATION: change_dir_vel(self.balls, ZERO2, magnitude(ZERO2)) if self.push_balls and self.game_status == GameLayer.INITIALIZATION: for ball in self.balls: if ball.body.is_static: ball.body.is_static = False v = Vector2(BALL_VELOCITY_X, BALL_VELOCITY_Y) change_dir_vel(self.balls, normalize(v), magnitude(v)) self.push_balls = False self.game_status = GameLayer.GAME_LOOP # Remove bricks that have been destroyed free_brick_list = [] for brick in self.bricks: if brick.health_points == 0: self.unregister_entity(brick) free_brick_list.append(brick) self.bricks = [b for b in self.bricks if free_brick_list.count(b) == 0] for paddle in self.paddles: # Integrate paddle paddle.body.rect.position = sum( paddle.body.rect.position, mul(paddle.body.direction, paddle.body.velocity * step_time)) # Relocate paddle position to a valid position range paddle.body.rect.position.x = utils.clamp( paddle.body.rect.position.x, 0, WINDOW_WIDTH - PADDLE_WIDTH) paddle.body.rect.position.y = WINDOW_HEIGHT - PADDLE_HEIGHT - PADDLE_LINE_SPACING for ball in self.balls: if ball.body.is_static: pos_r = Vector2((PADDLE_WIDTH - BALL_WIDTH) * 0.5, -BALL_HEIGHT) ball.body.rect.position = sum( self.paddles[0].body.rect.position, pos_r) def run(self): """ Main game loop: processes inputs, updates the game status and renders the game scene """ last_update_time = pygame.time.get_ticks() accumulated = 0.0 while self.game_status == GameLayer.INITIALIZATION or self.game_status == GameLayer.GAME_LOOP: #Process inputs for event in pygame.event.get(): if event.type == pygame.QUIT: utils.terminate() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: self.moving_left = True elif event.key == pygame.K_RIGHT: self.moving_right = True elif event.key == pygame.K_a: self.push_balls = True elif event.key == pygame.K_p: self.game_status = GameLayer.GAME_PAUSE_SCREEN elif event.type == pygame.KEYUP: if event.key == pygame.K_ESCAPE: utils.terminate() elif event.key == pygame.K_RIGHT: self.moving_right = False elif event.key == pygame.K_LEFT: self.moving_left = False time = pygame.time.get_ticks() delta_time = time - last_update_time accumulated = accumulated + delta_time if accumulated > BALL_PUSH: self.push_balls = True accumulated = 0.0 while delta_time > 0: sim_step_time = STEP_TIME if delta_time - STEP_TIME < 0: sim_step_time = delta_time self.update(sim_step_time) self.physics_world.step_simulation(sim_step_time) if len(self.bricks) < 1: if not self.current_map.has_next_map(): self.game_status = GameLayer.GAME_WIN_SCREEN else: self.update_map() self.game_status = GameLayer.INITIALIZATION accumulated = 0 delta_time -= sim_step_time last_update_time = time graphics.clear_display_surf(BLUE) for entity in self.entities: dest_rect = pygame.Rect(entity.body.rect.position.x, entity.body.rect.position.y, entity.body.rect.w, entity.body.rect.h) graphics.draw(entity.surface, entity.surface_src, dest_rect) graphics.flip_display_surf() self.fps_clock.tick(MAX_FPS) def at_exit(self): """ Sets the next layer to execute: GameOverLayer or PauseLayer """ if self.game_status == GameLayer.GAME_PAUSE_SCREEN: self.owner.set_layer(self.owner.PAUSE_LAYER) self.game_status = GameLayer.GAME_LOOP elif self.game_status == GameLayer.GAME_WIN_SCREEN: self.owner.set_layer(self.owner.WIN_LAYER) else: self.owner.set_layer(self.owner.GAME_OVER_LAYER) def signal_exit(self): self.game_status = GameLayer.GAME_EXIT