我们中的许多人都喜欢世纪之交的老式视频游戏。他们拥有出色的氛围,疯狂的动力和许多原始的解决方案,这些解决方案在几十年后都没有过时。但是,如今,游戏界面的愿景已发生了一些变化-线性走廊已取代了令人困惑的关卡,再生已取代了急救包,而不是用长行的0-9键来选择兵工厂,首先是鼠标滚轮,然后是虚拟滚轮。今天我们要谈的就是他。
历史总结
以前,在这样的射击游戏类型出现期间,并未提出鼠标控制的问题-仅使用键盘来控制主角。此外,也没有单一的管理格式-WASD稍后成为标准。您可以在此处阅读有关旧游戏键盘布局的更多信息。
因此,在那些实现了设备选择能力的游戏(《毁灭战士》,《德军总部》,《雷神之锤》等)中,当时是唯一直观的方法,即使用键盘上的数字键来实现。多年来,这种方法是唯一的一种。
然后,在90年代后期,使用鼠标滚轮更换武器成为可能。
我们在该主题上找不到明确的信息,但是在CS 1.6中,已通过控制台启用了此功能。但是,以前可能有这样的先例-在这种情况下,请在评论或PM中指出。但是以我们时代熟悉的形式,“武器轮”仅在《孤岛危机》及其“西服”菜单中使用。尽管尝试类似的尝试始于HL2,但“轮”直到00年代后期才进入大众市场,如今已成为主流。
但是,这仅是历史总结,仅作为历史才有意义。在本文的框架中,将不对特定解决方案流行的原因进行冗长的讨论。并了解哪种选择器更好。仅仅因为以下内容将描述使用鼠标将旧版《毁灭战士》改编为武器的过程。
设定目标
为了实现WW,您需要以某种方式拦截鼠标的移动,在按住选择器键的同时跟踪其移动,并在释放鼠标时模拟与所选扇区对应的按钮的单击。
为此,我使用了Java语言,特别是,使用jnativehook库执行了密钥拦截,并使用awt.Robot进行了按。处理接收到的钩子并不困难,因此,它是手动完成的。
实作
以前,开发了定义坐标对以确定位移矢量的类。
特别是,Shift类允许您存储二维向量以及确定其长度,而NormalizedShift类旨在存储归一化向量,除其他事项外,它还允许您确定截取的向量与向量之间的角度(1,0)。
扰流板方向
class Shift{
int xShift;
int yShift;
public int getxShift() {
return xShift;
}
public int getyShift() {
return yShift;
}
public void setxShift(int xShift) {
this.xShift = xShift;
}
public void setyShift(int yShift) {
this.yShift = yShift;
}
double getLenght(){
return Math.sqrt(xShift*xShift+yShift*yShift);
}
}
class NormalisedShift{
double normalizedXShift;
double normalizedYShift;
double angle;
NormalisedShift (Shift shift){
if (shift.getLenght()>0)
{
normalizedXShift = -shift.getxShift()/shift.getLenght();
normalizedYShift = -shift.getyShift()/shift.getLenght();
}
else
{
normalizedXShift = 0;
normalizedYShift = 0;
}
}
void calcAngle(){
angle = Math.acos(normalizedXShift);
}
double getAngle(){
calcAngle();
return (normalizedYShift<0?angle*360/2/Math.PI:360-angle*360/2/Math.PI);
};
};
它们没有特别的意义,只有将向量标准化的第73-74行需要注释。其中,向量被翻转。参考框架发生了变化-事实是,从软件的角度和从熟悉的数学的角度来看,向量在传统上的指向是不同的。这就是为什么Shift类的向量的原点位于左上方,而NormalizedShift类的原点位于左下方。
为了实现该程序,实施了Wheel类,该类实现了NativeMouseMotionListener和NativeKeyListener接口。该代码在扰流器下。
扰流板方向
public class Wheel implements NativeMouseMotionListener, NativeKeyListener {
final int KEYCODE = 15;
Shift prev = new Shift();
Shift current = new Shift();
ButtomMatcher mathcer = new ButtomMatcher();
boolean wasPressed = false;
@Override
public void nativeMouseMoved(NativeMouseEvent nativeMouseEvent) {
current.setxShift(nativeMouseEvent.getX());
current.setyShift(nativeMouseEvent.getY());
}
@Override
public void nativeMouseDragged(NativeMouseEvent nativeMouseEvent) {
}
@Override
public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {
}
@Override
public void nativeKeyPressed(NativeKeyEvent nativeKeyEvent) {
if (nativeKeyEvent.getKeyCode()==KEYCODE){
if (!wasPressed)
{
prev.setxShift(current.getxShift());
prev.setyShift(current.getyShift());
}
wasPressed = true;
}
}
@Override
public void nativeKeyReleased(NativeKeyEvent nativeKeyEvent) {
if (nativeKeyEvent.getKeyCode() == KEYCODE){
Shift shift = new Shift();
shift.setxShift(prev.getxShift() - current.getxShift());
shift.setyShift(prev.getyShift() - current.getyShift());
NormalisedShift normalisedShift = new NormalisedShift(shift);
mathcer.pressKey(mathcer.getCodeByAngle(normalisedShift.getAngle()));
wasPressed = false;
}
}
让我们弄清楚这里发生了什么。
KEYCODE变量存储用于调用选择器的键的代码。通常这是TAB,但如有必要,可以在代码中更改它,或者-理想情况下-从配置文件中提取它。
prev存储调用选择器时鼠标光标的位置。 Surrent在给定时间保持当前光标位置。因此,当释放选择器键时,在选择器键被按下的时间内将向量相减,并将光标的移位写入移位变量。
然后,在第140行,将向量归一化,即 当其长度接近统一时简化为形式。之后,将归一化的向量传输到匹配器,该匹配器在要按下的键的代码和向量的旋转角度之间建立对应关系。出于可读性原因,角度将转换为度,以及-沿完整的单位圆定向(acos仅适用于最大180度的角度)。
ButtonMatcher类定义角度和所选键控代码之间的对应关系。
扰流板方向
class ButtomMatcher{
Robot robot;
final int numberOfButtons = 6;
int buttonSection = 360/numberOfButtons;
int baseShift = 90-buttonSection/2;
ArrayList<Integer> codes = new ArrayList<>();
void matchButtons(){
for (int i =49; i<55; i++)
codes.add(i);
}
int getCodeByAngle(double angle){
angle= (angle+360-baseShift)%360;
int section = (int) angle/buttonSection;
System.out.println(codes.get(section));
return codes.get(section);
}
ButtomMatcher() {
matchButtons();
try
{
robot = new Robot();
}
catch (AWTException e) {
e.printStackTrace();
}
}
void pressKey(int keyPress)
{
robot.keyPress(keyPress);
robot.keyRelease(keyPress);
}
}
此外,变量numberOfButtons决定扇区及其相应按钮的数量,baseShift设置旋转角度(特别是,它提供了围绕垂直轴的对称性,并将轮子旋转90度,以便近战武器位于顶部),并且代码数组存储代码按键-如果更改了按键并且代码不会连续显示。在更详细的版本中,可以将它们从配置文件中拉出,但是使用标准的键布局-当前版本是相当可行的。
结论
在本文的框架内,描述了为现代标准定制经典射手界面的可能性。当然,我们在这里不添加任何急救箱或线性工具-为此,有很多mod,但是在如此详细的情况下,往往会提供友好而方便的界面。作者意识到他可能没有描述获得期望结果的最佳方法,并且还在评论中等待带有面包和无轨电车的图片,但是尽管如此,这还是一个有趣的经历,也许会鼓励一些游戏玩家发现Java的奇妙世界。
欢迎建设性的批评。
源代码