# 第五章:加入圖片和音效 ## 圖片 1. ### 載入圖片 * #### 程式碼: ```python= import os # 背景圖片 background_img = pygame.image.load(os.path.join("img", "background.jpg")).convert_alpha() # 磚塊圖片 brick_img = pygame.image.load(os.path.join("img", "chocolate.png")).convert_alpha() # 板子圖片 board_img = pygame.image.load(os.path.join("img", "wood.png")).convert_alpha()transform.scale(board_img, BOARD_SIZE) # 球圖片 ball_img = pygame.image.load(os.path.join("img", "ball.png")).convert_alpha() ``` * #### 語法說明: * `pygame.image.load(filepath)`:回傳路徑中的圖片 * `os.path.join(name1, name2, ...)`: * 不同作業系統的路徑分隔符不同,假如要取得 img 資料夾中的 ball.png: * Windows $\Rightarrow$ `img\\ball.png` * macOS/Linus $\Rightarrow$ `img/ball.png` * 用 `os.path.join()` 可以自動處理這些分隔符 * 須先導入 `os` 模組 * `convert_alpha()`:轉換圖片格式,提升 pygame 繪製圖片效率,並支援透明度。 [圖片檔案](https://github.com/NTPUCSIE12-ChoCodeLate/NTPUCSIE_CAMP/tree/7100b921e60076b1fa40ccf759ae529f7f1f2b41/img) 1. ### 圖片修正 * #### 程式碼: ```python= ... background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT)) ... brick_img = pygame.transform.scale(brick_img, (BRICK_WIDTH, BRICK_HEIGHT)) ... board_img = pygame.transform.scale(board_img, BOARD_SIZE) ... ball_img = pygame.transform.scale(ball_img, (BALL_RADIUS * 2, BALL_RADIUS * 2)) ``` * #### 語法說明: * `pygame.transform.scale(image, size)`:調整圖片大小 1. ### 圖片配置 * #### 程式碼: ```python= class Brick(pygame.sprite.Sprite): def __init__(self, x, y): ... self.image = brick_img ... class Board(pygame.sprite.Sprite): def __init__(self, center): ... self.image = board_img ... class Ball(pygame.sprite.Sprite): def __init__(self, angle, center): ... self.image = ball_img ... while running: ... # 畫面顯示 screen.blit(background_img, (0, 0)) ... ``` * #### 語法說明: * `surface.blit(image, (x, y))`:將 image 畫在另一個 surface 上,(x, y) 表示畫的位置。 ## 音效 1. ### 音效初始設定 ```python= pygame.mixer.init() ``` 1. ### 載入音效 ```python= hit_sound = pygame.mixer.Sound(os.path.join('sound', 'hit.wav')) ``` [音效檔案](https://github.com/NTPUCSIE12-ChoCodeLate/NTPUCSIE_CAMP/tree/7100b921e60076b1fa40ccf759ae529f7f1f2b41/sound) 1. ### 播放音效 ```python= class Ball(pygame.sprite.Sprite): def hit_brick(self, brick_rect): hit_sound.play() # 播放音效 ... ``` --- :::spoiler 完整程式碼 ```python= import pygame import math import random import os FPS = 60 # 大小設定 WIDTH, HEIGHT = 800, 700 BRICK_ROWS = 5 BRICK_COLS = 8 BRICK_MARGIN = 70 BRICK_SPACING = 8 BRICK_WIDTH = (WIDTH - 2 * BRICK_MARGIN) // BRICK_COLS - BRICK_SPACING BRICK_HEIGHT = 40 BOARD_SIZE = (150, 30) BALL_RADIUS = 15 # 顏色設定 WHITE = (255, 255, 255) BLUE = (0, 0, 255) RED = (255, 0, 0) GREEN = (0, 255, 0) BLACK = (0, 0, 0) pygame.init() pygame.mixer.init() screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("打磚塊遊戲") clock = pygame.time.Clock() # 載入圖片 # 背景圖片 background_img = pygame.image.load(os.path.join("img", "background.jpg")).convert_alpha() background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT)) # 磚塊圖片 brick_img = pygame.image.load(os.path.join("img", "chocolate.png")).convert_alpha() brick_img = pygame.transform.scale(brick_img, (BRICK_WIDTH, BRICK_HEIGHT)) # 板子圖片 board_img = pygame.image.load(os.path.join("img", "wood.png")).convert_alpha() board_img = pygame.transform.scale(board_img, BOARD_SIZE) # 球圖片 ball_img = pygame.image.load(os.path.join("img", "ball.png")).convert_alpha() ball_img = pygame.transform.scale(ball_img, (BALL_RADIUS * 2, BALL_RADIUS * 2)) # 載入音效 hit_sound = pygame.mixer.Sound(os.path.join("sound", "hit.wav")) # 矩形和圓形碰撞判斷 def rect_circle_collision(rectangle, circle): cx, cy = circle.rect.center r = circle.radius rx, ry = rectangle.rect.topleft w, h = rectangle.rect.size closest_x = max(rx, min(cx, rx + w)) closest_y = max(ry, min(cy, ry + h)) distance = math.sqrt((cx - closest_x) ** 2 + (cy - closest_y) ** 2) return distance < r # 定義磚塊類別 class Brick(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.image = brick_img self.rect = self.image.get_rect() self.rect.topleft = x, y # 定義板子類別 class Board(pygame.sprite.Sprite): def __init__(self, center): super().__init__() self.image = board_img self.rect = self.image.get_rect() self.rect.center = center self.speed = 15 def update(self): keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: self.rect.x -= self.speed if self.rect.left < 0: self.rect.left = 0 if keys[pygame.K_RIGHT]: self.rect.x += self.speed if self.rect.right > WIDTH: self.rect.right = WIDTH class Ball(pygame.sprite.Sprite): speed = 7 def __init__(self, angle, center): super().__init__() self.radius = BALL_RADIUS self.image = ball_img self.rect = self.image.get_rect() self.rect.center = center self.dx = Ball.speed * math.sin(angle) self.dy = -Ball.speed * math.cos(angle) self.speed_factor = 1.03 def update(self): self.rect.x += self.dx self.rect.y += self.dy if self.rect.left <= 0: self.rect.left = 0 self.dx = -self.dx elif self.rect.right >= WIDTH: self.rect.right = WIDTH self.dx = -self.dx if self.rect.top <= 0: self.rect.top = 0 self.dy = -self.dy elif self.rect.top >= HEIGHT: self.kill() def hit_board(self, board_rect): # 計算角度 max_angle = math.pi / 3 # 60 度 offset = (self.rect.centerx - board_rect.centerx) / (board_rect.width / 2) angle = offset * max_angle # 計算 dx, dy self.dx = Ball.speed * math.sin(angle) self.dy = -Ball.speed * math.cos(angle) def hit_brick(self, brick_rect): hit_sound.play() # 加速 self.dx *= self.speed_factor self.dy *= self.speed_factor Ball.speed *= self.speed_factor # 反彈方向判斷 if self.rect.centerx - brick_rect.right > 0 and self.dx < 0: self.dx = -self.dx elif brick_rect.left - self.rect.centerx > 0 and self.dx > 0: self.dx = -self.dx else: self.dy = -self.dy # 隨機產生新球 if random.random() < 0.: self.create_new_ball() def create_new_ball(self): angle = random.uniform(-math.pi / 3, math.pi / 3) new_ball = Ball(angle, self.rect.center) all_sprites.add(new_ball) balls.add(new_ball) all_sprites = pygame.sprite.Group() # 磚塊 bricks = pygame.sprite.Group() for r in range(BRICK_ROWS): for c in range(BRICK_COLS): x = c * (BRICK_WIDTH + BRICK_SPACING) + BRICK_SPACING // 2 + BRICK_MARGIN y = r * (BRICK_HEIGHT + BRICK_SPACING) + BRICK_MARGIN brick = Brick(x, y) all_sprites.add(brick) bricks.add(brick) # 板子 board = Board((WIDTH // 2, HEIGHT - 60)) all_sprites.add(board) # 球 balls = pygame.sprite.Group() ball = Ball(math.pi / 4, (WIDTH // 2, HEIGHT - 150)) all_sprites.add(ball) balls.add(ball) running = True while running: clock.tick(FPS) # 輸入處理 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 更新遊戲 all_sprites.update() # 球碰板子 hits = pygame.sprite.spritecollide(board, balls, False, rect_circle_collision) for hit_ball in hits: hit_ball.hit_board(board.rect) # 球碰磚塊 hits = pygame.sprite.groupcollide(bricks, balls, True, False, rect_circle_collision) for brick, hit_balls in hits.items(): hit_balls[0].hit_brick(brick.rect) # 畫面顯示 screen.blit(background_img, (0, 0)) all_sprites.draw(screen) pygame.display.update() pygame.quit() ``` :::