跳一跳辅助工具的原理分析,和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。在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模拟点击事件 inputexecuteCommand(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; } }
}
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css"/>
<script src="https://cdn.jsdelivr.net/gh/stevenjoezhang/live2d-widget/autoload.js"></script>
跳一跳辅助工具的原理分析,和Java实现。(其实没那么复杂)相关推荐
- python跳一跳编程构造_python实现微信跳一跳辅助工具步骤详解
说明 1.windows上安装安卓模拟器,安卓版本5.1以上 2.模拟器里下载安装最新的微信6.6.1 3.最好使用python2.7,python3的pyhook包有bug,解决比较麻烦 步骤 1. ...
- python hook pc微信_python实现微信跳一跳辅助工具步骤详解
说明 1.windows上安装安卓模拟器,安卓版本5.1以上 2.模拟器里下载安装最新的微信6.6.1 3.最好使用python2.7,python3的pyhook包有bug,解决比较麻烦 步骤 1. ...
- python实现微信hook_python实现微信跳一跳辅助工具步骤详解
说明 1.windows上安装安卓模拟器,安卓版本5.1以上 2.模拟器里下载安装最新的微信6.6.1 3.最好使用python2.7,python3的pyhook包有bug,解决比较麻烦 步骤 1. ...
- python hook 微信_python实现微信跳一跳辅助工具步骤详解
说明 1.windows上安装安卓模拟器,安卓版本5.1以上 2.模拟器里下载安装最新的微信6.6.1 3.最好使用python2.7,python3的pyhook包有bug,解决比较麻烦 步骤 1. ...
- python辅助_Python-第一个微信小项目——微信跳一跳辅助工具
思路 核心:每次落稳之后截图,根据截图算出棋子的坐标和下一个块顶面的中点坐标,根据两个点的距离乘以一个时间系数获得长按的时间 识别棋子:靠棋子的颜色来识别位置,通过截图发现最下面一行大概是一条直线,就 ...
- 一个简单的微信跳一跳辅助工具(android)
前两天手机微信升级到了最新版,多了个跳一跳的游戏,无聊玩了几把,奈何手残,最高只玩到70多分,就想着能不能搞个工具辅助下,于是有了下边的工具: 工具的简单原理如下: 根据前后两个方块的距离计算出一个按 ...
- 基于Node.js的微信跳一跳辅助工具
项目地址:https://github.com/sbfkcel/WechatJumpGameHelper 这是一个需要手动来玩的辅脚本,也是最为保险,官方无法禁封的辅助工具: 支持Mac OS.win ...
- 微信跳一跳高分系列四:一个 JAVA 版开源的微信跳一跳辅助工具
基于前三篇文章,我们一起来制作一个自动化工具,解放劳动力,通过机器自动完成跳一跳 wechat-jump-helper 一款JAVA版开源的微信跳一跳小程序辅助工具 传送门一:wechat-jump- ...
- 基于VS+Opencv2.4.10的微信跳一跳辅助工具
说明:最近微信的跳一跳小程序可谓火了一把,不是因为它本身多好玩,而是有大部分的程序员们加入其中,利用各种领域方法,实现了微信跳一跳的外挂,分数轻松上千或上万.之前也看了基于Python开源的代码,Gi ...
最新文章
- tdk怎么设置_不知道怎么分析对手网站?看这里!
- html圆形修饰,HTML修饰效果集锦(演示与代码)
- Python+opencv 机器视觉 - 基于霍夫圈变换算法检测图像中的圆形实例演示
- VC++打开、保存文件对话框和浏览文件夹对话框
- SQL Server 2008 BIDS组件的安装
- Java 8 新特性:扩展注解(类型注解和重复注解)
- java线程“生产/消费”模型2
- [css] css中的url()要不要加引号?说说你的理解
- 【Pytorch神经网络理论篇】 20 神经网络中的注意力机制
- php api查询开发,PHP开发API接口(注册、登录、查询用户信息)的实例代码
- Servlet 介绍
- JDK成年了,JDK18版本发布,走进JDK18新特性
- 5v继电器模块实物接线_继电器实物接线图
- 将uiimageview设置成纯圆形
- 一张图片切割成九宫格,微信朋友圈发布
- 常见的计算机网络联网设备有哪些,常见的网络设备有哪些
- HBA-蜜獾算法(Honey Badger Algorithm,HBA)(算法源码可复制)
- STM32 Keil快速新建工程
- 鸿蒙音波萨顶顶,萨顶顶卸妆堪比整容,“神婆”变女神,网友:早就该这样!...
- 强大的图像查看器:EdgeView mac中文
热门文章
- windows系统,使用命令行启动MySQL服务失败,报错:服务名无效。
- 市场聚焦丨微信人工智能小程序,销售界的又一个风口?
- QT学习笔记:简单的串口调试助手--实现 字符与十六进制发送接收
- 2020中国高校计算机大赛·华为云大数据挑战赛热身赛--EDA
- 【Vue+Java】前后端联动生成下载二维码
- 利用java多态实现植物大战僵_植物大战僵尸自动收集阳光金币
- Echarts中的 emphasis
- java futuretask 状态_java并发编程之FutureTask
- educoder机器学习 --- 多分类学习
- Android判断应用是否在前台或后台