---
# System prepended metadata

title: 第三章：遊戲物件初始設定

---

# 第三章：遊戲物件初始設定

## 磚塊
* ### 定義磚塊類別
    * #### 介紹：
        * 初始建構：
            * 將圖像設成寬為`BRICK_WIDTH`、高為`BRICK_HEIGHT`、顏色為紅色的矩形。
            * 將左上角位置設在傳入的`(x, y)`座標

    * #### 程式碼：
        ```python=
        class Brick(pygame.sprite.Sprite):
            def __init__(self, x, y):
                super().__init__()
                self.image = pygame.Surface((BRICK_WIDTH, BRICK_HEIGHT))
                self.image.fill(RED)
                self.rect = self.image.get_rect()
                self.rect.topleft = x, y
        ```
* ### 相關常數設定
    * #### 介紹：
        ![上午1.06 2026](https://hackmd.io/_uploads/ryDQdC67el.jpg =75%x)
    * #### 程式碼：
        ```python=
        BRICK_ROWS = 5
        BRICK_COLS = 8
        BRICK_MARGIN  = 70
        BRICK_SPACING = 8
        BRICK_WIDTH = (WIDTH - 2 * BRICK_MARGIN - (BRICK_COLS - 1) * BRICK_SPACING) // BRICK_COLS
        BRICK_HEIGHT = 45
        ```
* ### 創建物件
    ```python=
    # 磚塊 Group
    bricks = pygame.sprite.Group()
    # 迴圈創建磚塊
    for r in range(BRICK_ROWS):
        for c in range(BRICK_COLS):
            x = c * (BRICK_WIDTH + BRICK_SPACING) + BRICK_MARGIN
            y = r * (BRICK_HEIGHT + BRICK_SPACING) + BRICK_MARGIN
            brick = Brick(x, y)
            all_sprites.add(brick)
            bricks.add(brick)
    ```

## 板子
* ### 定義板子類別
    * #### 介紹：
        * 初始建構：
            * 將圖像設為大小為`BOARD_SIZE`、顏色為藍色的矩形
            * 將中心位置設在傳入的`center`座標
            * 移動速度（每步移動距離）設為 15
        * 每次`update()`時若按住
            * 左鍵：向左移動一步，若圖形左側超出視窗左側，將其移回視窗左側
            * 右鍵：向右移動一步，若圖形右側超出視窗右側，將其移回視窗右側

    * #### 程式碼：
        ```python=
        BOARD_SIZE = (150, 30)    

        class Board(pygame.sprite.Sprite):
            def __init__(self, center):
                super().__init__()
                self.image = pygame.Surface(BOARD_SIZE)
                self.image.fill(BLUE)
                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
        ```
    * #### 語法說明：
        * `pygame.key.get_pressed()`：
        用來偵測鍵盤上哪些按鍵正被按住。回傳一個「*boolean* 的列表」，表示每個按鍵的狀態（按下是 True，沒按是 False）。
        * `pygame.K_LEFT` / `pygame.K_RIGHT`：
        用以表示鍵盤某個按鍵的 *int* 常數，拿來和鍵盤事件做比對或查詢。分別表示左鍵和右鍵。

* ### 創建物件
    ```python=
    board = Board((WIDTH // 2, HEIGHT - 60))
    all_sprites.add(board)
    ```

## 球
* ### 定義球類別
    * #### 介紹：
        * 設定「全部球」的移動速度。
        * 初始建構：
            * 將圖像設定為半徑為`BALL_RADIUS`、顏色為綠色的「圓形」
            * 將中心位置設在傳入的`center`座標
            * 利用球速以及傳入的移動角度`angle`，透過三角函數算出 x , y 方向的速度
            ![image](https://hackmd.io/_uploads/r1PyvXsxlx.png =40%x)
        * 每次`update`：
            * 向前移動一步
            * 如果撞到上、左或右的便反彈
            * 如果掉到底下則將其刪除

    * #### 程式碼：
        ```python=
        import math

        BALL_RADIUS = 15

        class Ball(pygame.sprite.Sprite):
            speed = 7
            def __init__(self, angle, center):
                super().__init__()
                self.image = pygame.Surface((2 * BALL_RADIUS, 2 * BALL_RADIUS), pygame.SRCALPHA)
                pygame.draw.circle(self.image, GREEN, (BALL_RADIUS, BALL_RADIUS), BALL_RADIUS)
                self.rect = self.image.get_rect()
                self.rect.center = center
                self.dx = Ball.speed * math.sin(angle)
                self.dy = -Ball.speed * math.cos(angle)

            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()
        ```
        :::info
        須先導入 `math` 模組
        :::
    * #### 語法說明：
        * `pygame.SRCALPHA`
        Pygame 中的一個旗標常數（flag constant），代表啟用透明度的功能。原先預設只有 (R, G, B) 三個通道，使用後變成 (R, G, B, A) 四個通道，A 表示透明度。
        * `pygame.draw.circle(surface, color, center, radius)`
        在平面上畫一個圓形。surface為選擇的平面，color為圓形顏色，center為圓心在平面上的位置，radius為圓形半徑。
        
* ### 創建物件
    ```python=
    balls = pygame.sprite.Group()
    ball = Ball(math.pi / 4, (WIDTH // 2, HEIGHT - 150))
    all_sprites.add(ball)
    balls.add(ball)
    ```

---
:::spoiler 完整程式碼
```python=
import pygame
import math

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 - 1) * BRICK_SPACING) // BRICK_COLS
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)

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("打磚塊遊戲")
clock = pygame.time.Clock()

# 定義磚塊類別
class Brick(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((BRICK_WIDTH, BRICK_HEIGHT))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.topleft = x, y

# 定義板子類別
class Board(pygame.sprite.Sprite):
    def __init__(self, center):
        super().__init__()
        self.image = pygame.Surface(BOARD_SIZE)
        self.image.fill(BLUE)
        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.image = pygame.Surface((2 * BALL_RADIUS, 2 * BALL_RADIUS), pygame.SRCALPHA)
        pygame.draw.circle(self.image, GREEN, (BALL_RADIUS, BALL_RADIUS), BALL_RADIUS)
        self.rect = self.image.get_rect()
        self.rect.center = center
        self.dx = Ball.speed * math.sin(angle)
        self.dy = -Ball.speed * math.cos(angle)
    
    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()

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_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()

    # 畫面顯示
    screen.fill(WHITE)
    all_sprites.draw(screen)
    pygame.display.update()

pygame.quit()
```
:::