前陣子連假無聊下載了款 Roguelike 的卡牌手遊來玩,玩了幾天後發現這樣不行耗費太多時間了,突然靈機一動現在 AI 這麼方便搞不好可以弄個自動化刷關的程式,這樣就可以解放我的雙手了。
這個系列預計會有三篇,上篇講從零開始到能跑的 MVP,中篇深入 Unity 注入與卡牌策略,下篇聊重構、監控和一個月的回顧。
🎮 動機與目標
這款遊戲叫 Card Guardians,是一款 Roguelike 卡牌手遊。
遊戲機制很簡單:每次進入關卡的卡牌都相同,透過遊玩時打怪、選牌、升級的變化來豐富遊戲,一路推進到 Boss。完成一輪關卡後升級裝備就會比上一次更強一點,透過不斷地刷關來慢慢地變強。但問題是,要刷金幣、升級裝備需要大量的時間,而且這是一個無止境的循環,刷久了實在是有點悶:
進關 → 戰鬥 → 領獎 → 再進關
每一輪大概要花 20-30 分鐘,一天如果想刷個幾場,兩三個小時就去了。身為工程師,這種事情當然不能忍。
目標很明確:寫一個 Bot,讓它自動完成整個刷關循環,我只要偶爾看一下有沒有出問題就好。
有了想法後馬上跟 Claude 討論,初步規劃是透過 iPhone Mirroring 把手機畫面鏡像到 Mac 上,直接截圖投影的畫面下來,丟給 Python 程式做畫面判斷,決定要執行的卡牌動作後,透過 PyAutoGUI 直接控制滑鼠來取代手指的觸控,達到模擬玩遊戲的效果。
🛠️ 技術選型
跟 Claude 討論完後,最終選定的技術組合是:
- Python — 快速開發,生態豐富,各種 library 隨手可得
- OpenCV Template Matching — 用模板圖片比對畫面,判斷當前在哪個遊戲畫面
- PyAutoGUI — 模擬滑鼠點擊,取代手指觸控
- macOS screencapture — 抓取遊戲視窗截圖
為什麼不用 VLM / OCR?
一開始也想過要不要用 Ollama 跑個本地模型來辨識遊戲畫面,但想了想這遊戲的 UI 是固定的 — 按鈕永遠長那樣、畫面切換有固定的特徵元素。對於這種固定 UI 的場景,Template Matching 就是最簡單、最可靠的方案。
而且這也是最快可以實驗的方式,不需串 API、不需要調參數,截一張模板圖片就能用。先用最簡單的方案解決問題,後續如果要增強再慢慢增加功能,不要過度設計。
🧠 核心架構:用「狀態」的思維來辨識畫面
整個 Bot 的核心邏輯可以用一句話概括:截圖 → 辨識當前畫面 → 執行對應動作 → 重複。
遊戲的每個畫面就是一個狀態,而 Bot 要做的就是辨識出當前狀態,然後執行對應的動作讓遊戲推進到下一個狀態。
狀態定義
首先定義一個 GameScreen enum,列出所有可能的遊戲畫面。
畫面特徵(Screen Signature)
每個畫面都有獨特的視覺特徵。例如主選單有特定的按鈕、戰鬥畫面有血條和卡牌區域。我為每個畫面定義了一組「特徵模板」,藉此來辨識畫面。
主迴圈
Bot 的主迴圈就是不斷重複三個步驟:
while True:
1. screenshot = capture() # 截圖
2. screen = detect(screenshot) # 用所有 signature 比對,找出當前畫面
3. decide_action(screen) # 根據畫面執行對應動作
sleep(interval)
detect() 會把截圖跟每個 ScreenSignature 的模板一一比對,用 OpenCV 的 matchTemplate() 計算相似度,超過閾值就算匹配。所有 required 模板都匹配的那個 signature,就是當前畫面。
decide_action() 則是一個大的分派函式 — 如果在主選單就點進關卡、如果在戰鬥就打牌、如果在獎勵畫面就領取並繼續。把複雜的遊戲流程拆成一個個獨立的狀態處理,每個狀態只需要專注自己的邏輯就好。
🤖 Vibe Coding
從開起這個念頭後,到完成一個初步的原型,總共花了一天多的時間,現在有 AI 可以輔助,真的可以在極短的時間內做出一個原型來測試,基本上只要動一張嘴就可以了,花比較多時間的反而是在確認截圖與確認點擊的位置上,畢竟遊戲的玩法還是自己比較清楚,有些東西還是得人工確認,但也不排除是我口袋的深度限制了 AI 的能力 XD
Template Matching 引擎
最初的引擎很陽春但有效:
- 截圖:用
screencapture指令抓取遊戲視窗 - 載入模板:事先截好的各種 UI 元素小圖(按鈕、圖示、文字等)
- 比對:用
cv2.matchTemplate()在截圖中搜尋模板 - 判斷:匹配度超過閾值(通常 0.8-0.9)就算找到了
- 執行:用 PyAutoGUI 點擊匹配位置的座標
引擎跑起來後,接下來要面對的是模板本身的品質問題。
模板裁切的眉角
模板的裁切也是要一點經驗的:
- 裁太大:包含太多周圍背景,背景一變就匹配失敗
- 裁太小:特徵不夠明顯,容易跟其他 UI 元素誤判
最好的做法是只裁切目標元素本身,留一點點邊距但不要包含會變動的背景。這需要一些經驗和反覆測試。
🖥️ 執行環境的演進:iPhone Mirroring → PlayCover
最初方案:iPhone Mirroring
一開始的方案很直觀 — 用 macOS 的 iPhone Mirroring 功能把手機畫面投射到 Mac 上,然後截圖這個鏡像視窗。聽起來合理,實際上跑起來也沒問題,但是當初想寫這個 bot 是為了解放我的雙手,但現在完成後我只能在一旁看我的電腦透過滑鼠在玩我的手機 ???
轉移:PlayCover
後來發現 PlayCover 這個模擬器,可以直接在 Mac 上執行 ipa 檔,花了點時間研究一下,順利的把遊戲從 iPhone 鏡像,轉移到直接在 Mac 上模擬,不過這樣做有個很大的問題,就是我之前截的圖都要重新再截過,因為兩者的解析度和渲染結果不同,之前截的模板全部不能用,要重新截取,而且座標系統也要再次確認過,我的半天就這樣又沒了🫠
📝 本篇小結
從一個「不想手動刷關」的念頭開始,兩天內就做出了一個能自動循環刷關的 MVP。回頭看這段過程,有幾個心得:
- 先求能動,再求完美 — Day 1 的 Bot 很粗糙,很多 hardcode,但它能跑。能跑才有東西可以迭代。
- 選最簡單夠用的技術方案 — Template Matching 不酷、不潮、不需要 GPU,但對固定 UI 的遊戲來說它就是最好的選擇。
- AI 輔助開發的威力 — 整個技術架構是跟 Claude 一起討論出來的,很多 OpenCV 的用法也是即時查問的。一個人摸索可能要花一週的事,有 AI 的輔助兩天就搞定了。
不過到這裡只是一個開始。MVP 能自動刷關了,但它其實很脆弱 — 碰到一個意外彈窗就會卡住、出牌策略就是亂打、偶爾還會辨識錯畫面做出離譜的操作,導致 Bot 卡死,甚至還誤觸遊戲內購的按鈕…
下一篇(中篇)會聊聊怎麼讓 Bot 變得更聰明也更實用:逆向工程 Unity 引擎來注入控制、建立完整的卡牌辨識系統、以及設計出牌策略。