1. 程式人生 > >跳一跳輔助工具的原理分析,和Java實現。(其實沒那麼複雜)

跳一跳輔助工具的原理分析,和Java實現。(其實沒那麼複雜)

一、前言

(Java程式碼的實現是基於另外一篇部落格,我精簡了計算方法而成,參考部落格地址http://blog.csdn.net/lihushiwoa/article/details/78942322)

先說一說我的感受,之前覺得能做出做出輔助工具的技術要求一定很高,然而當自己真正分析原理並且動手實現之後發現門檻沒有那麼高。

想做這個工具首先得知道adb是什麼,其次是會一門程式語言。我瞭解adb之後瞬間覺得思路豁然開朗。

adb是Android Debug Bridge。他的作用是可以通過在計算機cmd輸入命令控制Android手機,比如馬上要用到的效果----模擬實現觸控螢幕和觸控多長時間。所以你在java程式碼裡控制adb命令是可行的。

二、思路與原理

想象一下手動能讓棋子命中下一個platform的場景:

1:測量棋子到目標座標的距離S;

2:按壓時間T=S*時間係數;(時間係數:單位位移的用時,200ms的n倍,多次嘗試的大致確認

 然後你按螢幕時間T就可以了。

在本程式碼中我發現每個跳板的連線與豎直方向大約呈60°夾角(跳板和跳板雖然是60°,但是棋子不一定與目標是60°角關係呀?沒關係,經過測試與棋子的角度沒太大關係),所以如果我們知道棋子和目標點的距離就可以算S了。

測量夾角計算斜邊原理

程式碼實現也一樣:

1:adb命令截圖,存放到指定資料夾。adb命令分別是:

adb shell screencap -p /sdcard/current.png

adb pull /sdcard/current.png

  d:/jump_screencapture 

2:掃描棋子的X軸座標。

圖片是一個個畫素點組成,從上往下遍歷每一行畫素點,如果某畫素點的R、G、B值在棋子的RGB值區間(R∈(50,60),G∈(53,63),B∈(95,110)),那麼可以確定該畫素點在棋子內了,然後記下這是第幾個(halmaXCount),並且計算橫座標的和halmaSum+=halmaSum(原理:多次測量取平均值,精確棋子的X軸座標)。

3:掃描目標跳板的橫座標。原理與掃描棋子相似,但又不同。

原理:由於跳板是左右對稱的,也就是說假如目標跳板的上表面是標準的菱形,那麼一旦遍歷到菱形上方的頂點,那麼該頂點的X座標就是目標跳板上表面中心的X座標。

不同之處是:遍歷完目標調班的首行畫素點便停止,(缺點:樣本空間略小,可能導致目標座標不精確,留日後優化。)。記錄每行首個畫素點的RGB值之後,在接下來的遍歷中如果發現本行與首個畫素點RGB差異太大,則認為該畫素點已經在目標跳板之內。找到所有不同於第一個畫素點的X座標求平均數,然後結束。

4:計算棋子與目標點的距離,和按壓時間。

三、ADB配置

ADB主要是Path配置,配置完,在cmd中輸入adb,如下圖便是配置成功。需要的可以去http://download.csdn.net/download/thread_cooperation/10212920下載adb配置

四、程式碼實現

注意:不要在開發工具裡跑,可能無法呼叫ADB。在cmd中是可行的。

import java.awt.image.BufferedImage;  
import java.io.BufferedReader;  
import java.io.File;  
import java.io.IOException;  
import java.io.InputStreamReader;  
import java.util.Arrays;  
import java.util.concurrent.TimeUnit;  
  
import javax.imageio.ImageIO;  
  
/** 
 * 參考CSDN
 *  @link <a target="_blank" href="http://blog.csdn.net/lihushiwoa/article/details/78942322">http://blog.csdn.net/lihushiwoa/article/details/78942322</a> 
 *  
 * 說明:本版本是在上述參考CSDN的基礎上,精簡計算而成,目前支援解析度1920x1080的安卓機(在下沒有其他解析度的手機用於測試)
 *  
 * 跳一跳輔助 
 * @author ykq 
 */  
public class Jump {  
  
    private static final String IMAGE_NAME              = "current.png";  
  
    private static final String STORE_DIR               = "d:/jump_screencapture";  
  
    //截圖的數量上限  
    private static final int    imageLengthLength       = 5;  
  
    //存放圖片的陣列
    private static final long[] imageLength             = new long[imageLengthLength];  
  
    private final RGBInfo       rgbInfo                 = new RGBInfo();  
  
    //adb指令碼
    private final String[]      ADB_SCREEN_CAPTURE_CMDS =  
                                                        { "adb shell screencap -p /sdcard/" + IMAGE_NAME,  
            "adb pull /sdcard/current.png " + STORE_DIR };  
  
    //截圖中游戲分數顯示區域最下方的Y座標,300是 1920x1080的值,根據實際情況修改  
    private final int           gameScoreBottomY        = 310;  
  
    //按壓的時間係數,可根據具體情況適當調節  
    private final double        pressTimeCoefficient    = 1.37;
    
    //按壓的起始點座標,也是再來一局的起始點座標  
    private final int           swipeX                  = 550;  
  
    private final int           swipeY                  = 1580;  
  
    //棋子的寬度,從截圖中量取,自行調節  
    private final int           halmaBodyWidth          = 74;

    //csc(pi/6)
    private final static double 		proportion 				= 2/Math.sqrt(3);
    
    public static void main(String[] args){  
        try{  
            File storeDir = new File(STORE_DIR);  //構建檔案目錄
            if (!storeDir.exists()) {  
               boolean flag = storeDir.mkdir();  //建立資料夾
               if (!flag) {  
                   System.err.println("建立圖片儲存目錄失敗");  
                   return;  
               }  
            }  
              
            Jump jumpjumpHelper = new Jump();  
            //執行次數  
            int executeCount = 0;  
            for (;;){  
                //執行ADB命令,獲取安卓截圖  
                jumpjumpHelper.executeADBCaptureCommands();  
                File currentImage = new File(STORE_DIR, IMAGE_NAME);  
                if (!currentImage.exists()){  
                    System.out.println("圖片不存在");  
                    continue;  
                }  
  
                long length = currentImage.length();  
                imageLength[executeCount % imageLengthLength] = length;  
                //檢視是否需要重新開局  
                jumpjumpHelper.checkDoReplay();  
                executeCount++;  
                System.out.println("當前第" + executeCount + "次執行!");  
                //獲取跳棋和底板的中心座標  
                int[] result = jumpjumpHelper.getHalmaAndBoardXYValue(currentImage);   //Halma跳棋
                if (result == null){  
                    System.out.println("The result of method getHalmaAndBoardXYValue is null!");  
                    continue;  
                }  
                int halmaX = result[0];  
                int boardX = result[1];  
                System.out.println("halmaX: " + halmaX + "  ****  " + "boardX: " + boardX);  
                //計算跳躍的距離  
                double jumpDistance = Math.abs(boardX-halmaX)*proportion;
                jumpjumpHelper.doJump(jumpDistance);  
                //每次停留2.5秒  
                TimeUnit.MILLISECONDS.sleep(2500);  
            }  
        }  
        catch (Exception e){  
            e.printStackTrace();  
        }  
    } 
  
    /** 
     * 獲取跳棋以及下一塊跳板的中心座標 
     * 
     * @return 
     * @throws IOException 
     */  
    public int[] getHalmaAndBoardXYValue(File currentImage) throws IOException{  
        BufferedImage bufferedImage = ImageIO.read(currentImage);  
        int width = bufferedImage.getWidth();  
        int height = bufferedImage.getHeight();  
        System.out.println("寬度:" + width + ",高度:" + height);  
        int halmaXSum = 0;  
        int halmaXCount = 0;  
        int boardX = 0;  
        //從截圖從上往下逐行遍歷畫素點,以棋子顏色作為位置識別的依據,最終取出棋子顏色最低行所有畫素點的平均值,即計算出棋子所在的座標  
        for (int y = gameScoreBottomY; y < height; y++){  
            for (int x = 0; x < width; x++){  
                processRGBInfo(bufferedImage, x, y);  //獲取指定座標的RGB值 
                int rValue = this.rgbInfo.getRValue();  
                int gValue = this.rgbInfo.getGValue();  
                int bValue = this.rgbInfo.getBValue();  
                //根據RGB的顏色來識別棋子的位置,  
                if (rValue > 50 && rValue < 60 && gValue > 53 && gValue < 63 && bValue > 95 && bValue < 110){  
                    halmaXSum += x;  
                    halmaXCount++;  
                }  
            }  
        }  
  
        if (halmaXSum != 0 && halmaXCount != 0){  
            //棋子底行的X座標值  
            int halmaX = halmaXSum / halmaXCount;  
            
            //從gameScoreBottomY開始,獲取棋子的x座標
            for (int y = gameScoreBottomY; y < height; y++){  
                processRGBInfo(bufferedImage, 0, y);  //每行第一個畫素的RGB(獲取指定座標的RGB值 )
                int lastPixelR = this.rgbInfo.getRValue();  
                int lastPixelG = this.rgbInfo.getGValue();  
                int lastPixelB = this.rgbInfo.getBValue();  
                //只要計算出來的boardX的值大於0,就表示下個跳板的中心座標X值取到了。  
                if (boardX > 0){  
                    break;  
                }  
                int boardXSum = 0;  
                int boardXCount = 0;  
                
                for (int x = 0; x < width; x++){  
                    processRGBInfo(bufferedImage, x, y);  
                    int pixelR = this.rgbInfo.getRValue();  
                    int pixelG = this.rgbInfo.getGValue();  
                    int pixelB = this.rgbInfo.getBValue();  
                    //處理棋子頭部比下一個跳板還高的情況      跳過棋子所在的x軸區域不遍歷(防止檢索到棋子的顏色)
                    if (Math.abs(x - halmaX) < halmaBodyWidth){    //abs絕對值
                        continue;  
                    }  
  
                    //從上往下逐行掃描至下一個跳板的頂點位置,下個跳板可能為圓形,也可能為方框,取多個點,求平均值  
                    if ((Math.abs(pixelR - lastPixelR) + Math.abs(pixelG - lastPixelG) + Math.abs(pixelB - lastPixelB)) > 8){   
                        boardXSum += x;  
                        boardXCount++;  
                    }  
                }  
  
                if (boardXSum > 0){  
                    boardX = boardXSum / boardXCount;  
                }  
            }  
  
            if (boardX > 0){  
                int[] result = new int[2];  
                //棋子的X座標  
                result[0] = halmaX;  
                //下一塊跳板的X座標    
                result[1] = boardX;  
                return result;  
            }  
        }  
  
        return null;  
    }  
  
    /** 
     * 執行命令 
     * 
     * @param command 
     */  
    private void executeCommand(String command){  
        Process process = null;  
        try{  
            process = Runtime.getRuntime().exec(command);  
            System.out.println("exec command start: " + command);  
            process.waitFor();  
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));  
            String line = bufferedReader.readLine();  
            if (line != null){  
                System.out.println(line);  
            }  
            System.out.println("exec command end: " + command);  
        }  
        catch (Exception e){  
            e.printStackTrace();  
        }  
        finally{  
            if (process != null){  
                process.destroy();  
            }  
        }  
    }  
  
    /** 
     * ADB獲取安卓截圖 
     */  
    public void executeADBCaptureCommands(){  
        for (String command : ADB_SCREEN_CAPTURE_CMDS)  
        {  
            executeCommand(command);  
        }  
    }  
  
    /** 
     * 跳一下 
     * 
     * @param distance 
     */  
    public void doJump(double distance){  
        System.out.println("distance: " + distance);  
        //計算按壓時間,最小200毫秒  
        int pressTime = (int) Math.max(distance * pressTimeCoefficient, 200);  
        System.out.println("pressTime: " + pressTime);  
        //執行按壓操作  
        String command = String.format("adb shell input swipe %s %s %s %s %s", swipeX, swipeY, swipeX, swipeY,  
                pressTime);  
        System.out.println(command);  
        executeCommand(command);  
        System.out.println();
        System.out.println();
    }  
  
    /** 
     * 再來一局 
     */  
    private void replayGame(){  
        String command = String.format("adb shell input tap %s %s", swipeX, swipeY);  //adb shell模擬點選事件 input
        executeCommand(command);  
    }  
  
    /** 
     * 檢查是否需要重新開局 
     */  
    public void checkDoReplay(){  
        if (imageLength[0] > 0 && imageLength[0] == imageLength[1] && imageLength[1] == imageLength[2]  
                && imageLength[2] == imageLength[3] && imageLength[3] == imageLength[4]){  
            //此時表示已經連續5次圖片大小一樣了,可知當前螢幕處於再來一局  
            Arrays.fill(imageLength, 0);  
            //模擬點選再來一局按鈕重新開局  
            replayGame();  
        }  
    }  
  
    /** 
     * 獲取指定座標的RGB值 
     * 
     * @param bufferedImage 
     * @param x 
     * @param y 
     */  
    private void processRGBInfo(BufferedImage bufferedImage, int x, int y){  
        this.rgbInfo.reset();  
        int pixel = bufferedImage.getRGB(x, y);  
        //轉換為RGB數字    
        this.rgbInfo.setRValue((pixel & 0xff0000) >> 16);  
        this.rgbInfo.setGValue((pixel & 0xff00) >> 8);  
        this.rgbInfo.setBValue((pixel & 0xff));  
    }  
  
    
    /**************************  RGBInfo  ****************************/
    class RGBInfo{  
        private int RValue;  
  
        private int GValue;  
  
        private int BValue;  
  
        public int getRValue(){  
            return RValue;  
        }  
  
        public void setRValue(int rValue){ 
            RValue = rValue;  
        }  
  
        public int getGValue(){  
            return GValue;  
        }  
  
        public void setGValue(int gValue){  
            GValue = gValue;  
        }  
  
        public int getBValue(){  
            return BValue;  
        }  
  
        public void setBValue(int bValue){  
            BValue = bValue;  
        }  
  
        public void reset(){  
            this.RValue = 0;  
            this.GValue = 0;  
            this.BValue = 0;  
        }  
    }  
}  


相關推薦

輔助工具原理分析Java實現其實那麼複雜

一、前言(Java程式碼的實現是基於另外一篇部落格,我精簡了計算方法而成,參考部落格地址http://blog.csdn.net/lihushiwoa/article/details/78942322)先說一說我的感受,之前覺得能做出做出輔助工具的技術要求一定很高,然而當自己

Rxjava2.x 原始碼分析以及手動實現Rxjava

這兩年Rxjava火的一塌糊塗,不會點Rxjava+Okhttp+Retrofit+MVP+Dagger2架構都不好意思說自己混Android的。Rxjava 到底是什麼和Rxjava到底怎麼用,這裡就不講了,網上太多了,具體可以參考 這位大佬 和扔物線的。  Rxjava

簡述 fbprophetPyStan庫安裝win10-64位系統

fbprophet依賴於PyStan,所以首先要安裝PyStan庫,而要安裝PyStan,首先要安裝C++編譯器 在自己剛用到的時候首先百度了下發現好多坑,試了很多百度的方法還是不行。  本人是win10-64位系統,使用的是Python3.7的Anaconda。廢話不說了,開始安

任意長度的十進位制數轉為二進位制、十六進位制大數除法演算法只有小半份

輾轉求餘法實現的任意長度十進位制數到2進位制和16進位制轉換方法 Sub asdf() Debug.Print Dec2Bin("321412341235123412341512341235123

空間點過程分析的R語言實現+PART11~4.6

    研究WSN空間覆蓋能力的論文或多或少會假設隨機部署的節點位置是服從柏鬆點過程(Possion Point Process,PPP)的,剛接觸到這個概念也是挺懵了,之前學過隨機過程、排隊論都是講的一維上的Possion Process,而二維平面上的PPP如何實現呢?在許多論壇上搜索

XPS檔案在Windows XP下的開啟檢視閱讀列印方法多圖詳細講解

http://blog.sina.com.cn/s/blog_3f81182c0100v64t.html 要列印誠信手冊,下載下來竟然是XPS的格式。怎麼開啟呢?公司的電腦都是WINDOWS XP的,身邊沒有WIN7.0或者WIN VISTA。用GOOGLE搜出一大堆垃

微信輔助JAVA最容易理解的演算法實現原理分析

上幾周更新微信後,進入歡迎介面就提示出讓玩一把微信小遊戲《跳一跳》。一向不愛玩遊戲的我(除了經典QQ飛車、CS外),當時抱著沒興趣的態度簡單看了下,沒有玩。與朋友玩耍時,常聽他們聊起這個小遊戲,偶爾也在網頁和微信公眾號上看見些關於這個小遊戲的一些話題,為了不落伍,我決定繼續

微信“輔助小米5配置分享-實測900+

ctr pow pie 後臺 hit python wechat pre round 我的手機屏幕分辨率為1920x1080,所以修改 /config/1920x1080/config.json{ "under_game_score_y": 300

輔助工具設計筆記

lin androi platform pos per width android開發 OS ott 分析: 步驟1、獲取Jumper 與 platefrom 位置 步驟2、距離轉換為屏幕按壓時間,使用api模擬點擊屏幕 步驟1實現方案: 方案一: 找到跳一跳遊戲的

python實現微信輔助工具

說明 1.windows上安裝安卓模擬器,安卓版本5.1以上 2.模擬器裡下載安裝最新的微信6.6.1 3.最好使用python2.7,python3的pyhook包有bug,解決比較麻煩 步驟

微信輔助工具Python

1.準備工具 adb驅動 –> 最好下載最新的版本,因為安卓對系統的效能有所提高,對驅動的要求也更高 ( 連結:https://pan.baidu.com/s/1qZqAxT6 密碼:6

原創微信外掛源碼、熱門遊戲輕松上千分

鼠標右鍵 abs control set rsh ptr img left 版本 看到別人跳一跳搞了很多分。 我也跳一下。最高分才十幾分,被別人秒殺。 於是制作這個外掛,能夠輕松上千分。 原理很簡單,計算出兩點距離,測試出按下的時間,就可以了。 現在開始嘍 當然是

貼一個微信小程序輔助

main other 根據 err button 別人 建議 pri tar //此程序根據微信公眾號DotNet的文章》net開發一個微信跳一跳輔助而來, 其核心時間系數值直接引用自文章; 1.窗體 using System;using System.Collect

.net開發 微信小遊戲輔助程序

strong 系統 圖片 src 路徑 div net開發 微信 計算 一次巧合我看到了一篇關於微信小遊戲跳一跳的輔助開發源碼,鏈接:http://mp.weixin.qq.com/s/qGpoHNEf1A2AlofKFVdE2w 然後我試著下載下來跑一遍看能不能運行,

JAVA實現輔助程序之虎嘯龍吟

total 一位 gre wid put tar 圖片 ole jump 前序: 今天有幸,看到2位博主的文章,在此表示感謝。自己也動手實現了一下。 實現原理 請參考博主 https://www.cnblogs.com/dongkuo/p/8285162.html 另感

作ADB輔助微信[C#]

gae get dxf www. com ioi amp 微信 div ph7lll弦媳彩難且研http://www.58pic.com/c/122313258mk60m恫揮輛燎胺倍http://www.58pic.com/c/1223253375fjvz殉哨粵吹拍屠htt

除了在坦克大戰、吃雞、上作弊物理外掛帶給我們什麽腦洞?

絕地求生 外掛 外掛,是什麽?如果換個高大上的說法,其實是遊戲玩家為了解放雙手而創造出來的產品。當然,實際來說就是為了偷懶。當然,還有更高大上的說法,比如成為發明創造的起點。我自己就深有感觸。話說,小時候在紅白機上玩《坦克大戰》,為了省事,我也曾用夾子卡住射擊的那個按鈕,然後解放雙手嗑瓜子的說。這可以

python 打造一個微信輔助手機本地運行

.com simple pre 註釋 ima 獲取 IE 分享圖片 mask 先上成果效果圖: 用opencv 做識圖識別出棋子的坐標並把它框出來 終點位置的坐標是: 先觀察圖像發現棋子每跳過後的下一個目標點總是在棋子的上面 這樣就可以先獲取一個感興趣的區域,用num

微信 可以直接更改分數 POST 請求沒有校驗

重啟 微信 chrome 蘋果 版本 weixin for 發現 AR 這兩天逛 v 站出現了一眾微信跳一跳 ‘AI‘,已經被刷屏了…… https://www.v2ex.com/t/418833 https://www.v2ex.com/t/418775 https:/

Android LayoutInflater原理分析帶你步步深入瞭解View

有段時間沒寫部落格了,感覺都有些生疏了呢。最近繁忙的工作終於告一段落,又有時間寫文章了,接下來還會繼續堅持每一週篇的節奏。 有不少朋友跟我反應,都希望我可以寫一篇關於View的文章,講一講View的工作原理以及自定義View的方法。沒錯,承諾過的文章我是一定要兌現的,而且在View這個話題上我還