嗨,各位程式碼冒險家!歡迎來到我的「順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作」第十七天。昨天我們打造了遊戲的基礎單位 Player
類別,但光有角色還不足以構成一場完整的遊戲。今天,我們將深入探討整個《奇幻卡牌競技場》的總指揮官——GameManager
類別。它就像是遊戲的心臟與大腦,負責初始化所有遊戲元素、管理回合流程、追蹤戰局狀態,並協調玩家與敵人的所有行動。
在物件導向設計中,GameManager
是一個典型的核心邏輯控制器 (Core Logic Controller),它將遊戲的各個部分(玩家、牌組、規則)串連起來,形成一個有機的整體。現在,讓我們深入 gamemanager.py
檔案,剖析其結構與功能。
__init__
方法當一個 GameManager
物件被建立時,它的 __init__
方法會被自動執行,這個階段的任務是建立並準備好遊戲所需的一切資源。
gamemanager.py
)class GameManager:
def __init__(self, player1_name, player2_name, max_hand_size):
pygame.mixer.init() # 初始化音效系統
# 1. 建立玩家物件
self.player1 = Player(player1_name)
self.player2 = Player(player2_name)
self.players = [self.player1, self.player2]
# 2. 初始化牌組與牌堆
self.full_deck = []
self.remaining_deck = []
self.center_pile = []
self.discard_piles = {self.player1: [], self.player2: []}
# 3. 設定遊戲狀態與規則
self.current_player_index = 0
self.turn_count = 0
self.max_rounds = 15
self.game_state = "INITIALIZING"
self.max_hand_size = max_hand_size
self.max_actions_per_turn = 5
self.action_count = 0
self.min_center_cards = 5
# 4. 建立戰鬥日誌與音效資源
self.combat_log = []
self.add_to_log("遊戲開始!")
self.sounds = {
"draw_card": pygame.mixer.Sound(os.path.join(base_path, "audio", "draw_card_bg.wav")),
"touch_card": pygame.mixer.Sound(os.path.join(base_path, "audio", "touch_card_bg.wav")),
# ... 其他音效
}
玩家與牌堆管理:
self.player1
與 self.player2
:透過傳入的名稱,實例化兩個 Player
物件。self.full_deck
與 self.remaining_deck
:分別代表完整的 54 張牌組與遊戲中剩餘可抽的牌組。self.center_pile
:一個清單(List),存放桌面中央的公共卡牌。self.discard_piles
:一個字典(Dictionary),鍵(Key) 是玩家物件,值(Value) 是對應玩家的棄牌堆(一個清單)。這種結構能清晰地將棄牌堆與特定玩家綁定。遊戲狀態與規則:
self.game_state
:一個字串,用來追蹤遊戲目前所處的階段(例如 "INITIALIZING"
, "PLAYER_TURN"
, "GAME_OVER"
),這是控制遊戲流程的關鍵。self.turn_count
:記錄目前的回合數,用於判斷是否達到 15 回合的上限。self.max_actions_per_turn
:定義了每回合最多可進行 5 次行動的規則。self.action_count
:計數器,用於追蹤當前回合已執行的行動次數。輔助系統:
self.combat_log
:一個清單,用來儲存遊戲過程中發生的事件字串,形成戰鬥日誌。self.sounds
:一個字典,集中管理所有遊戲音效。鍵是音效的名稱(如 "draw_card"
),值是載入後的 pygame.mixer.Sound
物件,方便後續透過 play_sound("draw_card")
這樣直觀的方式呼叫。resource_path
函式在開發遊戲時,我們通常使用相對路徑來讀取圖片、音效等資源。但當使用 PyInstaller 這類工具將遊戲打包成獨立執行檔(.exe)時,程式的執行環境會發生改變,原本的相對路徑會失效。
為了讓遊戲無論是在開發環境執行,還是在打包後執行,都能正確找到資源檔,我們需要一個特別的函式來處理路徑問題。
gamemanager.py
)import sys
import os
def resource_path(relative_path):
"""取得資源的絕對路徑,不論是在開發環境還是打包後的 .exe。"""
try:
# PyInstaller 建立一個暫存資料夾並將路徑儲存在 _MEIPASS
base_path = sys._MEIPASS
except Exception:
# 在非打包環境中,取得腳本所在的絕對路徑
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# 將 base_path 設為指向根目錄
base_path = resource_path(".")
sys._MEIPASS
:這是 PyInstaller 在執行打包後的程式時,會建立的一個特殊屬性。它指向一個暫存資料夾,所有資源檔(圖片、音效等)都會被解壓縮到這裡。try-except
結構:這個結構非常關鍵。程式會嘗試讀取 sys._MEIPASS
,如果成功,代表目前是在打包後的環境執行,base_path
就會是暫存資料夾的路徑。如果失敗(except
),則代表是在正常的開發環境中,base_path
就會是專案的根目錄。os.path.join()
:這個函式會根據作業系統,智慧地將 base_path
和我們傳入的相對路徑(如 "audio/draw_card_bg.wav"
)組合成一個完整的、正確的絕對路徑。透過今天對 GameManager
初始化過程的深度剖析,我們可以看到一個好的遊戲架構是如何在開頭就將所有元素——玩家狀態、牌組管理、遊戲規則、乃至於底層的資源路徑——都考慮周全。這個穩固的基礎,為後續實現複雜的卡牌配對、技能觸發和 AI 邏輯提供了強而有力的支援。
明天,我們將繼續探索 GameManager
的內部,揭示卡牌技能是如何被程式碼精準觸發的。敬請期待!