Java、游戏手柄与我

你是否曾想过能用已有的材料来创造出一些新奇东西呢?我看过一些关于“Steam Controller”(下文简称SC)的视频,再看看我的游戏pad。问自己是否能像SC一样来使用我的pad,我找到了一些Java库并创建了一个工程,这就是我今天要和大家分享的。

 

当然,在SC问世前就已经存在很多输入设备了(尤其是游戏控制器),但是SC拥有的一个与众不同的新功能。它有两个触摸板,这让它能模拟鼠标或者键盘的输入,这样就可以操作(近乎)每个游戏。正如一些早期的视频所展现,甚至是鼠标操作很频繁的游戏,像解谜游戏“Portal”(中文名为《传送门》),使用SC的兼容模式的话也是可以操控的。

作为一名游戏狂和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++代码而言)。

原文链接: javacodegeeks 翻译: ImportNew.com - Talisman_Lin
译文链接: http://www.importnew.com/9722.html
[ 转载请保留原文出处、译者和译文链接。]



可能感兴趣的文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部