import pygame import random from ..command import Command from .game_base import GameBase import logging BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) BLUE = (0, 0, 255) PADDLE_WIDTH = 100 PADDLE_HEIGHT = 10 BALL_SIZE = 10 BRICK_WIDTH = 80 BRICK_HEIGHT = 30 BRICK_ROWS = 5 BRICK_COLS = 10 SPEED = 5 logger = logging.getLogger(__name__) class Breakout(GameBase): def __init__(self, screen, serial, instance, is_joint_mode=False): super().__init__(screen, serial, instance, is_joint_mode) self.reset() def reset(self): global PADDLE_WIDTH, PADDLE_HEIGHT, BALL_SIZE, BRICK_WIDTH, BRICK_HEIGHT, BRICK_ROWS, BRICK_COLS, SPEED scale_factor = min(self.screen_width / 640, self.screen_height / 480) if self.is_vertical: PADDLE_WIDTH = min(100, int(80 * scale_factor)) BRICK_COLS = max(5, min(8, int(self.screen_width / 90))) else: PADDLE_WIDTH = min(100, int(100 * scale_factor)) BRICK_COLS = max(6, min(10, int(self.screen_width / 90))) PADDLE_HEIGHT = max(8, int(10 * scale_factor)) BALL_SIZE = max(6, int(10 * scale_factor)) BRICK_WIDTH = int((self.screen_width - 20) / BRICK_COLS) - 5 BRICK_HEIGHT = max(15, min(30, int(30 * scale_factor))) BRICK_ROWS = max(3, min(5, int(self.screen_height / 120))) SPEED = max(3, min(5, int(5 * scale_factor))) self.paddle = [ self.screen_width // 2 - PADDLE_WIDTH // 2, self.screen_height - 40, ] self.ball = [self.screen_width // 2, self.screen_height // 2] self.ball_dx = SPEED self.ball_dy = -SPEED self.bricks = [] for row in range(BRICK_ROWS): for col in range(BRICK_COLS): self.bricks.append( [ col * (BRICK_WIDTH + 5) + 5, row * (BRICK_HEIGHT + 5) + 5, BRICK_WIDTH, BRICK_HEIGHT, ] ) self.score = 0 def move_paddle(self, dx): self.paddle[0] += dx if self.paddle[0] < 0: self.paddle[0] = 0 if self.paddle[0] > self.screen_width - PADDLE_WIDTH: self.paddle[0] = self.screen_width - PADDLE_WIDTH def move_ball(self): self.ball[0] += self.ball_dx self.ball[1] += self.ball_dy if self.ball[0] <= BALL_SIZE or self.ball[0] >= self.screen_width - BALL_SIZE: self.ball_dx *= -1 if self.ball[1] <= BALL_SIZE: self.ball_dy *= -1 paddle_rect = pygame.Rect( self.paddle[0], self.paddle[1], PADDLE_WIDTH, PADDLE_HEIGHT ) ball_rect = pygame.Rect( self.ball[0] - BALL_SIZE, self.ball[1] - BALL_SIZE, BALL_SIZE * 2, BALL_SIZE * 2, ) if ball_rect.colliderect(paddle_rect) and self.ball_dy > 0: self.ball_dy *= -1 hit_pos = (self.ball[0] - self.paddle[0]) / PADDLE_WIDTH self.ball_dx = SPEED * (hit_pos - 0.5) * 2 for brick in self.bricks[:]: brick_rect = pygame.Rect(brick[0], brick[1], brick[2], brick[3]) if ball_rect.colliderect(brick_rect): self.bricks.remove(brick) self.score += 1 self.send_stats(self.score) self.ball_dy *= -1 logger.debug(f"brick hit, score: {self.score}") break def check_game_over(self): if self.ball[1] >= self.screen_height: return "lose" if not self.bricks: return "win" return None def main_loop(self): clock = pygame.time.Clock() while self.is_running: result = self.update() if result is not None: return result if not (self.waiting or self.score_display_time): for event in pygame.event.get(): result = self.handle_common_events(event) action = self.input_handler.get_input(event) if result is not None: return result if action == "LEFT": self.move_paddle(-20) elif action == "RIGHT": self.move_paddle(20) # self.paddle[0] = pygame.mouse.get_pos()[0] - PADDLE_WIDTH // 2 self.move_paddle(0) self.check_serial() self.move_ball() game_over = self.check_game_over() if game_over: self.end_game(self.score) continue self.screen.fill(BLACK) pygame.draw.rect( self.screen, BLUE, [self.paddle[0], self.paddle[1], PADDLE_WIDTH, PADDLE_HEIGHT], ) pygame.draw.circle( self.screen, WHITE, [int(self.ball[0]), int(self.ball[1])], BALL_SIZE, ) for brick in self.bricks: pygame.draw.rect(self.screen, RED, brick) if not self.is_joint_mode or self.instance != "primary": score_text = self.font.render(f"Score: {self.score}", True, WHITE) if self.is_vertical: self.screen.blit(score_text, (self.screen_width // 2 - score_text.get_width() // 2, 10)) else: self.screen.blit(score_text, (10, 10)) pygame.display.flip() clock.tick(60) return None