iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
自我挑戰組

順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作系列 第 26

第二十六天:指尖的策略:滑鼠事件處理與卡牌的精準選取

  • 分享至 

  • xImage
  •  

嗨,各位程式碼冒險家!歡迎來到我的「順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作」第二十六天。在過去幾天,我們為《奇幻卡牌競技場》賦予了豐富的視覺元素,但一個遊戲若缺少了互動,就只是一幅靜態的畫。今天,我們將深入探討將玩家意圖轉化為實際行動的關鍵技術——滑鼠事件處理,並解析如何實現精準的卡牌選取與配對操控。

這不僅是策略遊戲的核心,更是 Pygame 中事件驅動程式設計 (Event-Driven Programming) 的典型應用。我們將揭示 main.py 中,遊戲主迴圈如何捕捉玩家的每一次點擊,並透過 handle_click 方法,將其轉化為複雜的遊戲邏輯。


一、事件的起點:Pygame 的主遊戲迴圈

在 Pygame 中,所有玩家的輸入——無論是鍵盤敲擊、滑鼠移動還是點擊——都被視為**「事件」(Event)。我們的遊戲需要一個持續運行的迴圈來不斷地「監聽」** 這些事件並做出反應。

程式碼實現 (main.pyrun 方法)
# 位於 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)
        # ... (後續的繪圖與更新邏輯) ...
技術細節解析
  1. pygame.event.get():這是 Pygame 事件處理的核心。它會回傳一個包含所有待處理事件的清單 (List)。我們的 for 迴圈會遍歷這個清單,逐一處理每個事件。
  2. event.type == pygame.MOUSEBUTTONDOWN:我們透過檢查事件的 type 屬性,來判斷它是否為「滑鼠按鍵被按下」的事件。
  3. pygame.mouse.get_pos():一旦確認是滑鼠點擊事件,此函式會回傳一個包含 (x, y) 座標的元組 (Tuple),精確記錄了點擊發生在視窗內的哪個位置。
  4. handle_click(mouse_x, mouse_y):取得座標後,我們將其作為引數傳遞給 handle_click 方法。這個方法是我們設計的互動邏輯中央指揮所,負責解析點擊的具體目標並觸發後續操作。

二、互動的中央指揮所:handle_click 方法深度解析

handle_click 方法接收滑鼠座標後,需要判斷玩家究竟點擊了哪個遊戲元素——是手牌、桌面牌,還是「結束回合」按鈕?這背後依賴的是一套精密的碰撞偵測 (Collision Detection) 邏輯。

程式碼實現 (main.pyhandle_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

    # ... (處理結束回合按鈕的點擊) ...
技術細節深度解析
  1. 手牌點擊偵測

    • 動態計算位置:程式首先計算出整個手牌區域的起始 X 座標 start_x
    • 建立 pygame.Rect 物件:在 for 迴圈中,為每一張手牌建立一個 pygame.Rect 物件。這個物件在記憶體中代表了一個矩形區域,包含了卡牌的 (x, y) 座標以及寬高。
    • rect.collidepoint():這是 Pygame 進行碰撞偵測的核心函式。它會判斷傳入的 (mouse_x, mouse_y) 座標是否落在該 Rect 物件的範圍內,並回傳 TrueFalse
  2. 選取與取消選取邏輯

    • 我們使用 self.selected_card_index 這個屬性來追蹤玩家當前選中的手牌索引。
    • 如果玩家點擊的卡牌正是已經選中的卡牌,self.selected_card_index 就會被重置為 None,實現**「取消選取」**的功能。
    • 如果點擊的是另一張牌,self.selected_card_index 則會更新為新牌的索引,完成**「選取」**。這種「切換選取」的邏輯非常符合玩家的直覺操作習慣。
  3. 桌面牌配對

    • 前置條件:只有當玩家已經選中了一張手牌(self.selected_card_index 不為 None)時,程式才會繼續偵測對桌面牌的點擊。
    • 邏輯觸發:與偵測手牌類似,程式會遍歷桌面牌堆,為每張牌建立 Rect 並使用 collidepoint 進行判斷。
  4. 呼叫核心邏輯 (process_player_match)

    • 一旦確認玩家意圖進行配對,handle_click 方法會將**「已選中的手牌索引」「被點擊的桌面牌索引」**傳遞給 GameManagerprocess_player_match 方法。
    • handle_click 的職責到此為止,它只負責**「翻譯」玩家的輸入,而具體的遊戲規則執行(如觸發技能、移動卡牌等)則交由 GameManager 處理。這種職責分離**的設計讓程式碼結構更清晰,易於維護。

透過 Pygame 強大的事件監聽迴圈碰撞偵測機制,我們成功地在《奇幻卡牌競技場》中建立了一套反應靈敏、操作直覺的互動系統。玩家的每一次點擊都能被精準地捕捉、解析,並轉化為遊戲中的策略行動,這為沉浸式的遊戲體驗打下了堅實的基礎。

明天,我們將探討如何進一步提升資訊的透明度,打造即時訊息提示與可滾動的戰鬥日誌,讓玩家能更清晰地掌握瞬息萬變的戰局。敬請期待!


上一篇
第二十五天:打造《奇幻卡牌競技場》的視覺魅力:各類遊戲元素的呈現
系列文
順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言