其實也不能說算是外掛吧,算是個游戲小助手吧狸捅,畢竟不能抓包微王,也不能直接修改分數(shù)(據(jù)說之前可以直接抓包修改分數(shù),不過這漏洞已經(jīng)被微信官方修復)松嘶,今天這個是 Android 同學可以非常容易看懂的一篇文章艘狭,是從 Android 的角度實現(xiàn)的,附帶著技術原理分析和代碼分析翠订。
這個開源庫已經(jīng)被我同學分享到 GitHub 上巢音,他自己很無聊,就寫了這個東西和這篇文章尽超,自己通過寫代碼實現(xiàn)高分也是玩的不亦樂乎官撼,這就是程序員和普通玩家的區(qū)別吧。
功能簡介
用JAVA自動控制手機玩跳一跳
自動識別圖像計算距離
自動幫你點擊屏幕
-
自動緩存圖片似谁,并在圖片上標記一些識別結果傲绣,如下圖:
image
運行環(huán)境
JAVA掠哥,最低版本為7.0,官網(wǎng)下載
adb驅動秃诵,官網(wǎng)下載(需要翻墻)续搀,或者到這里下載SDK-tools,其中就包含adb
安卓手機菠净,目前已適配分辨率
1600x2560
1440x2560
1080x1920
720x1080
使用方法
有JAVA開發(fā)工具的同學可以直接運行java代碼禁舷,便于代碼調試,下面主要介紹運行已經(jīng)打包好的jar包的方法
- 手機打開USB調試毅往,并連接電腦
打開USB調試方法牵咙,進入
設置
,找到開發(fā)者選項
攀唯,打開并勾選USB調試
洁桌;如果沒有
開發(fā)者選項
,進入關于手機
侯嘀,連續(xù)點擊版本號
7次另凌,即可開啟開發(fā)者選項
。
-
通過下面的命令戒幔,運行Android.jar
java -jar Android.jar
-
根據(jù)手機分辨率選擇跳躍系數(shù)途茫,目前已適配機型:
其他分辨率請自己微調。
1600x2560機型推薦0.92
1440x2560機型推薦1.039
1080x1920機型推薦1.392
720x1080機型推薦2.078
原理說明
-
通過adb命令控制手機截圖溪食,并取回到本地
adb shell screencap -p /sdcard/screen.png
adb pull /sdcard/screen.png .
圖片分析
有靶點,即目標物體中心的白色圓點娜扇,則靶點中心為目標落點
無靶點错沃,但是純色平面,或者規(guī)則平面雀瓢,則平面中心為目標落點
無靶點枢析,又無純色規(guī)則平面,但是左上和右上位置的斜率是固定的刃麸,可根據(jù)固定斜率的斜線和目標物體中心線的焦點計算落點
根據(jù)棋子的顏色醒叁,取頂部和底部的特征像素點,在截圖中進行匹配泊业,找到棋子坐標
-
由于目標物體不是在左上就是在右上把沼,可以從上往下掃描,根據(jù)色差判斷目標物體位置吁伺,其中又分為以下幾種類型
imageimageimage 計算棋子坐標和目標落點的距離
距離×跳躍系數(shù)=按壓屏幕的時間饮睬,不同分辨率的手機,跳躍系數(shù)也有所不同
-
通過adb命令篮奄,給手機模擬按壓事件
adb shell input swipe x y x y time
其中
x
和y
是屏幕坐標捆愁,time
是觸摸時間割去,單位ms。
工程結構
代碼詳解
這里將針對一些關鍵算法的代碼進行解釋
-
尋找棋子位置
把截圖放大昼丑,可以看到棋子頂部像素連成一條橫線呻逆,那么我們通過顏色匹配,找到這一條線的始末位置菩帝,取中間位置咖城,就得到了棋子的x坐標。
image棋子的底部也是一條橫線胁附,用顏色匹配酒繁,我們檢測到相似顏色的最大y坐標,就是棋子底部了控妻,不過考慮到棋子底部是個圓盤州袒,我們把棋子的y坐標再往上提一些。
image.gif這樣我們就得到了棋子的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) { //規(guī)避井蓋的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;
-
尋找靶點
所謂靶點狰域,就是目標物體中心的那個小圓點,顏色值為
0xf5f5f5
依鸥。image那么我們只需要尋找顏色值為0xf5f5f5的色塊就可以了亥至,為了規(guī)避其他物體相近顏色干擾,我們可以限制色塊的大小贱迟,正確大小的色塊才是靶點姐扮。
但是如何計算色塊的大小呢,色塊最頂端到最底端y坐標的差值我們作為色塊的高度衣吠,同理茶敏,最左側到最右側x坐標的差值作為寬度,我們只需要查找這四個頂點的坐標就可以了缚俏。
本來打算用凸包的Graham掃描算法惊搏,后來發(fā)現(xiàn)色塊已經(jīng)是凸包了,且邊緣像素是連續(xù)的忧换,那么我們按照一定順序恬惯,遍歷邊緣像素,就可以在O(n^-2)的時間復雜度里亚茬,得到色塊的頂點坐標了宿崭。
我們從第一個像素點開始,尋找的順序如圖所示:
image/** * 尋找色塊頂點像素 */ 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; }
得到了四個坐標點才写,我們就可以計算色塊的中點了葡兑,也就是目標落點奖蔓。
對于沒有靶點,但是落點是規(guī)則平面的讹堤,也可以用類似算法吆鹤。
-
斜率計算對于沒有靶點,又不是規(guī)則平面的洲守,我們怎么計算落點呢疑务,這時候就要用到斜率了。
可以看得出來梗醇,每次左上角或右上角出現(xiàn)的物體知允,針對當前物體的方向都是一樣的,也就是兩個物體中心的連線叙谨,斜率是固定的温鸽。
基本所有的目標物體,最頂點像素中點的x坐標手负,都是在物體中間涤垫,我們至少先得到了目標物體x坐標了,記為des.x 竟终,接下來要求des.y 蝠猬。
image.gif如上圖所示,計算過程如下:
斜線的公式為 y=kx+b
那么统捶,在棋子坐標上有 piece.y=k*piece.x+b
在目標落點坐標上有 des.y=kdes.x+b
代入得到 des.y=k*(des.x-piece.x)+piece.y
然而這種算法還是有偏差的榆芦。
image.gif可以看到,同樣的斜率喘鸟,如果棋子的位置有偏差歧杏,計算出來最終落點還是會有偏差的。
代碼解析就先講這么多迷守,希望有大神可以提出更好的解決方案。
玩游戲小竅門
連續(xù)的落到物體中心位置旺入,是有分數(shù)加成的兑凿,最多跳一次可以得幾十分
井蓋、商店茵瘾、唱片礼华、魔方,多停留一會拗秘,有音樂響起后也是有分數(shù)加成的
那么看一下程序員的朋友圈有多殘酷吧
原文作者:Samon
閱讀原文