本專案為2025 中山資工營之遊戲實作課程教材。
營隊課程以 Python 與 Pygame 遊戲引擎為主軸,帶領學員從零打造具備完整方塊旋轉、碰撞檢測、暫存、預覽、滿行消除與硬降功能的經典俄羅斯方塊遊戲。
透過本教材,學員能夠深入理解遊戲主迴圈、二維陣列地圖管理、物件導向程式設計以及離散坐標系中的碰撞檢測演算法。
- 專案特色
- 遊戲操作說明
- 專案目錄結構
- 核心架構與檔案職責
- 核心演算法與實作原理解析
- 快速開始 (Quick Start)
- 遊戲參數自訂 (Setting.py)
- 學員課後延伸挑戰 (Bonus Challenges)
- 經典規則完整實作:
- 支援 7 種經典四格骨牌(I、J、L、O、S、T、Z)及其專屬配色。
- 實作順時針旋轉、軟降(Soft Drop)、硬降(Hard Drop)、左右橫移。
- 具備方塊暫存(Hold)與下一個方塊預覽(Next)功能。
- 直觀的物件導向設計(OOP & Pygame):
- 使用
Tetromino類別繼承pygame.sprite.Sprite封裝單一方塊的狀態(種類、旋轉角、位置座標、著地延遲計數)。 - 模組化拆分遊戲主迴圈、方塊邏輯、地圖資料與全域設定。
- 使用
- 穩健的離散碰撞檢測(Discrete Collision Detection):
- 在方塊進行任何平移或旋轉操作時,先一步預判邊界(左、右、底)及是否與盤面上已固定的方塊重疊,若發生衝突則自動退回前一合法狀態。
- 貼近真實機制的著地延遲(Lock Delay / Hit Bottom Delay):
- 方塊接觸底部或下方障礙時不會立刻定型,提供緩衝時間讓玩家進行最後微調,提升遊戲手感。
- 高度可自訂性:
- 集中於
Setting.py統一管理落下速度、移動冷卻、視窗大小與網格尺寸,方便教學展示與難度調整。
- 集中於
| 按鍵 | 動作名稱 | 功能說明 |
|---|---|---|
↑ (上方向鍵) |
旋轉 (Rotate) | 將目前方塊順時針旋轉 90 度(若旋轉後會穿牆或重疊則取消旋轉) |
↓ (下方向鍵) |
軟降 (Soft Drop) | 按住可加速方塊向下墜落 |
← / → (左右方向鍵) |
水平移動 (Move) | 向左或向右移動一格(內建冷卻間隔,防止滑動過快失控) |
Space (空白鍵) |
硬降 (Hard Drop) | 瞬間讓方塊直接垂直落到底部 |
Z 鍵 |
暫存方塊 (Hold) | 將當前方塊暫存至 Hold 區,或與 Hold 區內的方塊進行調換 |
| 關閉視窗 (X) | 退出 (Quit) | 結束主迴圈並關閉遊戲程式 |
Tetris-pygame/
├── Game.py # 遊戲主程式:包含遊戲主迴圈、事件處理、消除行與主要畫面繪製
├── Tetrominoes.py # 方塊物件與核心邏輯:方塊類別、位移、旋轉、落下判定、Hold 交換
├── Tmap.py # 資料定義:7 種方塊顏色 (TCOLOR) 與 4 方向旋轉矩陣 (TMAP)
└── Setting.py # 參數設定:遊戲區行列數、格點大小、FPS、下落頻率等常數
flowchart TD
subgraph Setting.py
S[常數與參數設定<br/>WIDTH, HEIGHT, FPS, INTERVAL]
end
subgraph Tmap.py
TM[形狀定義 TMAP<br/>顏色定義 TCOLOR]
end
subgraph Tetrominoes.py
T[Tetromino 類別<br/>旋轉 / 移動 / 碰撞判斷]
PF[二維陣列 play_field<br/>儲存固定後的盤面方塊]
DU[dropUpdate & hold 函式]
end
subgraph Game.py
GL[遊戲主迴圈 main<br/>事件監聽 Event Handling]
RA[消除整行 RemoveALine]
REN[畫面渲染 Render]
end
S --> Tetrominoes.py
S --> Game.py
TM --> Tetrominoes.py
Tetrominoes.py --> Game.py
Setting.py: 集中管理遊戲各項物理與介面常數,包含網格大小(UNIT_LENGTH = 30)、盤面尺寸(寬 10 格、高 20 格)、更新幀率(FPS = 60)、落下週期(DROP_INTERVAL = 10)等。Tmap.py: 純資料定義模組。定義了 7 種四格骨牌的 RGB 顏色(TCOLOR)以及各方塊在 4 種旋轉角度下的 4×4 二維陣列布林遮罩(TMAP)。Tetrominoes.py: 核心邏輯模組。建立全域盤面陣列play_field(10×24);實作Tetromino物件導向類別(負責旋轉、移動、碰撞檢測與自身繪製);提供方塊自然落下判定(dropUpdate)與暫存置換(hold)。Game.py: 遊戲啟動入口。負責 Pygame 視窗初始化、處理鍵盤事件、主遊戲迴圈計時更新、消行邏輯(RemoveALine)以及組合所有畫面元件進行最終渲染。
遊戲區域(Playfield)邏輯尺寸為:寬 10 格($WIDTH = 10$)、高 20 格($HEIGHT = 20$)。
為了讓方塊能夠在進入可見視窗前順利生成並旋轉,盤面陣列高度定義為
-
$j \in [0, 3]$ :畫面頂部之上的隱藏生成緩衝區。方塊初始位置為$(x=4, y=-3)$ 。 -
$j \in [4, 23]$ :玩家在視窗中實際看見的可視遊戲區域。
當邏輯格點座標為
-
主遊戲區像素座標:
$$pixel_x = (x + 1) \times UNIT_LENGTH$$ $$pixel_y = (y + 1) \times UNIT_LENGTH$$ -
盤面陣列索引對應:
$$play_field[x][y + 4]$$
在 Tmap.py 中,每一種骨牌皆以一個
以 T 方塊為例 (4 種朝向狀態):
朝向 0 朝向 1 朝向 2 朝向 3
[0, 1, 0, 0] [0, 1, 0, 0] [0, 0, 0, 0] [0, 1, 0, 0]
[1, 1, 1, 0] [0, 1, 1, 0] [1, 1, 1, 0] [1, 1, 0, 0]
[0, 0, 0, 0] [0, 1, 0, 0] [0, 1, 0, 0] [0, 1, 0, 0]
[0, 0, 0, 0] [0, 0, 0, 0] [0, 0, 0, 0] [0, 0, 0, 0]
def rotate(self) -> None:
self.rotation = (self.rotation + 1) % 4
if self.Iscollision():
# 若旋轉後發生碰撞,復原角度
self.rotation = (self.rotation - 1) % 4當玩家按下旋轉時,先樂觀更新 self.rotation,若呼叫 Iscollision() 發現穿牆或卡入既有方塊,則立刻退回原角度,確保合法性。
在 Tetromino.Iscollision 中,會歷遍
flowchart TD
Start(["開始碰撞檢查"]) --> Loop["遍歷 4x4 矩陣中的每一格"]
Loop --> CheckBit{"該位置是否有方塊?"}
CheckBit -- 否 --> NextCell["檢查下一格"]
CheckBit -- 是 --> CheckBound{"是否超出邊界?<br/>(超出左、右邊界或底部)"}
CheckBound -- 是 --> Collide(["判定碰撞: True"])
CheckBound -- 否 --> CheckField{"盤面該位置是否已被佔用?<br/>(play_field 該座標已有方塊)"}
CheckField -- 是 --> Collide
CheckField -- 否 --> NextCell
NextCell --> AllDone{"是否檢查完畢?"}
AllDone -- 否 --> Loop
AllDone -- 是 --> Safe(["判定無碰撞: False"])
若任何一個小單元超出左界、右界、底界,或該格在 play_field 中已被其他方塊佔用,立即回傳 True。
為了避免方塊一接觸到地板就瞬間凝固導致玩家無法進行滑移或旋轉調整,程式實作了 HitBtmCnt 計數機制:
- 方塊向下嘗試移動一格。
- 若發生碰撞,退回前一格,並將著底計數
HitBtmCnt += 1。 - 只有當
HitBtmCnt >= HITBTM_DELAY(預設為 5 次檢查)時,才正式將方塊寫入play_field陣列固定。 - 固定時若發現任何小單元位於頂部緩衝區($y < 0$),即判定為堆疊超出上限,觸發 Game Over。
每次方塊降落固定後,呼叫 RemoveALine() 進行滿行判定與清理:
def RemoveALine() -> None:
for y in range(HEIGHT):
hasAline = True
for x in range(WIDTH):
if not play_field[x][y + 4]:
hasAline = False
break
if hasAline:
# 從該行開始,逐行將上方的內容向下覆蓋搬移
for j in range(y, 0, -1):
for i in range(WIDTH):
play_field[i][j + 4] = play_field[i][j - 1 + 4]
# 最頂部的一行補空 (0)
for i in range(WIDTH):
play_field[i][0 + 4] = 0- 滿行檢測:由上至下掃描每一橫行,若任一行所有 10 格皆非 0,即表示整行填滿。
- 重力下推:將該行上方的每一行向下拷貝一列($j \to j+1$),並將第 0 行清空重置。
當玩家按下 Z 鍵時,觸發 hold 函式:
- 交換當前的
DropingTetr與暫存區的HoldTetr。 - 被換出的方塊會重置座標至頂部生成位置
$(INIT_X, INIT_Y)$ ,重新開始下落。 - 右側介面的 Hold 區域將同步重繪最新暫存方塊。
本專案支援 Python 3.8 或以上版本,僅需安裝官方 pygame 套件:
# 安裝 pygame
pip install pygame進入專案根目錄並執行 Game.py:
# 執行俄羅斯方塊主程式
python Game.py啟動後即可在跳出的 Pygame 視窗中透過鍵盤開始遊玩!
學員可以自由修改 Setting.py 中的各項常數,體驗不同的遊戲手感或自訂遊戲介面:
| 參數名稱 | 預設值 | 說明 | 調整效果 |
|---|---|---|---|
UNIT_LENGTH |
30 |
主遊戲區每格像素邊長 | 放大可使遊戲視窗整體按比例變大 |
SMALL_UNIT_LENGTH |
25 |
側邊欄(Hold/Next)格子像素邊長 | 控制預覽方塊的顯示尺寸 |
WIDTH |
10 |
遊戲區橫向網格數 | 修改可改變棋盤寬度(標準規則為 10) |
HEIGHT |
20 |
遊戲區縱向網格數 | 修改可改變棋盤高度(標準規則為 20) |
FPS |
60 |
畫面更新率 (Frames Per Second) | 控制主迴圈每秒更新次數 |
DROP_INTERVAL |
10 |
方塊自動下落間隔 (幀數) | 數值愈小下落速度愈快,難度愈高 |
HITBTM_DELAY |
5 |
著地延遲鎖定閾值 | 數值愈大著地後可調整的時間愈長 |
CONTROL_INTERVAL |
7 |
左右移動冷卻幀數 | 控制橫移時的按鍵連發靈敏度 |
WIN_BACKGROUND_COLOR |
(50, 50, 50) |
視窗背景 RGB 顏色 | 可調整視窗周圍背景色彩 |
本教材架構清晰、模組分明,非常適合作為學員在營隊結束後的自主探索專案。歡迎挑戰以下功能擴充:
- 計分與等級系統 (Score & Level Progression):
- 根據單次消除行數(單行 Single、雙行 Double、三行 Triple、四行 Tetris)給予階梯式加分。
- 當消除總行數達到門檻時升級(Level Up),並調小
DROP_INTERVAL使方塊越落越快。
- 幽靈方塊 (Ghost Piece):
- 在方塊正下方繪製半透明或虛線框,預先標示硬降(Hard Drop)落點,輔助玩家瞄準。
- 音效與背景音樂 (Sound Effects & BGM):
- 加入旋轉、硬降、消行與 Game Over 音效,以及經典的俄羅斯方塊 8-bit 背景旋律。
- Hold 鎖定防刷機制 (Hold Lock):
- 實作經典標準規則:每一次方塊落定前,只能使用一次 Hold,避免玩家無限次來回交換方塊。
- 排行榜或最高分紀錄 (High Score Persistence):
- 使用 Python 檔案 I/O(如 JSON 或文字檔)保存本機歷史最高紀錄,並在側邊欄即時呈現。