微信跳一跳遊戲助手
開頭附上java專案原始碼下載地址
http://download.csdn.net/download/ou775968876/10199335專案需要java環境和adb環境 不知道配的同學可以轉到這個地址 http://blog.csdn.net/ou775968876/article/details/79028408
功能簡介
用JAVA自動控制手機玩跳一跳
執行環境
- JAVA,最低版本為7.0,
- 安卓手機,目前已適配解析度
- 1600x2560
- 1440x2560
- 1080x1920
- 720x1280
使用方法
有JAVA開發工具的同學可以直接執行java程式碼,便於程式碼除錯,下面主要介紹執行已經打包好的jar包的方法
-
手機開啟USB除錯,並連線電腦
- 開啟USB除錯方法,進入
設定
,找到開發者選項
,開啟並勾選USB除錯
; - 如果沒有
開發者選項
,進入關於手機
,連續點選版本號
7次,即可開啟開發者選項
。
- 開啟USB除錯方法,進入
-
通過下面的命令,執行Android.jar
java -jar Android.jar
-
根據手機解析度選擇跳躍係數,目前已適配機型:
- 1600x2560機型推薦0.92
- 1440x2560機型推薦1.039
- 1080x1920機型推薦1.392
- 720x1280機型推薦2.078
其他解析度請自己微調。
原理說明
-
通過adb命令控制手機截圖,並取回到本地
adb shell screencap -p /sdcard/screen.png adb pull /sdcard/screen.png .
-
圖片分析
-
通過adb命令,給手機模擬按壓事件
adb shell input swipe x y x y time
其中
x
和y
是螢幕座標,time
是觸控時間,單位ms。
程式碼詳解
這裡將針對一些關鍵演算法的程式碼進行解釋
1. 尋找棋子位置
把截圖放大,可以看到棋子頂部畫素連成一條橫線,那麼我們通過顏色匹配,找到這一條線的始末位置,取中間位置,就得到了棋子的x座標。
棋子的底部也是一條橫線,用顏色匹配,我們檢測到相似顏色的最大y座標,就是棋子底部了,不過考慮到棋子底部是個圓盤,我們把棋子的y座標再往上提一些。
這樣我們就得到了棋子的xy座標,下面是相關程式碼:
/* 計算棋子位置 */ Pixel piece = new Pixel(); for (int i = TOP_BORDER; i < screenHeight - BOTTOM_BORDER; i++) { int startX = 0; int endX = 0; for (int j = LEFT_BORDER; j < screenWidth - RIGHT_BORDER; j++) { int red = Color.red(pixels[i][j].color); int green = Color.green(pixels[i][j].color); int blue = Color.blue(pixels[i][j].color); if (50 < red && red < 55 && 50 < green && green < 55 && 55 < blue && blue < 65) {//棋子頂部顏色 //如果偵測到棋子相似顏色,記錄下開始點 if (startX == 0) { startX = j; endX = 0; } } else if (endX == 0) { //記錄下結束點 endX = j; if (endX - startX < PIECE_TOP_PIXELS) { //規避井蓋的BUG,畫素點不夠長,則重新計算 startX = 0; endX = 0; } } if (50 < red && red < 60 && 55 < green && green < 65 && 95 < blue && blue < 105) {//棋子底部的顏色 //最後探測到的顏色就是棋子的底部畫素 piece.y = i; } } if (startX != 0 && piece.x == 0) { piece.x = (startX + endX) / 2; } } //棋子縱座標從底部邊緣調整到底部中心 piece.y -= PIECE_BOTTOM_CENTER_SHIFT;
2. 尋找靶點
所謂靶點,就是目標物體中心的那個小圓點,顏色值為0xf5f5f5
。
那麼我們只需要尋找顏色值為0xf5f5f5的色塊就可以了,為了規避其他物體相近顏色干擾,我們可以限制色塊的大小,正確大小的色塊才是靶點。
但是如何計算色塊的大小呢,色塊最頂端到最底端y座標的差值我們作為色塊的高度,同理,最左側到最右側x座標的差值作為寬度,我們只需要查詢這四個頂點的座標就可以了。
本來打算用凸包的Graham掃描演算法,後來發現色塊已經是凸包了,且邊緣畫素是連續的,那麼我們按照一定順序,遍歷邊緣畫素,就可以在O(n^-2)的時間複雜度裡,得到色塊的頂點座標了。
我們從第一個畫素點開始,尋找的順序如圖所示:
/** * 尋找色塊頂點畫素 */ public static final Pixel[] findVertexs(Pixel[][] pixels, Pixel firstPixcel) { Pixel[] vertexs = new Pixel[4]; Pixel topPixel = firstPixcel; Pixel leftPixel = firstPixcel; Pixel rightPixel = firstPixcel; Pixel bottomPixel = firstPixcel; Pixel currentPixcel = firstPixcel; //先把座標置於左上角 while (checkBorder(pixels, currentPixcel)//判斷是否超出影象邊緣 && Color.compareColor(pixels[currentPixcel.y - 1][currentPixcel.x], firstPixcel)) {//判斷是否是相同顏色 currentPixcel = pixels[currentPixcel.y - 1][currentPixcel.x]; } while (checkBorder(pixels, currentPixcel) && Color.compareColor(pixels[currentPixcel.y][currentPixcel.x - 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x - 1]; } //尋找上頂點畫素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y - 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y - 1][currentPixcel.x]; } else if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x + 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x + 1]; } else { topPixel = findCenterPixcelHorizontal(pixels, currentPixcel); break; } } //尋找右頂點畫素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x + 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x + 1]; } else if (Color.compareColor(pixels[currentPixcel.y + 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y + 1][currentPixcel.x]; } else { rightPixel = findCenterPixcelVertial(pixels, currentPixcel); break; } } //尋找下頂點畫素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y + 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y + 1][currentPixcel.x]; } else if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x - 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x - 1]; } else { bottomPixel = findCenterPixcelHorizontal(pixels, currentPixcel); break; } } //尋找左頂點畫素 while (checkBorder(pixels, currentPixcel)) { if (Color.compareColor(pixels[currentPixcel.y][currentPixcel.x - 1], firstPixcel)) { currentPixcel = pixels[currentPixcel.y][currentPixcel.x - 1]; } else if (Color.compareColor(pixels[currentPixcel.y - 1][currentPixcel.x], firstPixcel)) { currentPixcel = pixels[currentPixcel.y - 1][currentPixcel.x]; } else { leftPixel = findCenterPixcelVertial(pixels, currentPixcel); break; } } vertexs[0] = leftPixel; vertexs[1] = topPixel; vertexs[2] = rightPixel; vertexs[3] = bottomPixel; return vertexs; }
得到了四個座標點,我們就可以計算色塊的中點了,也就是目標落點。
對於沒有靶點,但是落點是規則平面的,也可以用類似演算法。
3. 斜率計算
對於沒有靶點,又不是規則平面的,我們怎麼計算落點呢,這時候就要用到斜率了。
可以看得出來,每次左上角或右上角出現的物體,針對當前物體的方向都是一樣的,也就是兩個物體中心的連線,斜率是固定的。
基本所有的目標物體,最頂點畫素中點的x座標,都是在物體中間,我們至少先得到了目標物體x座標了,記為des.x ,接下來要求des.y 。
如上圖所示,計算過程如下:
斜線的公式為 y=kx+b
那麼,在棋子座標上有 piece.y=k*piece.x+b
在目標落點座標上有 des.y=k*des.x+b
代入得到 des.y=k*(des.x-piece.x)+piece.y
然而這種演算法還是有偏差的。
可以看到,同樣的斜率,如果棋子的位置有偏差,計算出來最終落點還是會有偏差的。
程式碼解析就先講這麼多,希望有大神可以提出更好的解決方案。
玩遊戲小竅門
- 連續的落到物體中心位置,是有分數加成的,最多跳一次可以得幾十分
- 井蓋、商店、唱片、魔方,多停留一會,有音樂響起後也是有分數加成的
那麼看一下程式設計師的朋友圈有多殘酷吧