最近很火的微信小程序跳一跳,玩了幾次分?jǐn)?shù)都低的可憐,所以作為碼農(nóng)的我就想著怎么通過程序去實(shí)現(xiàn)自動(dòng)跳啊跳啊跳啊跳。。。 下面簡單講講個(gè)人實(shí)現(xiàn)方法及過程,請(qǐng)連上android手機(jī),讓程序飛起來吧!
一、基本思路 微信跳一跳原理就是小人和目標(biāo)物體上表面中心點(diǎn)的直線距離,然后根據(jù)按住屏幕時(shí)間長短跳到相應(yīng)的位置, 擊中目標(biāo)物體上表面區(qū)域中心點(diǎn)附近即可得分。 我們可以看到微信跳一跳背景顏色是接近單調(diào)色,所以很容易就過濾掉背景色; 小人顏色也是固定的,所以也很容易定位小人所在的位置; 目標(biāo)物體顏色跟背景色區(qū)別比較大,也很容易定位; 采用adb shell 可以很簡單的控制Android手機(jī);
二、實(shí)現(xiàn)過程 軟硬件環(huán)境: adb、java、MAC、android手機(jī)一臺(tái)(1080*1920) MAC連接android手機(jī),手機(jī)端打開usb調(diào)試開關(guān); 首先是要獲取手機(jī)屏幕截圖,代碼如下: 注意:獲取屏幕截圖要等屏幕圖片穩(wěn)定下來,即跳完后3秒左右,不然一些彈出的圖像會(huì)干擾到計(jì)算點(diǎn)位
private static void getScreenshot() { .exec("/Users/gavin/adt-bundle-mac/sdk/platform-tools/adb shell /system/bin/screencap -p /sdcard/screenshot.png");
.exec("/Users/gavin/adt-bundle-mac/sdk/platform-tools/adb pull /sdcard/screenshot.png /Users/gavin/Downloads/screenshot.png"); System.out.print("Get screenshot success!\n");
通過截圖獲取小人和目標(biāo)物體的坐標(biāo),然后計(jì)算直線距離,最后乘以1.35ms,得到的就是長按的時(shí)間;
得到的測試圖片如下:
public static void getPosition() throws Exception { File file = new File(IMAGE_PATH);
// 只搜索屏幕中間矩形區(qū)域,可根據(jù)分辨率配置高度 int width = bi.getWidth();
// 獲取背景色值,這里直接選取坐標(biāo)(500, 500)這個(gè)點(diǎn)的顏色值,不同分辨率手機(jī)要依據(jù)實(shí)際情況修改 int pixel = bi.getRGB(500, 500); mBasicR = (pixel & 0xff0000) >> 16; mBasicG = (pixel & 0xff00) >> 8; mBasicB = (pixel & 0xff); System.out.println("mBasicR = " + mBasicR + ", mBasicG = " + mBasicG + ", mBasicB = " + mBasicB);
for (int j = miny; j < height; j++) { for (int i = minx; i < width; i++) { rgb[0] = (pixel & 0xff0000) >> 16; rgb[1] = (pixel & 0xff00) >> 8;
if (getColorOffset(mBasicR, mBasicG, mBasicB, rgb[0], rgb[1], rgb[2]) < mColorOffset){
// 小人顏色接近點(diǎn),用取色器取到的小人底部中心點(diǎn)顏色大致為R:55, G:55, B:93,所以可以搜索目標(biāo)區(qū)域的該顏色相近的區(qū)域 // 然后取X, Y坐標(biāo)最小和最大的兩個(gè)值 if (rgb[0] >= 50 && rgb[0] <= 60 && rgb[1] >= 50 && rgb[1] <= 60 && rgb[2] >= 90 && rgb[2] <= 95 ){
mPersonX = personStartX + ((personEndX - personStartX) / 2) - 15; mPersonY = personEndY - 20;
for (int j = miny; j < height; j++) { for (int i = minx; i < width; i++) { rgb[0] = (pixel & 0xff0000) >> 16; rgb[1] = (pixel & 0xff00) >> 8;
if (getColorOffset(mBasicR, mBasicG, mBasicB, rgb[0], rgb[1], rgb[2]) < mColorOffset){
// 過濾小人干擾,通過調(diào)試發(fā)現(xiàn)有時(shí)候小人會(huì)干擾判斷所以要過濾小人所在的縱向區(qū)域 if (Math.abs(i - mPersonX) < 50){
// 從上至下橫向便利每個(gè)點(diǎn)(排除上面的背景色和小人縱向區(qū)域),獲取到的第一個(gè)其他顏色點(diǎn)即為物體上邊緣點(diǎn) targetStartY = j + 10; // 加點(diǎn)偏移量,使其定位到物體較大面積區(qū)域的顏色 pixel = bi.getRGB(targetStartX, targetStartY); rgb[0] = (pixel & 0xff0000) >> 16; rgb[1] = (pixel & 0xff00) >> 8;
// 根據(jù)上面取到的上邊緣點(diǎn),縱向向下搜索相同顏色的點(diǎn)中Y坐標(biāo)最大的點(diǎn),注意要根據(jù)跳躍的進(jìn)度修改縱向搜索區(qū)域 // 這里的mTargetHeight 默認(rèn)值設(shè)為250,即搜索縱向250個(gè)像素點(diǎn),然后在主函數(shù)那個(gè)每10次遞減10直到最小值為20 // 可根據(jù)調(diào)試結(jié)果修改 if (targetStartX != 0 &&targetStartY != 0){ if (i >= targetStartX - 25 && i < targetStartX + 25 && j < targetStartY + mTargetHeight && getColorOffset(targetR, targetG, targetB, rgb[0], rgb[1], rgb[2]) <= 3){
mTargetY = targetStartY + ((targetEndY - targetStartY) / 2) - 10;
System.out.println("mPersonX = " + mPersonX + ", mPsersonY = " + mPersonY + ", mTargetX = " + mTargetX + ", mTargetY = " + mTargetY);
// 將處理完后的圖片,如小人位置,物體上下邊緣點(diǎn)和中心點(diǎn)、搜索矩形區(qū)域繪制好后保存,以便調(diào)試調(diào)整參數(shù) drawPoint(IMAGE_PATH, Color.green, mPersonX, mPersonY); drawPoint(mImageOutPath, Color.red, targetStartX, targetStartY); drawPoint(mImageOutPath, Color.red, targetEndX, targetEndY); drawPoint(mImageOutPath, Color.red, mTargetX, mTargetY); drawRect(mImageOutPath, 0, mStartY, width, mEndY - mStartY);
adb shell沒有長按,所以我用swipe替換,結(jié)果是一樣的。
根據(jù)第三步中獲取的位置計(jì)算滑動(dòng)時(shí)間,每個(gè)像素點(diǎn)1.35ms,我的測試機(jī)是1080*1920,不同分辨率不一樣可以自行調(diào)整匹配。
private static void swipeTime(long ms) { .exec("/Users/gavin/adt-bundle-mac/sdk/platform-tools/adb shell input swipe 400 400 600 600 " + ms);
運(yùn)行日志如下:
四、運(yùn)行
游戲打開,點(diǎn)擊開始游戲后
terminal 運(yùn)行:
有什么問題歡迎交流。
|