嗨,各位程式碼冒險家!歡迎來到我的「順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作」第二十六天。在過去幾天,我們為《奇幻卡牌競技場》賦予了豐富的視覺元素,但一個遊戲若缺少了互動,就只是一幅靜態的畫。今天,我們將深入探討將玩家意圖轉化為實際行動的關鍵技術——滑鼠事件處理,並解析如何實現精準的卡牌選取與配對操控。
這不僅是策略遊戲的核心,更是 Pygame 中事件驅動程式設計 (Event-Driven Programming) 的典型應用。我們將揭示 main.py
中,遊戲主迴圈如何捕捉玩家的每一次點擊,並透過 handle_click
方法,將其轉化為複雜的遊戲邏輯。
在 Pygame 中,所有玩家的輸入——無論是鍵盤敲擊、滑鼠移動還是點擊——都被視為**「事件」(Event)。我們的遊戲需要一個持續運行的迴圈來不斷地「監聽」** 這些事件並做出反應。
main.py
的 run
方法)# 位於 Game 類別的 run 方法中
def run(self):
running = True
while running:
# 1. 事件監聽迴圈
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 2. 捕捉滑鼠左鍵點擊事件
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1: # 1 代表滑鼠左鍵
# 3. 取得點擊座標
mouse_x, mouse_y = pygame.mouse.get_pos()
# 4. 呼叫核心處理方法
if not self.paused:
self.handle_click(mouse_x, mouse_y)
# ... (後續的繪圖與更新邏輯) ...
pygame.event.get()
:這是 Pygame 事件處理的核心。它會回傳一個包含所有待處理事件的清單 (List)。我們的 for
迴圈會遍歷這個清單,逐一處理每個事件。event.type == pygame.MOUSEBUTTONDOWN
:我們透過檢查事件的 type
屬性,來判斷它是否為「滑鼠按鍵被按下」的事件。pygame.mouse.get_pos()
:一旦確認是滑鼠點擊事件,此函式會回傳一個包含 (x, y)
座標的元組 (Tuple),精確記錄了點擊發生在視窗內的哪個位置。handle_click(mouse_x, mouse_y)
:取得座標後,我們將其作為引數傳遞給 handle_click
方法。這個方法是我們設計的互動邏輯中央指揮所,負責解析點擊的具體目標並觸發後續操作。handle_click
方法深度解析handle_click
方法接收滑鼠座標後,需要判斷玩家究竟點擊了哪個遊戲元素——是手牌、桌面牌,還是「結束回合」按鈕?這背後依賴的是一套精密的碰撞偵測 (Collision Detection) 邏輯。
main.py
的 handle_click
方法)def handle_click(self, mouse_x, mouse_y):
# ... (前置條件檢查,如是否為玩家回合、行動次數是否超限等) ...
# 1. 偵測手牌點擊
spacing = CARD_WIDTH + 10
total_hand_width = len(self.player.hand) * spacing - 10
start_x = (SCREEN_WIDTH - total_hand_width) // 2 + self.scroll_offset
clicked_hand_card_index = None
for i, card in enumerate(self.player.hand):
rect = pygame.Rect(start_x + i * spacing, HAND_Y, CARD_WIDTH, CARD_HEIGHT)
if rect.collidepoint(mouse_x, mouse_y):
clicked_hand_card_index = i
break
if clicked_hand_card_index is not None:
# 2. 處理選取與取消選取邏輯
if self.selected_card_index == clicked_hand_card_index:
self.selected_card_index = None # 取消選取
self.show_message("取消選擇卡牌。")
else:
self.selected_card_index = clicked_hand_card_index # 選取卡牌
self.show_message(f"選中了 {self.player.hand[clicked_hand_card_index].card_type.value}")
return
# 3. 偵測桌面牌配對
if self.selected_card_index is not None:
# ... (計算桌面牌位置) ...
for i, card in enumerate(self.gm.center_pile):
rect = pygame.Rect(...) # 計算桌面牌的 rect
if rect.collidepoint(mouse_x, mouse_y):
# 4. 呼叫 GameManager 執行配對
success, message = self.gm.process_player_match(self.player, self.selected_card_index, i)
self.show_message(message)
if success:
self.selected_card_index = None
self.has_made_action_this_turn = True
return
# ... (處理結束回合按鈕的點擊) ...
手牌點擊偵測:
start_x
。pygame.Rect
物件:在 for
迴圈中,為每一張手牌建立一個 pygame.Rect
物件。這個物件在記憶體中代表了一個矩形區域,包含了卡牌的 (x, y)
座標以及寬高。rect.collidepoint()
:這是 Pygame 進行碰撞偵測的核心函式。它會判斷傳入的 (mouse_x, mouse_y)
座標是否落在該 Rect
物件的範圍內,並回傳 True
或 False
。選取與取消選取邏輯:
self.selected_card_index
這個屬性來追蹤玩家當前選中的手牌索引。self.selected_card_index
就會被重置為 None
,實現**「取消選取」**的功能。self.selected_card_index
則會更新為新牌的索引,完成**「選取」**。這種「切換選取」的邏輯非常符合玩家的直覺操作習慣。桌面牌配對:
self.selected_card_index
不為 None
)時,程式才會繼續偵測對桌面牌的點擊。Rect
並使用 collidepoint
進行判斷。呼叫核心邏輯 (process_player_match
):
handle_click
方法會將**「已選中的手牌索引」和「被點擊的桌面牌索引」**傳遞給 GameManager
的 process_player_match
方法。handle_click
的職責到此為止,它只負責**「翻譯」玩家的輸入,而具體的遊戲規則執行(如觸發技能、移動卡牌等)則交由 GameManager
處理。這種職責分離**的設計讓程式碼結構更清晰,易於維護。透過 Pygame 強大的事件監聽迴圈與碰撞偵測機制,我們成功地在《奇幻卡牌競技場》中建立了一套反應靈敏、操作直覺的互動系統。玩家的每一次點擊都能被精準地捕捉、解析,並轉化為遊戲中的策略行動,這為沉浸式的遊戲體驗打下了堅實的基礎。
明天,我們將探討如何進一步提升資訊的透明度,打造即時訊息提示與可滾動的戰鬥日誌,讓玩家能更清晰地掌握瞬息萬變的戰局。敬請期待!