Jade Dungeon

Java USB控制

Java游戏手柄操作

一个叫做StrangeCtrl的小工具给世界带来曙光。与控制器通信需要一些JNI(因为在Java 虚拟机里没有USB子系统)。但是别的都是用纯Java语言编写的。它位于系统托盘下面, 尽管(同时)可以创建GUI,通过手动配置每个配置文件。

它需要2.0.5版本的net.java.jinput.JInput(win8.1上依旧可以运行)和我写的一个 小助手(com.xafero.SuperLoader,0.1的版本).下面我就阐释一下这几个步骤。

第一步:我们怎样用Java来与控制器通信

幸运的是,BSD许可的JInut项目已经差不多完成了。它连接到微软的XInput接口,并填入 一些Java数据类型就行了。别担心,Linux和Mac操作系统也是可以的。我打开了我的游戏 pad(兼容XBox的游戏控制器)

  1. 获取控制器
  2. 获取他们的输入时间
  3. 把他们转换成鼠标和键盘的虚拟事件

JAR提供了这个库的原生组件给三大操作系统。但是你应该知道,java.lang.System只能 直接的加载文件系统可用的文件。

第二部:我们怎么避开局限性?

快速的搜索后,我发现了wcmatthysen的mix-native-loader,它看起来很有用,因为 声称它提取了JAR并加载了原生方法。但是事实上并不起作用,因为Jinput的库被封住成 几个jinput-platform-***.jar文件,而不是像作者所想的在META-INF/lib下一个大的 组件一样。

所以在这样一些情况下一个叫做SuperLoader的帮助库起到了作用。

  1. 给所有那些恶心的本地库创建一个临时目录,例如在系统属性java.io.tmpdir的 帮助下。它也可以由用户直接指定,因为它在哪儿其实无关紧要。
  2. 把所有恶心的库从下载好的JAR中取出,迭代掉所有类路径中的URL并且提取出他们, 或者用一个filter排除他们。
  3. 扩展现有的库路径;真是很恼人要去手动的做一件其他库没有做的事情,所以应该扩展 系统属性java.library.path
  4. 强制Java虚拟机去更新这些系统路径;可以把系统类装如期的的sys_paths字段置空 来实现。这将会在下次你需要一个库的时候强制系统类重视这样一个新的环境。

现在的应用程序都会预先下载所有本地库到一个临时文件夹,这样当JInput被要求列出 一系列的,例如游戏设备,它并不需要改变使用JAR文件这策略,它就像别人一样用 System.loarLibrary就行了。

第三步:可以模拟些什么?

好,我们终于可以读到这个游戏pad的事件了,那么我们能对它做什么呢?通过AWT的 Robot类我们可知,在早期的java就可以模拟一个按键或者鼠标移动之类的。尽管机器人 需要人类指定他工作的机器,但是它在多荧幕的系统中表现出色。唯一的区别就是它 产生的事件的偏移,如果人们想去点击电脑屏幕上特殊的区域的话这将会是很重要的一个 问题。到目前为止的执行指令如下:

  • 水平的或者垂直的移动鼠标
  • 在当前屏幕位置点击已知鼠标
  • 按一些按键并逆序释放他们

为了允许可扩展性,下面这个接口的参数是机器人产生的虚拟时间,当前的图形处理设备 和JInput给出的数值。

public interface ICommand {
    void execute(Robot rbt, GraphicsDevice dev, float value);
}

它的抽象实现AbstractCmd提供了参数为一个字符串的构造函数。作为处理的第一步, 从配置文件得到的原始字符串在一块空内存上被分解为字符串数组。

第四步:我们可以用哪种配置格式?

现在已经有很多流行的格式,例如YAML,JSON等等,但是Java早就提供给我们一种简单的 方式。

  • 加载配置
  • 遍历所有条目
  • 搜索命令每个条目的值
  • 通过实例化文本参数加载每个命令
  • 把关键的结果(控制器按钮)和价值(相关命令)放到一个新的映射,
  • 和生产实际的映射用于转换传入的事件。

第五步:跑起来

助手类ControllerPoller是一个定期执行TimerTask,而TimerTask是负责收集从 任意数量的控制器中传出的JInput事件并且把每个新事件都通知调用者。

	public void run() {
		for (Controller controller : controllers) {
			if (!controller.poll()) continue;
			EventQueue queue = controller.getEventQueue();
			Event event = new Event();
			while (queue.getNextEvent(event))
				callback.onNewEvent(this, controller, event);
		}
	}

调用者(在本案例中也就是所谓的在系统托盘中的App)仅仅实现了回调接口,在发生输入 事件的时候他可以轻易的获得所有信息。

public static interface IControllerCallback {
	void onNewEvent(ControllerPoller p, Controller c, Event e);
}

「App」的左边是对注入游戏pad事件的指令以及正确执行他们所需的参数的一些探究。现在 我们能用它来操控一些游戏,也许像一些老款的不能用游戏pa的来操控的游戏,例如: Prince of Persia。

游戏之外的案例:怎样为那些有约束运动的人们配置它?

为了展示应用程序的另一个可能的领域,让我们来给那些无法同时按下两个键的人进行 配置。这儿的一个实例应当是web浏览了。在配置文件里,设置如下:

<!-- Button A means now left mouse click -->
<entry key="Button 0">mouseClick 1</entry>
<!-- Button B will open a new tab -->
<entry key="Button 1">keyCombo CONTROL T</entry>
<!-- Button X will close an existing tab -->
<entry key="Button 2">keyCombo CONTROL W</entry>

本案例中的浏览器并不一定要知道游戏机的存在,因为操作系统会产生虚拟的输入事件, 由它按照要求进行操作。通过运用Java语言,并成为了FOSS(Free And Open Source Software开源软件),这个工具是可定制的,并且无论在哪方面都是易于理解的(相对于 一些模拟输入设备时所需要的C/C++代码而言)。