实现原理:
- 每次跳跃之前,截取一下手机屏幕,并将截图保存到本地电脑中;
- 计算截图中人偶的位置与将要跳至的台面中心的距离d d;
- 将以上距离d d换算成相应的触摸时间s s;
- 发送模拟触摸的命令至手机,触摸时间为以上时间s s;
重点是要计算出人偶与将要跳至的台面中心的距离,需要分别识别出人偶的位置(坐标)和台面中心的位置(坐标)。
我们以人偶最底部的一行的中心作为人偶的位置,如下图所示:
至于怎么识别出人偶的最底部,可以这样来操作。通过观察可发现,人偶底部的颜色的rgb值在(53, 57, 95)到(59, 61, 103)之间,因此我们逐行扫描各个像素点,找到rbg值在该区间的各行,最后一行即为人偶的底部了。得到了最底部的一行,自然就能算出该行的中心坐标。
接下来需要识别人偶将要跳至的平台的中心。要想得到该中心的坐标,我们只需要识别得到下图中的两个顶点vertex1和vertex2的坐标即可:
我们同样用从左往右,从上往下的顺序扫描各个像素点的方法来找出vertex1的坐标。扫描之前先获取整个背景的颜色的rgb值,取任意“空白”处即可(例如本人手机截图大小为1920x1080,可断定坐标为(40, 500)的点一定处于“空白”处。)。在扫描过程中一旦发现某处的颜色与背景色不一致,发生了“突变”,可断定该点即为vertex1。
我们把vertex1点的rgb值记录下来作为台面的背景色。在接下去的扫描过程中,我们开始关心当前扫描的点的rgb值是否和该记录值“相似”。“相似”则说明该点“属于”台面,而通过上图可发现,顶点vertex2是所有“属于”台面的点中,横坐标最小的点,这样vertex2的坐标也找到了。
显然,台面中心的横坐标等于vertex1的横坐标,而纵坐标等于vertex2的纵坐标。
步骤3
d d(单位:px)为时间s s(单位:毫秒)比较合适:
s=d∗1.35
代码实现:
1 package code;
2
3 import java.awt.AWTException;
4 import java.awt.Graphics2D;
5 import java.awt.Transparency;
6 import java.awt.image.BufferedImage;
7 import java.io.File;
8 import java.io.IOException;
9 import java.io.InputStreamReader;
10 import java.io.LineNumberReader;
11 import java.util.Map;
12 import java.util.Map.Entry;
13 import java.util.TreeMap;
14 import javax.imageio.ImageIO;
15
16 public class Jump {
17 //存放图片的路径
18 static String file1 = "C:\\Users\\JJJ\\Desktop\\jump3.jpg";
19 //始 扫描行,针对不同的手机分辨率可能不一样,需要修改,此处建议手工测量下坐上角跳跃步数的数字位置,估算出起始行 楼主的手机是se 像素是 1156*640
20 static int start_y = 200;
21 static boolean istest = false;
22 static int background_x = 10; //定义默认的背景色,一定是不会出现其他物体的位置
23 static int background_y = 580;
24
25 public static void main(String[] args) throws AWTException, InterruptedException, IOException {
26 istest = true;
27
28
29 System.out.println("开始:计算" + file1);
30 if (istest) {
31 cmd_java("cmd /c start adb.bat ", "C:\\Users\\JJJ\\Downloads\\cofface_adb\\cofface_adb_windows_v5.1");//批处理文件
32 }
33 BufferedImage bi = (BufferedImage) ImageIO.read(new File(file1));
34 Map<Integer, Integer> treemap = new TreeMap<Integer, Integer>();
35 // 获取图像的宽度和高度
36 int width = bi.getWidth();
37 int height = bi.getHeight();
38 //小人中心点坐标
39 int poix = 0;
40 int poiy = 0;
41 //目标物体的最顶点X轴 和左边或右边的Y轴
42 int mubiaopoix1 = 0;
43 int mubiaopoiy2 = 0;
44 //目标中心点坐标
45 int mubiaopoix = 0;
46 int mubiaopoiy = 0;
47 BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
48 Graphics2D g2d = img.createGraphics();
49 // 设置画布为透明
50 img = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
51 // 扫描图片,获取小人的底部中心坐标
52 for (int i = start_y; i < height; i++) {
53 for (int j = 0; j < width; j++) {// 行扫描
54 int dip = bi.getRGB(j, i);
55 int p = dip;
56 int red = 0xff & (p >> 16);
57 int green = 0xff & (p >> 8);
58 int blue = 0xff & p;
59 // 目标人图形
60 if (i < (height / 1.5))
61 {
62 if ((red > 53 && red < 59) && (green > 57 && green < 61) && (blue > 59 && blue < 103))
63 { // 获取小人坐标,从中取除Y值最大的一个
64 treemap.put(i, j);
65 }
66 }
67 }
68 }
69 //获取最后的小人底部坐标 ,此处使用treemap 直接排好序,循环获取Y轴值最大的一个
70 for (Entry<Integer, Integer> entry : treemap.entrySet())
71 {
72 poiy = entry.getKey();
73 poix = entry.getValue();
74 }
75 //开始扫描目标物体的最上 和最左坐标 ,扫描的Y最大值为当前小人的Y值
76 treemap.clear(); //清除前面的记录,准备存放目标物体的坐标
77 RGB rgb = null;
78 boolean frist = true;
79 RGB rgb_0 = pixToRgb(bi, background_x, background_y); //默认的背景色
80 for (int y = start_y; y < poiy; y++) {
81 int x = 0;
82 if (poix < width /2) //此处特别说明下,如果小人的坐标在整个屏幕的左边,则目标物体一定在右边,遂起始x轴加20开始循环,反之则不用
83 {
84 x = poix + 20;
85 }
86 else
87 {
88 x = 20;
89 }
90 for (; x < width - 20; x++) {// 行扫描
91 int dip = bi.getRGB(x, y);
92 int p = dip;
93 RGB rgb_1 = pixToRgb(bi, x, y);
94 if(frist && rgbCz(rgb_0,rgb_1,10)) //如果不相同则说明找到了第一个点
95 {
96 mubiaopoix1 = x;
97 rgb = rgb_1;
98 frist = false;
99 continue;
100 }
101 if (!frist && rgbBcz(rgb,rgb_1,10))
102 {
103 treemap.put(x, y); //存放所有当前台面的像素点坐标,然后从中选出X值最小的一个,
104 }
105 }
106 }
107 //获取目标物体的坐标,如果是在右半边,则获取最后一个值的Y轴,如果是在左边,则获取第一个
108 if (poix > width / 2)
109 {
110 for (Entry<Integer, Integer> entry : treemap.entrySet())
111 {
112 mubiaopoiy2 = entry.getValue();
113 break;
114 }
115 }
116 else
117 {
118 for (Entry<Integer, Integer> entry : treemap.entrySet())
119 {
120 mubiaopoiy2 = entry.getValue();
121 }
122 }
123 //通过获取的2个点坐标计算出中心点位置
124 mubiaopoix = mubiaopoix1;
125 mubiaopoiy = mubiaopoiy2;
126 //计算 小人与目标人物的举例
127 int total = (mubiaopoix - poix) * (mubiaopoix - poix) + (mubiaopoiy - poiy) * (mubiaopoiy - poiy);
128 double length = (double) Math.sqrt(total);
129 double time = length * 1.35; //时间系数
130 System.out.println("小人的坐标为:" + poix + "," + poiy);
131 System.out.println("目标物体的坐标为:" + mubiaopoix + "," + mubiaopoiy);
132 System.out.println("需要按压屏幕的时间为:" + time + "毫秒");
133 }
134
153
154 static InputStreamReader ir = null;
155 static LineNumberReader input = null;
156
157 public static void cmd_java(String cmd) throws IOException {
158 Process process = Runtime.getRuntime().exec(cmd, null, new File("C:\\Users\\chenyd\\adb"));
159 ir = new InputStreamReader(process.getInputStream());
160 input = new LineNumberReader(ir);
161 while (input.readLine() != null) {
162 }
163 input.close();
164 ir.close();
165 }
166
167 public static void cmd_java(String cmd, String url) throws IOException {
168 Process process = Runtime.getRuntime().exec(cmd, null, new File(url));
169 ir = new InputStreamReader(process.getInputStream());
170 input = new LineNumberReader(ir);
171 while (input.readLine() != null) {
172 System.out.println(11);
173 }
174 input.close();
175 ir.close();
176 }
177
178 /**
179 * 颜色的差值不在范围内
180 */
181 public static boolean rgbCz(RGB rgb_1, RGB rgb_2, int fd_rgb) {
182 if (Math.abs(rgb_1.getRed() - rgb_2.getRed()) > fd_rgb && Math.abs(rgb_1.getGreen() - rgb_2.getGreen()) > fd_rgb
183 && Math.abs(rgb_1.getBlue() - rgb_2.getBlue()) > fd_rgb) {
184 return true;
185 }
186 return false;
187 }
188
189 /**
190 * 颜色的差值在范围内
191 */
192 public static boolean rgbBcz(RGB rgb_1, RGB rgb_2, int fd_rgb) {
193 if (Math.abs(rgb_1.getRed() - rgb_2.getRed()) < fd_rgb && Math.abs(rgb_1.getGreen() - rgb_2.getGreen()) < fd_rgb
194 && Math.abs(rgb_1.getBlue() - rgb_2.getBlue()) < fd_rgb) {
195 return true;
196 }
197 return false;
198 }
199
200 public static RGB pixToRgb(BufferedImage bi, int j, int i) {
201 try {
202 int dip = bi.getRGB(j, i);
203 int p = dip;
204 int red = 0xff & (p >> 16);
205 int green = 0xff & (p >> 8);
206 int blue = 0xff & p;
207 return new RGB(j, i, red, green, blue);
208 } catch (Exception e) {
209
210 }
211 return null;
212 }
213
214 }
215
216 class RGB {
217
218 public RGB() {
219 }
220
221 public RGB(int x, int y, int red, int green, int blue) {
222 super();
223 X = x;
224 Y = y;
225 this.red = red;
226 this.green = green;
227 this.blue = blue;
228 }
229
230 public int X;
231 public int Y;
232 public int red;
233 public int green;
234 public int blue;
235
236 public int getRed() {
237 return red;
238 }
239
240 public void setRed(int red) {
241 this.red = red;
242 }
243
244 public int getGreen() {
245 return green;
246 }
247
248 public void setGreen(int green) {
249 this.green = green;
250 }
251
252 public int getBlue() {
253 return blue;
254 }
255
256 public void setBlue(int blue) {
257 this.blue = blue;
258 }
259
260 public int getX() {
261 return X;
262 }
263
264 public void setX(int x) {
265 X = x;
266 }
267
268 public int getY() {
269 return Y;
270 }
271
272 public void setY(int y) {
273 Y = y;
274 }
275
276 }
由于楼主目前没有安卓设备,还无法带上adb实验,目前只是通过手动截取图片,和测量距离计算了3张,总体比较下来,还算是正确的。
第一张图片
计算结果如图:
第二张图片
计算结果如图:
第三张图片
计算结果如图:
后续再搞个安卓设备试试~
give me the ball!