去年底在公司曾經很瘋狂一款手機App遊戲,叫作 Zoo Keeper
Zoo Keeper的遊戲方式很簡單,就是把三個相同的圖案連線──消去──得分!
像這樣簡單卻固定的遊戲機制,總是常常被以不同的型式重新包裝;這讓我想到在大學的時候,也曾經有在臉書上風靡過一款類似的遊戲,叫寶石方塊(Bejeweled Blitz),在那時候曾經有聽到一個傳說:
聽說在學校有資工系的學生,寫出了一個可以自動玩寶石方塊的外掛,像我們平常人玩可能畫面就慢慢跑,但是用外掛玩的遊戲畫面,看起來就像是失控的電腦一樣,咻咻咻咻,超帥!
不過那時候還沒什麼能力寫出這樣的程式,直到最近因為工作的關係,算是有跟類似的技術沾上了邊。所以呢,我就打算來寫寫看這樣傳說中的外掛,剛好也當作練習,如果做得出來,相信一定會很好玩!
運作原理
所以呢,從技術的角度出發,如果我們要實作一個可以自動玩寶石方塊的遊戲,我們需要哪些技術呢?
1. 螢幕區域截圖
2. 寶石顏色分析
3. 遊戲規則演算法
(也就是要讓電腦知道,該點哪顆寶石才能得分)
4. 移動並點擊滑鼠
5. 重覆步驟1 – 4,直到遊戲時間結束
以上大概就是這個程式運作的流程,看起來就是跟人類玩遊戲的方式差不多,只是把眼睛、腦袋跟手指給了電腦自己跑而已。
實作螢幕截圖
所以呢,剛開始研究就是要從螢幕截圖開始,首先來看看這個遊戲的畫面:
這款遊戲的『寶石』──也就是動物,總共有七種不同的基本花色(不包含特殊道具),然後每次畫面上會有一個 8×8的遊戲棋盤,所以我們要作的第一個工作,就是要取得畫面上的所有花色。
怎麼做呢?這邊可以有好幾種方式:
1. 最簡單的,可以只跑8×8次的迴圈,各在每隻動物上取1 pixel的顏色,再將顏色對照到對應的動物。這樣的方式速度快、實作容易,但誤判率高,整體程式的成功率其實是很低的(等於是一個沒什麼用的外掛)
2. 我們將第一種方式的精準度拉高一點,在每隻動物身上取 5 x 5 pixels的顏色,取得這25個顏色的平均值,對照到對應的動物,這樣的方式雖然增加了運作的負擔,但誤判率相對大大降低,提升整體成功率。
3. 用圖片直接對照螢幕上的畫面:我沒認真的想過要這樣作,第一個原因是因為如果動物正在移動、消去或者畫面上有其他特效,這樣判斷是沒有彈性空間的,第二個原因是因為,我用第二種方式已經算成功了,哈哈哈
所以我們開始來研究怎麼抓取螢幕的顏色吧!
花了一兩天的時間Google了一些螢幕取色的方式,一開始找到了一個是使用GDI+的方式,取得螢幕上某一個點的顏色:
[DllImport("gdi32")] private static extern int GetPixel(int hdc, int nXPos, int nYPos); [DllImport("user32")] private static extern int GetWindowDC(int hwnd); [DllImport("user32")] private static extern int ReleaseDC(int hWnd, int hDC); public static Color GetScreenPixelColor(Point point) { int lDC = GetWindowDC(0); int intColor = GetPixel(lDC, (int)point.X, (int)point.Y); ReleaseDC(0, lDC); byte b = (byte)((intColor >> 0x10) & 0xffL); byte g = (byte)((intColor >> 8) & 0xffL); byte r = (byte)(intColor & 0xffL); return Color.FromRgb(r, g, b); }
用這樣的方式在取得螢幕上某一點的顏色確實可行,但是網路上的文章都指出這樣的方式十分消耗運算時間;
但是剛開始測試時,迴圈分析的顏色不多,運算時間緩慢並不會非常明顯;但是在後來發現,如果要取得螢幕上大量的點陣色彩時,會因為重複呼叫的頻率過高,造成運算時間變得很慢,有多慢呢?
大概就是用手指玩都比你快,這麼慢
為了避免寫出程式玩得還比手指慢的悲劇發生,我們勢必要找到更快速的方法。
又Google了好幾天之後,終於找到了一個穩定又快速的抓色方式(而且不是抓色,是抓了整個螢幕的截圖),請參考以下連結:
From: Fastest Screen Capture using C# – VISTA vs WIN7
實測之後的速度是夠我們快到比手指還要快的水準!
再來將每格的25個顏色分析之後,我們就可以取得這個8×8裡動物的內容了!
遊戲演算法
知道了這個遊戲棋盤的排列之後呢,接下來就是輪到重頭戲──演算法
像寶石方塊這樣的連線遊戲,規則都是相同的:
把三個一樣的花色連在一起。
所以呢,我們就必須開始思考該如何讓電腦知道該移動哪一步棋;首先,在三個花色連在一起之前,一定會先找到哪裡有『兩個連在一起』的花色,然後我們才會將『一個臨近的花色』移動到『兩個連在一起的花色』,進行消去。
或者像這樣,兩個間隔的相同花色,臨近一個也是相同的花色
所以,我們列出所有可能的狀況,如下:
橫向 六種狀況
縱向 六種狀況
好的,基本上以上的狀況已經包含了所有遊戲時可能有的狀況,我們可以將以上的狀況簡化為兩種:
1. 『一個臨近的花色』在『兩個連在一起的花色』
2. 『兩個間隔的相同花色』臨近『一個也是相同的花色』
機會地圖
先不管第二種,我們直接先實作第一種,邏輯如下:
先將棋盤中的花色以1~7代替,第一層迴圈開始走訪8×8中的每一個元素,如果有兩個元素連續重複,進入第二層的迴圈,開始檢查四個角落是否為相同花色,如果是相同花色,則在這個元素上加一點『機會點數』(所謂『機會點數』,就是有機會可以消去的計數值);相同的羅及套在第二種變化上,走訪完整個遊戲棋盤之後,我們可以得到一個由『機會點數』組合而成的『機會地圖』
所以上圖的『機會地圖』則會如下:
數字代表可以得分的元素,1代表這格有一次得分機會,2代表這格有兩次得分機會,依此類推。
既然知道了哪一個元素可以得分之後,我們就可以開始真正地『下棋』了。
滑鼠自動控制
控制滑鼠的方式,在網路上已經是有非常多資料的一個需求了,可能從有滑鼠的Window 95,甚至更早之前就有這樣的需求,所以我們現在要控制滑鼠可以說是非常的簡單。
我使用了一套叫作Global Mouse and Keyboard Library的函式庫,非常快速的實作了滑鼠的移動及控制。
整合運作
好了,所以我們現在有了三大部分的程式,分別為:螢幕截取分析、演算法邏輯運算、滑鼠自動操作,把這三者合在一起運作之後,就可以達到我們要的『自動運作程式』囉!
Demo
程式實際運行的影片如下
實作了ZooKeeper之後,回到我們的資工系傳說──寶石方塊;
相同的程式運作原理,在寶石方塊也可以輕易實現囉
是不是很酷呢?
後記
其實這篇文章希望分享的是製作過程的點滴,也因為遊戲公平性的關係,所以並不會在這邊公開這樣子的下載或者範例程式;如果您真的也想要嘗試玩玩看這樣的程式,最好的方式就是試著自己寫寫看,有時候撰寫程式就只是為了一種成就感而已,不是嗎?
若有任何建議,歡迎您在以下留言