package com.example.jmacro.wjdr; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.ReUtil; import com.example.jmacro.wjdr.base.ScreenPoint; import com.example.jmacro.wjdr.base.ScreenRect; import com.example.jmacro.wjdr.util.ColorUtil; import com.example.jmacro.wjdr.util.Imager; import com.example.jmacro.wjdr.util.Logger; import com.example.jmacro.wjdr.util.TaskUtil; import java.awt.*; import java.awt.event.InputEvent; import java.awt.image.BufferedImage; import java.io.File; import java.util.concurrent.TimeUnit; /** * Java脚本精灵 * * @author wangbing * @version 0.0.1 * @since 1.8 */ public class JMacro { /** * 机器人操作实例 */ private final Robot robot; /** * 图例目录 */ private File legend; public JMacro() throws AWTException { // 机器人初始化 this.robot = new Robot(); this.robot.setAutoDelay(100); } public JMacro(File legend) throws AWTException { this(); this.legend = legend; } /** * 键盘按键组 */ public void keyInput(int... keycodes) { for (int keycode : keycodes) { keyPress(keycode); } } /** * 键盘按键 * keycode Key to press (e.g. KeyEvent.VK_A) */ public void keyPress(int keycode) { this.robot.keyPress(keycode); } /** * 鼠标移动 * * @param point 坐标点 */ public void mouseMove(ScreenPoint point) { mouseMove(point, false); } /** * 鼠标移动 * * @param point 坐标点 * @param smooth 平滑移动 */ public void mouseMove(ScreenPoint point, boolean smooth) { if (smooth) { // 获取当前鼠标位置 Point mousePoint = MouseInfo.getPointerInfo().getLocation(); int startX = mousePoint.x; int startY = mousePoint.y; // 求两点距离 double absX = Math.abs(startX - point.getX()); double absY = Math.abs(startY - point.getY()); double absZ = Math.sqrt(Math.pow(absX, 2) + Math.pow(absY, 2)); int times = (int) (absZ / 30 + (absZ % 30 > 0 ? 1 : 0)); int interval = Math.min(500 / times, 10); times = Math.min(times, 10); // 分times次移动到指定点 for (int i = 1; i <= times; i++) { float d = i * 1.0f / times; int dx = (int) (startX + (point.getX() - startX) * d); int dy = (int) (startY + (point.getY() - startY) * d); robot.mouseMove(dx, dy); robot.delay(RandomUtil.randomInt(interval - 10, interval + 10)); } } else { robot.mouseMove(point.getX(), point.getY()); } } /** * 鼠标左键单击 * * @param rect 点 */ public void mouseLeftClick(ScreenPoint rect) { mouseMove(rect); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); delayTap(); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } /** * 鼠标左键单击 * * @param rect 矩形区域 */ public void mouseLeftClick(ScreenRect rect) { mouseLeftClick(new ScreenPoint(rect.getCenter()[0], rect.getCenter()[1])); } /** * 鼠标左键拖拽 * * @param start 开始位置 * @param end 结束位置 */ public void mouseLeftDrag(ScreenPoint start, ScreenPoint end) { mouseLeftDrag(start, end, false); } /** * 鼠标左键拖拽 * * @param start 开始位置 * @param end 结束位置 * @param smooth 平滑移动 */ public void mouseLeftDrag(ScreenPoint start, ScreenPoint end, boolean smooth) { mouseMove(start, smooth); delayTap(); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); delayTap(); mouseMove(end, smooth); delayTap(); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } /** * 鼠标右键拖拽 * * @param start 开始位置 * @param end 结束位置 */ public void mouseRightDrag(ScreenPoint start, ScreenPoint end) { mouseRightDrag(start, end, false); } /** * 鼠标右键拖拽 * * @param start 开始位置 * @param end 结束位置 * @param smooth 平滑移动 */ public void mouseRightDrag(ScreenPoint start, ScreenPoint end, boolean smooth) { mouseMove(start, smooth); delayTap(); robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); delayTap(); mouseMove(end, smooth); delayTap(); robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); } /** * 鼠标滚轮单击 * * @param rect 矩形区域 */ public void mouseWheelClick(ScreenRect rect) { delayTap(); robot.mouseMove(rect.getCenter()[0], rect.getCenter()[1]); delayTap(); robot.mousePress(InputEvent.BUTTON2_DOWN_MASK); delayTap(); robot.mouseRelease(InputEvent.BUTTON2_DOWN_MASK); } /** * 鼠标右键单击 * * @param rect 矩形区域 */ public void mouseRightClick(ScreenRect rect) { delayTap(); robot.mouseMove(rect.getCenter()[0], rect.getCenter()[1]); delayTap(); robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); delayTap(); robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); } /** * 鼠标左键双击 * * @param rect 矩形区域 */ public void mouseLeftDoubleClick(ScreenRect rect) { delayTap(); robot.mouseMove(rect.getCenter()[0], rect.getCenter()[1]); delayTap(); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.delay(100); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); robot.delay(100); robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); robot.delay(100); robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); } /** * 捕获指定区域屏幕 */ public BufferedImage capture(Robot robot, ScreenRect screenRect) { return robot.createScreenCapture(new Rectangle(screenRect.getLeft(), screenRect.getTop(), screenRect.getWidth(), screenRect.getHeight())); } /** * 获取屏幕范围 */ public ScreenRect getScreenRect() { Toolkit tk = Toolkit.getDefaultToolkit(); return new ScreenRect(0, 0, tk.getScreenSize().width, tk.getScreenSize().height); } /** * 全屏查找图片 * * @param legendName 图例名称 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect findPic(String legendName, double minSimilar) { if (!legendName.endsWith(".png")) { legendName = legendName + ".png"; } return findPic(new File(legend, legendName), minSimilar); } /** * 全屏查找图片 * * @param pic 图片 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect findPic(File pic, double minSimilar) { return findPic(getScreenRect(), Imager.load(pic), minSimilar); } /** * 全屏查找图片 * * @param pic 图片 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect findPic(BufferedImage pic, double minSimilar) { return findPic(getScreenRect(), pic, minSimilar); } /** * 应用视口查找图例 * * @param screenRect 应用视口 * @param legendName 图例名称 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect findPic(ScreenRect screenRect, String legendName, double minSimilar) { if (!legendName.endsWith(".png")) { legendName = legendName + ".png"; } return findPic(screenRect, new File(legend, legendName), minSimilar); } /** * 应用视口查找图例 * * @param screenRect 应用视口 * @param legend 图例文件 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect findPic(ScreenRect screenRect, File legend, double minSimilar) { return findPic(screenRect, Imager.load(legend), minSimilar); } /** * 获取定位 * * @param pic 参考图 * @param screenRect 查找范围 * @param minSimilar 相似度 * @return 匹配图片区域 */ public ScreenRect findPic(ScreenRect screenRect, BufferedImage pic, double minSimilar) { // 当查找区域比图片还小时,直接返回失败 if (screenRect.getWidth() < pic.getWidth() || screenRect.getHeight() < pic.getHeight()) { Logger.error("视口尺寸小于图片"); return null; } // 获取实时屏幕 BufferedImage screen = capture(robot, screenRect); int[][] screenData = Imager.getImageRGB(screen); int[][] picData = Imager.getImageRGB(pic); // 得到图片左上角范围 int xMin = screenRect.getLeft(); // 因为坐标取像素点是从0开始,因此需要-1 int xMax = screenRect.getRight() - pic.getWidth(); int yMin = screenRect.getTop(); // 因为坐标取像素点是从0开始,因此需要-1 int yMax = screenRect.getBottom() - pic.getHeight(); for (int y = yMin; y <= yMax; y++) { for (int x = xMin; x <= xMax; x++) { // 对关键点进行先期匹配,降低运算复杂度。如果关键点本身就不匹配,就没必要再去匹配小图的每一个像素点 // 左上角 boolean lt = ColorUtil.isSimilar(screenData[x - xMin][y - yMin], picData[0][0]); // 右上角 boolean rt = ColorUtil.isSimilar(screenData[x - xMin + pic.getWidth() - 1][y - yMin], picData[pic.getWidth() - 1][0]); // 左下角 boolean lb = ColorUtil.isSimilar(screenData[x - xMin][y - yMin + pic.getHeight() - 1], picData[0][pic.getHeight() - 1]); // 右下角 boolean rb = ColorUtil.isSimilar(screenData[x - xMin + pic.getWidth() - 1][y - yMin + pic.getHeight() - 1], picData[pic.getWidth() - 1][pic.getHeight() - 1]); //中心点 boolean cc = ColorUtil.isSimilar(screenData[x - xMin + pic.getWidth() / 2][y - yMin + pic.getHeight() / 2], picData[pic.getWidth() / 2][pic.getHeight() / 2]); if (lt && rt && lb && rb && cc) { // 进行全像素匹配 double similar = Imager.calcSimilar(x - xMin, y - yMin, pic.getHeight(), pic.getWidth(), screenData, picData); if (similar >= minSimilar) { return new ScreenRect(x, y, x + pic.getWidth(), y + pic.getHeight(), similar); } } } } // try { // ImgUtil.write(screen,"png", new FileOutputStream("D://1.png")); // } catch (FileNotFoundException e) { // // } return null; } public void delay() { delayNormal(); } public void delayTap() { int i = RandomUtil.randomInt(100, 500); robot.delay(i); } public void delayNormal() { int i = RandomUtil.randomInt(500, 1500); robot.delay(i); } public void delayLong() { int i = RandomUtil.randomInt(1500, 3000); robot.delay(i); } /** * 等待并查找图片 * * @param file 图例 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect waitAndFindPic(File file, double minSimilar) { return waitAndFindPic(getScreenRect(), file, minSimilar, 10, TimeUnit.SECONDS); } /** * 等待并查找图片 * * @param rect 查找区域 * @param file 图例 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect waitAndFindPic(ScreenRect rect, File file, double minSimilar) { return waitAndFindPic(rect, file, minSimilar, 10, TimeUnit.SECONDS); } /** * 等待并查找图片 * * @param rect 查找区域 * @param legendName 图例名称 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect waitAndFindPic(ScreenRect rect, String legendName, double minSimilar) { if (!legendName.endsWith(".png")) { legendName = legendName + ".png"; } return waitAndFindPic(rect, new File(legend, legendName), minSimilar, 10, TimeUnit.SECONDS); } /** * 等待并查找图片 * * @param file 图例 * @param minSimilar 最低相似度 * @param time 最长等待时间 * @param unit 最长等待时间单位 * @return 匹配图片区域 */ public ScreenRect waitAndFindPic(File file, double minSimilar, long time, TimeUnit unit) { return waitAndFindPic(getScreenRect(), file, minSimilar, time, unit); } /** * 等待并查找图片 * * @param rect 查找区域 * @param file 图例 * @param minSimilar 最低相似度 * @param time 最长等待时间 * @param unit 最长等待时间单位 * @return 匹配图片区域 */ public ScreenRect waitAndFindPic(ScreenRect rect, File file, double minSimilar, long time, TimeUnit unit) { return TaskUtil.timeTask(() -> { BufferedImage image = Imager.load(file); if (rect.getWidth() < image.getWidth()) { Logger.error("查找图片区域宽度{}小于图片宽度{}", rect.getWidth(), image.getWidth()); return null; } if (rect.getHeight() < image.getHeight()) { Logger.error("查找图片区域宽度{}小于图片宽度{}", rect.getHeight(), image.getHeight()); return null; } while (true) { delayTap(); ScreenRect pic = findPic(rect, image, minSimilar); if (pic != null) { return pic; } } }, time, unit); } /** * 等待并匹配图例 * * @param rect 参照区域 * @param legendName 图例名称 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect waitAndMatchPic(ScreenRect rect, String legendName, double minSimilar) { if (!legendName.endsWith(".png")) { legendName = legendName + ".png"; } return waitAndMatchPic(rect, new File(legend, legendName), minSimilar, 10, TimeUnit.SECONDS); } /** * 等待并匹配图例 * * @param rect 参照区域 * @param file 图例 * @param minSimilar 最低相似度 * @return 匹配图片区域 */ public ScreenRect waitAndMatchPic(ScreenRect rect, File file, double minSimilar) { return waitAndMatchPic(rect, file, minSimilar, 10, TimeUnit.SECONDS); } /** * 等待并匹配图例 * * @param rect 参照区域 * @param file 图例 * @param minSimilar 最低相似度 * @param time 最长等待时间 * @param unit 最长等待时间单位 * @return 匹配图片区域 */ public ScreenRect waitAndMatchPic(ScreenRect rect, File file, double minSimilar, long time, TimeUnit unit) { return TaskUtil.timeTask(() -> { while (true) { delayTap(); ScreenRect matchPic = matchPic(rect, file, minSimilar); if (matchPic != null) { return matchPic; } } }, time, unit); } /** * 匹配图片 * * @param rect 程序窗口(参照系) * @param file 图例 * @param minSimilar 最低相似度 * @return 匹配图片 */ public ScreenRect matchPic(ScreenRect rect, File file, double minSimilar) { if (!file.exists()) { Logger.error("file [{}] not exist", file.getAbsolutePath()); return null; } String name = file.getName(); int offsetX = 0; int offsetY = 0; if (name.matches("L[0-9]+,[0-9]+-[\\S\\s]+")) { offsetX = Convert.toInt(ReUtil.get("L([0-9]+),[0-9]+-[\\S\\s]+", name, 1), 0); offsetY = Convert.toInt(ReUtil.get("L[0-9]+,([0-9]+)-[\\S\\s]+", name, 1), 0); } ScreenRect screenRect = new ScreenRect(); screenRect.setLeft(rect.getLeft() + offsetX); screenRect.setTop(rect.getTop() + offsetY); BufferedImage image = Imager.load(file); screenRect.setRight(screenRect.getLeft() + image.getWidth()); screenRect.setBottom(screenRect.getTop() + image.getHeight()); return findPic(screenRect, image, minSimilar); } /** * 匹配图片 * * @param rect 程序窗口(参照系) * @param legendName 图例名称 * @param minSimilar 最低相似度 * @return 匹配图片 */ public ScreenRect matchPic(ScreenRect rect, String legendName, double minSimilar) { if (!legendName.endsWith(".png")) { legendName = legendName + ".png"; } return matchPic(rect, new File(legend, legendName), minSimilar); } /** * 是否匹配图片 * * @param rect 程序窗口(参照系) * @param file 图例 * @param minSimilar 最低相似度 * @return 匹配图片 */ public boolean isMatchPic(ScreenRect rect, File file, double minSimilar) { return matchPic(rect, file, minSimilar) != null; } /** * 是否匹配图片 * * @param rect 程序窗口(参照系) * @param legendName 图例名称 * @param minSimilar 最低相似度 * @return 匹配图片 */ public boolean isMatchPic(ScreenRect rect, String legendName, double minSimilar) { if (!legendName.endsWith(".png")) { legendName = legendName + ".png"; } return matchPic(rect, new File(legend, legendName), minSimilar) != null; } }