Java开发游戏脚本(第六卷)

游戏脚本开发第六卷

前言介绍

本卷简要介绍脚本2.0,只说重要部分代码。

相关技术

  1. 后台截图,该功能搬运而来,来源:点击跳转
  2. 后台操作进程窗口,用来实现后台操作,游戏可以挂着,电脑也可以做其他事情。

相关功能

后台截图

    /**
	 * 本方法会根据游戏界面句柄扫描进程窗口,窗口尺寸并且返回窗口截图(进程不可以最小化)
	 * 
	 * @param hwnd        - 游戏界面句柄
	 * @param game_width  - 窗口宽度
	 * @param game_height - 窗口高度
	 * 
	 * @return - 返回窗口截图
	 */
	public static BufferedImage scanningProcess(HWND hwnd, int game_width, int game_height) {

		// 检索游戏窗口区域的显示设备上下文环境的句柄,以后在GDI函数中使用该句柄来在设备上下文环境中绘图
		HDC gameDC = GDI32.INSTANCE.GetDC(hwnd);
		// 创建与指定的设备环境相关的设备兼容的位图
		HBITMAP outputBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(gameDC, game_width, game_height);
		try {
			// 创建一个与指定设备兼容的内存设备上下文环境
			HDC blitDC = GDI32.INSTANCE.CreateCompatibleDC(gameDC);
			try {
				// 选择一对象到指定的设备上下文环境中
				HANDLE oldBitmap = GDI32.INSTANCE.SelectObject(blitDC, outputBitmap);
				try {
					// 对指定的源设备环境区域中的像素进行位块(bit_block)转换
					GDI32.INSTANCE.BitBlt(blitDC, 0, 0, game_width, game_height, gameDC, 0, 0, GDI32.SRCCOPY);
				} finally {
					GDI32.INSTANCE.SelectObject(blitDC, oldBitmap);
				}
				// 位图信息头,大小固定40字节
				BITMAPINFO bi = new BITMAPINFO(40);
				bi.bmiHeader.biSize = 40;
				// 函数获取指定兼容位图的位,然后将其作一个DIB—设备无关位图使用的指定格式复制到一个缓冲区中
				boolean ok = GDI32.INSTANCE.GetDIBits(blitDC, outputBitmap, 0, game_height, (byte[]) null, bi,
						WinGDI.DIB_RGB_COLORS);
				if (ok) {
					BITMAPINFOHEADER bih = bi.bmiHeader;
					bih.biHeight = -Math.abs(bih.biHeight);
					bi.bmiHeader.biCompression = 0;
					BufferedImage img = bufferedImageFromBitmap(blitDC, outputBitmap, bi);
					return img;
				}
			} finally {
				GDI32.INSTANCE.DeleteObject(blitDC);
			}
		} finally {
			GDI32.INSTANCE.DeleteObject(outputBitmap);
		}

		return null;

	}
	// 依赖方法scanningProcess
	private static BufferedImage bufferedImageFromBitmap(HDC blitDC, HBITMAP outputBitmap, BITMAPINFO bi) {
		BITMAPINFOHEADER bih = bi.bmiHeader;
		int height = Math.abs(bih.biHeight);
		final ColorModel cm;
		final DataBuffer buffer;
		final WritableRaster raster;
		int strideBits = (bih.biWidth * bih.biBitCount);
		int strideBytesAligned = (((strideBits - 1) | 0x1F) + 1) >> 3;
		final int strideElementsAligned;
		switch (bih.biBitCount) {
		case 16:
			strideElementsAligned = strideBytesAligned / 2;
			cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F);
			buffer = new DataBufferUShort(strideElementsAligned * height);
			raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned,
					((DirectColorModel) cm).getMasks(), null);
			break;
		case 32:
			strideElementsAligned = strideBytesAligned / 4;
			cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
			buffer = new DataBufferInt(strideElementsAligned * height);
			raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned,
					((DirectColorModel) cm).getMasks(), null);
			break;
		default:
			throw new IllegalArgumentException("检测到不支持的图片位数: " + bih.biBitCount);
		}
		final boolean ok;
		switch (buffer.getDataType()) {
		case DataBuffer.TYPE_INT: {
			int[] pixels = ((DataBufferInt) buffer).getData();
			ok = GDI32.INSTANCE.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
		}
			break;
		case DataBuffer.TYPE_USHORT: {
			short[] pixels = ((DataBufferUShort) buffer).getData();
			ok = GDI32.INSTANCE.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
		}
			break;
		default:
			throw new AssertionError("检测到不支持的缓冲元素类型: " + buffer.getDataType());
		}
		if (ok) {
			return new BufferedImage(cm, raster, false, null);
		} else {
			return null;
		}
	}

该功能搬运而来,来源:点击跳转

注意:此后台截图不可以截取最小化的进程窗口,截取返回的是黑屏的图片,电脑的缩放尺寸需要调整为100%,返回的图片才是正常的。选择的进程不可以是网页,特殊软件(经测试后,除了QQ不可以,其他的软件基本都可以获取图片)

后台操作进程窗口

    /**
	 * 本方法可以向后台进程窗口发送鼠标事件从而实现后台操作游戏
	 * 
	 * @param gameProcess   - 指定后台进程窗口
	 * @param mouseMessages - 鼠标事件描述
	 */
	public static void MouseClick(HWND hwnd, java.util.List<Work> mouseMessages) {

		for (int i = 0; i < mouseMessages.size(); i++) {
			// 解析鼠标坐标参数,低位为X轴,高位为Y轴坐标
			String X = Integer.toHexString(mouseMessages.get(i).getMouseX());
			String Y = Integer.toHexString(mouseMessages.get(i).getMouseY());
			while (X.length() < 4) {
				X = "0" + X;
			}
			while (Y.length() < 4) {
				Y = "0" + Y;
			}
			Integer in = Integer.valueOf(Y + X, 16);
			LPARAM lPARAM = new LPARAM(in);
			int moveTime = (int) (Math.random() * 400 + 300);
			int mousePressTime = (int) (Math.random() * 500 + 400);
			try {
				// 模拟计算鼠标按下的间隔并且按下鼠标
				Thread.sleep(moveTime);
				User32.INSTANCE.PostMessage(hwnd, 513, new WPARAM(513), lPARAM);
				Thread.sleep(mousePressTime);
				User32.INSTANCE.PostMessage(hwnd, 514, new WPARAM(514), lPARAM);

			} catch (InterruptedException e) {

				e.printStackTrace();

			}

		}

	}

解析:
User32.INSTANCE.PostMessage()
hwnd:需要进行鼠标操作的窗口的句柄。
513,514:在Window中,就是按下与释放的指令。
WPARAM:封装指令。
lPARAM:封装鼠标按下与释放的位置坐标。

lPARAM封装的是16进制的数字,十进制表示为8位(YYYY XXXX),
当XXXX = 0100,YYYY = 0200 时,鼠标点击的是窗口(100,200)这一点,
因需要转化为16进制,则使用Integer.valueOf()转化。
Integer in = Integer.valueOf(“02000100”, 16);

该函数作用不止于此,随着指令的不同,lPARAM此参数封装的数据也不同,以上的参数解析只适用于鼠标按下与释放,不可在其他指令套用该解析,同时,该解析纯个人领悟,可能存在错误的理解,请见谅!

后台操作思路

思路与脚本1.0的前台图像识别类似,先在图片库读取图片并且转化为数据,然后后台扫描进程窗口,得到窗口图片并且转化为数据,该数据通过对比图片库数据,如果两个数据有重合的区域,则说明此时的进程窗口存在与图片库的某张图片相同的某个图片区域,最后获取该图片区域在进程窗口的坐标,调用后台操作进程窗口的方法,实现后台点击进程窗口。

最后结语

脚本2.0介绍完毕,该项目不分享,有心人可根据六卷的思路,自己动手,超越自我。
博主没有脚本3.0,一直致力于完善脚本1.0(因脚本2.0限制多,效率低)。
最后暗示–>>关注点赞评论!!!

猜你喜欢

转载自blog.csdn.net/weixin_44005360/article/details/107093976