---
# System prepended metadata

title: 第五章：加入圖片和音效

---

# 第五章：加入圖片和音效

## 圖片
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()
```
:::
