用JAVA和adb实现微信跳一跳全自动完成

最近很火的微信小程序跳一跳,玩了几次分数都低的可怜,所以作为码农的我就想着怎么通过程序去实现自动跳啊跳啊跳啊跳。。。

下面简单讲讲个人实现方法及过程,请连上android手机,让程序飞起来吧!




一、基本思路


微信跳一跳原理就是小人和目标物体上表面中心点的直线距离,然后根据按住屏幕时间长短跳到相应的位置,

击中目标物体上表面区域中心点附近即可得分。

  • 我们可以看到微信跳一跳背景颜色是接近单调色,所以很容易就过滤掉背景色;
  • 小人颜色也是固定的,所以也很容易定位小人所在的位置;
  • 目标物体颜色跟背景色区别比较大,也很容易定位;
  • 采用adb shell 可以很简单的控制Android手机;


二、实现过程

软硬件环境: adb、java、MAC、android手机一台(1080*1920)

  1. MAC连接android手机,手机端打开usb调试开关;
  2. 首先是要获取手机屏幕截图,代码如下:
    注意:获取屏幕截图要等屏幕图片稳定下来,即跳完后3秒左右,不然一些弹出的图像会干扰到计算点位
    private static void getScreenshot() {  
            try {  
                // 获取手机截图
                Runtime.getRuntime()  
                .exec("/Users/gavin/adt-bundle-mac/sdk/platform-tools/adb shell /system/bin/screencap -p /sdcard/screenshot.png");  
                Thread.sleep(1000);  
    
                // 上传手机截图到电脑
                Runtime.getRuntime()  
                .exec("/Users/gavin/adt-bundle-mac/sdk/platform-tools/adb pull /sdcard/screenshot.png /Users/gavin/Downloads/screenshot.png");
                Thread.sleep(1000);  
                System.out.print("Get screenshot success!\n");
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
              
        }  

  3. 通过截图获取小人和目标物体的坐标,然后计算直线距离,最后乘以1.35ms,得到的就是长按的时间;
    public static void getPosition() throws Exception {  
            int[] rgb = new int[3];  
            File file = new File(IMAGE_PATH);  
            BufferedImage bi = null;  
            try {  
                bi = ImageIO.read(file);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
    
            // 只搜索屏幕中间矩形区域,可根据分辨率配置高度
            int width = bi.getWidth();  
            int height = mEndY; 
            int minx = bi.getMinX();  
            int miny = mStartY;  
    
            int personStartX = 0;
            int personStartY = 0;
            int personEndX = 0;
            int personEndY = 0;
            
            int targetStartX = 0;
            int targetStartY = 0;
            int targetEndX = 0;
            int targetEndY = 0;
            int targetR = 0;
            int targetG = 0;
            int targetB = 0;
            
            // 获取背景色值,这里直接选取坐标(500, 500)这个点的颜色值,不同分辨率手机要依据实际情况修改
            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++) {  
                    pixel = bi.getRGB(i, j); 
                    rgb[0] = (pixel & 0xff0000) >> 16;  
                    rgb[1] = (pixel & 0xff00) >> 8;  
                    rgb[2] = (pixel & 0xff);  
    
                    // 背景颜色跳过
                    if (getColorOffset(mBasicR, mBasicG, mBasicB, rgb[0], rgb[1], rgb[2]) < mColorOffset){
                    	continue;
                    }
                   
                    // 小人颜色接近点,用取色器取到的小人底部中心点颜色大致为R:55, G:55, B:93,所以可以搜索目标区域的该颜色相近的区域
                    // 然后取X, Y坐标最小和最大的两个值
                    // 最后计算最大最小值的中间值
                    if (rgb[0] >= 50 && rgb[0] <= 60 && rgb[1] >= 50 && rgb[1] <= 60 && rgb[2] >= 90 && rgb[2] <= 95 ){
                    	if (personStartX == 0){
                    		personStartX = i;
                    		personStartY = j;
                    		personEndX = i;
                    		personEndY = j;
                    	}
                    	
                    	if (i <= personStartX){
                    		personStartX = i;
                    	}
                    	if (j <= personStartY){
                    		personStartY = j;
                    	}
                    	
                    	if (i >= personEndX){
                    		personEndX = i;
                    	}
                    	if (j >= personEndY){
                    		personEndY = j;
                    	}
                    }
                }  
            }  
            
            mPersonX = personStartX + ((personEndX - personStartX) / 2) - 15;
            mPersonY = personEndY - 20;
            
            
            
            // 获取下一个物体位置
            for (int j = miny; j < height; j++) {  
            	for (int i = minx; i < width; i++) {  
                    pixel = bi.getRGB(i, j); 
                    rgb[0] = (pixel & 0xff0000) >> 16;  
                    rgb[1] = (pixel & 0xff00) >> 8;  
                    rgb[2] = (pixel & 0xff);  
    
                    // 背景颜色跳过
                    if (getColorOffset(mBasicR, mBasicG, mBasicB, rgb[0], rgb[1], rgb[2]) < mColorOffset){
                    	continue;
                    }
                    
                    // 过滤小人干扰,通过调试发现有时候小人会干扰判断所以要过滤小人所在的纵向区域
                    if (Math.abs(i - mPersonX) < 50){
                    	continue;
                    }
                    
                    // 从上至下横向便利每个点(排除上面的背景色和小人纵向区域),获取到的第一个其他颜色点即为物体上边缘点
                    if (targetStartX == 0){
                    	targetStartX = i;
                    	targetStartY = j + 10; // 加点偏移量,使其定位到物体较大面积区域的颜色
                    	pixel = bi.getRGB(targetStartX, targetStartY); 
                        rgb[0] = (pixel & 0xff0000) >> 16;  
                        rgb[1] = (pixel & 0xff00) >> 8;  
                        rgb[2] = (pixel & 0xff);  
                    	targetR = rgb[0];
                    	targetG = rgb[1];
                    	targetB = rgb[2];
                    }
                    
                    // 根据上面取到的上边缘点,纵向向下搜索相同颜色的点中Y坐标最大的点,注意要根据跳跃的进度修改纵向搜索区域
                    // 这里的mTargetHeight 默认值设为250,即搜索纵向250个像素点,然后在主函数那个每10次递减10直到最小值为20
                    // 可根据调试结果修改
                    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){
                    		targetEndX = i;
                    		targetEndY = j;
                    	}
                    }
                }  
            }  
            
            mTargetX = targetStartX;
            mTargetY = targetStartY + ((targetEndY - targetStartY) / 2) - 10;
            
            System.out.println("mPersonX = " + mPersonX + ", mPsersonY = " + mPersonY + ", mTargetX = " + mTargetX + ", mTargetY = " + mTargetY);
    
            // 将处理完后的图片,如小人位置,物体上下边缘点和中心点、搜索矩形区域绘制好后保存,以便调试调整参数
            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);
            
        }  

    得到的测试图片如下:
     

  4. adb shell没有长按,所以我用swipe替换,结果是一样的。

    根据第三步中获取的位置计算滑动时间,每个像素点1.35ms,我的测试机是1080*1920,不同分辨率不一样可以自行调整匹配。
    private static void swipeTime(long ms) {  
            try {  
                Runtime.getRuntime()  
                .exec("/Users/gavin/adt-bundle-mac/sdk/platform-tools/adb shell input swipe 400 400 600 600 " + ms);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
              
        }  

  5. 运行日志如下:




四、运行

 游戏打开,点击开始游戏后
 terminal 运行:
javac -d . Tiaotiao.java
java Tiaotiao

 有什么问题欢迎交流。


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页