600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 跳一跳辅助工具的原理分析 和Java实现。(其实没那么复杂)

跳一跳辅助工具的原理分析 和Java实现。(其实没那么复杂)

时间:2019-07-31 07:23:23

相关推荐

跳一跳辅助工具的原理分析 和Java实现。(其实没那么复杂)

一、前言

(Java代码的实现是基于另外一篇博客,我精简了计算方法而成,参考博客地址/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.pngd:/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,如下图便是配置成功。需要的可以去/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="/lihushiwoa/article/details/78942322">/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="/npm/jquery/dist/jquery.min.js"></script><link rel="stylesheet" href="/npm/font-awesome/css/font-awesome.min.css"/><script src="/gh/stevenjoezhang/live2d-widget/autoload.js"></script>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。