微信跳一跳辅助JAVA 自动模拟点击

工具:ADB

原理:

  1. 开始游戏后,使用ADB工具让手机截屏发送到电脑
  2. 分析图像中小人与目标中心点间的距离,根据一定比例计算出需要触屏的时间
  3. 使用ADB进行模拟点击(触屏)相应的时间,完成精准跳跃

程序代码:

  1 import java.awt.EventQueue;
  2 import java.awt.Graphics;
  3 
  4 import javax.swing.JFrame;
  5 import javax.swing.JLabel;
  6 import java.awt.BorderLayout;
  7 import java.awt.Color;
  8 
  9 import javax.imageio.ImageIO;
 10 import javax.swing.ImageIcon;
 11 import java.awt.event.MouseAdapter;
 12 import java.awt.event.MouseEvent;
 13 import java.awt.image.BufferedImage;
 14 import java.io.File;
 15 import java.io.IOException;
 16 import java.math.BigDecimal;
 17 
 18 public class Skip {
 19     /*
 20      * 以下参数根据手机配置自行调整:
 21      */
 22     private final int time = 2800;        //执行跳跃间隔时间(单位:ms)
 23     private final double scale = 2.04;    //用于计算按下屏幕时间的比例,触屏时间=距离*scale
 24     
 25     private JFrame frame;                //窗体
 26     private JLabel lblNewLabel;            //用于显示图片的Label
 27     private BufferedImage image;        //手机截屏
 28     private int skipTime = 0;            //跳跃次数
 29     /**
 30      * Launch the application.
 31      */
 32     public static void main(String[] args) {
 33         EventQueue.invokeLater(new Runnable() {
 34             public void run() {
 35                 try {
 36                     Skip window = new Skip();        //跳一跳类实例
 37                     window.frame.setVisible(true);    //设置窗体可见
 38                 } catch (Exception e) {
 39                     e.printStackTrace();
 40                 }
 41             }
 42         });
 43     }
 44 
 45     /**
 46      * Create the application.
 47      */
 48     public Skip() {
 49         initialize();        //初始化
 50     }
 51 
 52     /**
 53      * Initialize the contents of the frame.
 54      */
 55     private void initialize() {
 56         image = Control.getScreen();            //截取第一个图片
 57         Control.getFoot(image);                    //寻找计算小人位置
 58         Control.getTarget(image);                //寻找目标中心
 59         ImageIcon imageIcon = new ImageIcon(image);        //以截图创建一个ImageIcon对象,供标签使用
 60         
 61         frame = new JFrame();                    //创建一个窗体
 62         frame.setBounds(100, 0, imageIcon.getIconWidth(), imageIcon.getIconHeight());    //设置窗体大小位置
 63         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        //关闭窗体时结束进程
 64         
 65         lblNewLabel = new JLabel("手机屏幕");        //创建标签
 66         //给标签添加单机时间:单机后开始自动跳跃
 67         lblNewLabel.addMouseListener(new MouseAdapter() {
 68             @Override
 69             public void mouseClicked(MouseEvent e) {
 70                 Thread skip = new Thread(new SkipRun());
 71                 skip.start();
 72             }
 73         });
 74         lblNewLabel.setIcon(imageIcon);            //设置标签图片
 75         frame.getContentPane().add(lblNewLabel, BorderLayout.CENTER);        //添加标签到窗体中
 76     }
 77 
 78     //单步跳跃
 79     protected void skip() {
 80         double distance = Control.getDistance(image);        //计算距离
 81         Control.mouseon(Integer.parseInt(new BigDecimal(String.valueOf(distance * scale)).setScale(0, BigDecimal.ROUND_HALF_UP).toString()));            //模拟触屏
 82         try {
 83             //记录本次图片
 84             ImageIO.write(image, "png", new File("debug" + skipTime++ + ".png"));
 85         } catch (IOException e) {
 86             e.printStackTrace();
 87         }
 88     }
 89     
 90     
 91     //自动跳跃主线程
 92     class SkipRun implements Runnable{
 93 
 94         @Override
 95         public void run() {
 96             //循环跳跃
 97             while(true) {
 98                 skip();
 99                 try {
100                     //小人跳跃需要花一定时间,在此等待3秒
101                     Thread.sleep(time);
102                 } catch (InterruptedException e1) {
103                     e1.printStackTrace();
104                 }
105                 //将当前手机屏幕显示在窗体中
106                 image = Control.getScreen();
107                 lblNewLabel.setIcon(new ImageIcon(image));
108             }
109         }
110         
111     }
112     
113 }
114 
115 /**
116  * 工具类,负责发送ADB命令、计算小人与目标距离
117  */
118 class Control {
119     private static int footX = 0, footY = 0;        //小人底部坐标
120     private static int targetX = 0,targetY = 0;        //目标方块中心坐标
121     //获取截屏
122     public static BufferedImage getScreen(){
123         //截屏
124         try {
125             //手机截屏,存储到SD卡
126             Process process = Runtime.getRuntime().exec("adb shell screencap /sdcard/screen.png");
127             process.waitFor();
128             //将截图传到电脑,以便接下来的分析
129             process = Runtime.getRuntime().exec("adb pull /sdcard/screen.png screen.png");
130             process.waitFor();
131         } catch (IOException | InterruptedException e) {
132             //异常处理
133             e.printStackTrace();
134         }
135         //加载文件
136         File file = new File("screen.png");
137         if (file == null || !file.exists()) {
138             System.out.println("获取截屏失败");
139             return null;
140         }
141         
142         //加载图片
143         BufferedImage image = null;
144         try {
145             image = ImageIO.read(file);
146         } catch (IOException e) {
147             e.printStackTrace();
148         }
149         return image;
150     }
151     
152     //长按屏幕
153     public static void mouseon(int time) {
154         try {
155             Runtime.getRuntime().exec("adb shell input swipe 200 200 200 200 " + time);
156         } catch (IOException e) {
157             // TODO Auto-generated catch block
158             e.printStackTrace();
159         }
160     }
161     
162     //计算距离
163     public static double getDistance (BufferedImage image) {
164         getFoot(image);            //获取小人坐标
165         getTarget(image);        //获取目标坐标
166         return Math.sqrt((footX - targetX) * (footX - targetX) + (footY - targetY) * (footY - targetY));
167     }
168     
169     public static void getFoot(BufferedImage image){
170         
171         Color footColor = new Color(54,60,102);        //小人底部颜色
172         int top = (int)(image.getHeight() * 0.5);        //扫描范围顶部(X)
173         int bottom = (int) (image.getHeight() * 0.7);    //扫描范围底部(X)
174         /*
175          * 自底向上扫描小人脚下位置
176          */
177         for(int i = bottom;i > top;i--)
178         {
179             for(int j = 0;j < image.getWidth();j++)
180             {
181                 //如果当前颜色与小人脚部颜色相近
182                 if(Math.abs(image.getRGB(j, i) - footColor.getRGB()) < 200) {
183                     footX = j;
184                     footY = i - 10;        //自左向右扫描,原始结果会偏左,在此稍向右移
185                     i = top;        //结束外层循环
186                     break;
187                 }
188             }
189         }
190         /*
191          * 用蓝色方块标记找到的小人位置
192          */
193         Graphics g = image.getGraphics();
194         g.setColor(Color.BLUE);
195         g.fillRect(footX - 5, footY - 5, 10, 10);
196     }
197     
198     public static void getTarget(BufferedImage image) {
199         
200         /*
201          * 第一步,找到目标方块上端顶点,该顶点的X坐标即中心的X坐标
202          */
203         
204         int top = (int)(image.getHeight() * 0.35);            //扫描范围顶部
205         int bottom = (int)(image.getHeight() * 0.49);        //扫描范围底部
206         Color headColor = new Color(56, 54, 71);
207         /*
208          * 自顶向下扫描目标方块顶端位置
209          */
210         for(int i = top;i < bottom;i++)
211         {
212             Color bgColor = new Color(image.getRGB(10, i));        //取背景色
213             for(int j = 0;j < image.getWidth();j++)
214             {
215                 /*
216                  * 小人可能高于目标方块,为排除干扰,略过小人所在的纵坐标(列)
217                  */
218                 if (j >= footX - 30 && j <= footX + 30) {
219                     continue;
220                 }
221                 Color color = new Color(image.getRGB(j, i));    //取当前颜色
222                 int t = 0;
223                 t = Math.abs(bgColor.getRGB() - color.getRGB());    //计算色差
224 
225                 if (t > 200000){    //如果与背景色不同
226                     targetX = j;    //记录行坐标
227                     targetY = i;    //记录纵坐标
228                     i = bottom;        //结束外层循环
229                     break;            //结束内层循环
230                 }
231             }
232         }
233         
234         /*
235          * 第二步,从顶点开始,向下扫描,找到方块占据最多列的那一行,即为目标的Y坐标
236          */
237         
238         int maxLength;            //方块的直径
239         int x = targetX;        //行扫描起始坐标
240         int y = targetY;        //直径所在行(y坐标)
241         
242         /*
243          * 如果在下面多行,该方块占据的列都相同,说明本行是中心所在行,
244          * 即Y坐标就是本行。使用flag记录连续相同的行数
245          */
246         
247         int flag = 0;            //不满足条件标志
248         for(int i = y + 1;i < y + 101 && flag < 8;i++)        //当连续8行直径相同后,结束循环
249         {
250             for(int j = x;j < image.getWidth();j++)
251             {
252                 Color bgColor = new Color(image.getRGB(image.getWidth() - 10, i));        //取背景色
253                 Color color = new Color(image.getRGB(j, i));        //取当前颜色
254                 if (( Math.abs(bgColor.getRGB() - color.getRGB())) <= 703400) {        //如果与背景色颜色相近
255                     if (j > x) {        //当前x坐标大于之前的x,(说明方块在本行占据的列更多)
256                         x = j;            //当前x坐标赋值给x
257                         targetY = i;    //直径所在行替换为当前y
258                         flag = 0;
259                     }else {                //当前x坐标不大于之前的x,(说明方块在本行占据的列不是最多)
260                         flag++;            //此标志+1            
261                     }
262                     break;
263                 }
264             }
265         }
266         targetY = targetY - flag;        //减去多加的flag
267         
268         /*至此,targetX与targetY寻找完毕*/
269         /*
270          * 用红色方块记录目标点
271          */
272         Graphics g = image.getGraphics();
273         g.setColor(Color.RED);
274         g.fillRect(targetX - 5, targetY - 5, 10, 10);
275     }
276 }

---恢复内容结束---