我有一个你可以攻击的游戏。当按下SPACE键时,武器会向下移动,当它被释放时它会向上移动。
问题在于(在某些)UNIX系统上,KeyReleased事件也会在按住键的同时触发。我正在使用BitSet跟踪按下哪些键,但由于KeyReleased持续发射,武器只能在我的Ubuntu机器上快速上下移动。
我可以同时移动我的两个角色,但是在按下空格键时我无法保持武器。
(按住键几秒后会发生什么事情: keyPressed - keyReleased - keyPressed - keyReleased - keyPressed - keyReleased - ....)
这是一个System.out,会发生什么,在每个动作之前使用System.currentTimeMillis():
1368696607559 - Pressed
1368696608052 - Released
1368696608053 - Pressed
1368696608082 - Released
1368696608083 - Pressed
1368696608112 - Released
1368696608113 - Pressed
1368696608143 - Released
1368696608144 - Pressed
1368696608173 - Released
我正在使用的代码是:
//Inner Class
private class KeyDispatcher extends KeyAdapter implements ActionListener{
private BitSet keyBits = new BitSet(256);
private Timer timer = new Timer(20, this);
boolean player1Attack = false, player2Attack = false;
public KeyDispatcher(){
timer.start();
}
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
keyBits.set(keyCode);
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
keyBits.clear(keyCode);
if(player1Attack && !isKeyPressed(KeyEvent.VK_SPACE)){
controller.stopAttack(0);
player1Attack = false;
}
//player 2
...
}
public boolean isKeyPressed(final int keyCode) {
return keyBits.get(keyCode);
}
@Override
public void actionPerformed(ActionEvent e) {
if(isGameOver()){
timer.stop();
}else{
//player1
if(!player1Attack && isKeyPressed(KeyEvent.VK_RIGHT)){
controller.moveRight(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_UP)){
controller.moveUp(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_DOWN)){
controller.moveDown(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_LEFT)){
controller.moveLeft(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_NUMPAD0)){
controller.attack(0); player1Attack = true; update();
}
//player2
...
}
}
}
我也尝试用KeyBinding替换它,因为这些似乎更值得推荐,但是这里仍然存在相同的“问题”(释放的动作在按下时会一直触发)......
Action attack = new AbstractAction(){
@Override
public void actionPerformed(ActionEvent e) {
controller.attack(0);
}
};
Action stopAttack = new AbstractAction(){
@Override
public void actionPerformed(ActionEvent e) {
controller.stopAttack(0);
}
};
background.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "attack");
background.getActionMap().put("attack", attack);
background.getInputMap().put(KeyStroke.getKeyStroke("released SPACE"), "stopAttack");
background.getActionMap().put("stopAttack", stopAttack);
我已经阅读了有关这种情况的多个主题,但未能使其正常运行。
提前致谢
答案 0 :(得分:3)
让我们举一个例子,在其中一台UBUNTU机器上遇到问题; 假设他们按住键盘按钮1000毫秒。
根据你的评论,这就是时间线上会发生的事情:
0 ms - 10ms - 1000ms
keyPressed keyReleased keyReleased
因此,在这些有问题的计算机中,您有1x keyPressed事件和2x keyReleased事件。 第二个关键发布的事件,是坏人。
我会执行以下操作来“修补”问题(直到您正确识别这些UBUNTU计算机中的差异):
(注意我使用的是系统时间,而不是每秒固定的MS,因为KeyEvents发生在UI线程而不是游戏或OpenGL线程上)
//This isn't the 10ms of the example, but if this doesn't work, try increasing it a bit further or
//try meassuring the exact time of the "faulty/not wanted" first onKeyReleased event
final private static long KEY_RELEASE_TIME_LOCK_IN_MS = 100L;
private static long lastKeyRelease_TimeMillis;
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
keyBits.set(keyCode);
}
@Override
public void keyReleased(KeyEvent e) {
final long currentTimeMillis = System.currentTimeMillis();
if( (currentTimeMillis - lastKeyRelease_TimeMillis) <= KEY_RELEASE_TIME_LOCK_IN_MS){
//We just ignored the keyRelease!, we are not allowing keyReleases that fast!
return;
}
int keyCode = e.getKeyCode();
keyBits.clear(keyCode);
if(player1Attack && !isKeyPressed(KeyEvent.VK_SPACE)){
controller.stopAttack(0);
player1Attack = false;
}
lastKeyRelease_TimeMillis = System.currentTimeMillis();
//player 2
...
}
//Rockster., let me know if it worked out
答案 1 :(得分:0)
我有点跟踪Rockster建议的时间。因为在keyReleased之后几乎立即触发了keyPressed,你可以检查: keyPressedTime
当应用程序繁忙时(在我的情况下将敌人添加到字段中),这有时会失败,因为keyPressed不会立即触发。但对于这个应用程序来说,这不是什么大问题。
感兴趣的代码:
//Inner Class
private class KeyDispatcher extends KeyAdapter implements ActionListener{
private long p1AttackLastKeyRelease = 0, p1AttackLastKeyPressed = 0;
private BitSet keyBits = new BitSet(256);
private Timer timer = new Timer(20, this);
boolean player1Attack = false;
public KeyDispatcher(){
timer.start();
}
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_NUMPAD0){
p1AttackLastKeyPressed = System.currentTimeMillis();
}
keyBits.set(keyCode);
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_NUMPAD0){
p1AttackLastKeyRelease = System.currentTimeMillis();
}
keyBits.clear(keyCode);
}
public boolean isKeyPressed(final int keyCode) {
return keyBits.get(keyCode);
}
@Override
public void actionPerformed(ActionEvent e) {
if(isGameOver()){
timer.stop();
}else{
//release the key
if(p1AttackLastKeyPressed != 0 && p1AttackLastKeyPressed<p1AttackLastKeyRelease && player1Attack && !isKeyPressed(KeyEvent.VK_NUMPAD0)){
p1AttackLastKeyPressed = 0;
controller.stopAttack(0);
player1Attack = false;
}
//do the actions of the keys that are down
if(!player1Attack && isKeyPressed(KeyEvent.VK_RIGHT)){
controller.moveRight(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_UP)){
controller.moveUp(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_DOWN)){
controller.moveDown(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_LEFT)){
controller.moveLeft(0);
}
if(!player1Attack && isKeyPressed(KeyEvent.VK_NUMPAD0) && !isKeyPressed(KeyEvent.VK_UP) && !isKeyPressed(KeyEvent.VK_DOWN) && !isKeyPressed(KeyEvent.VK_LEFT) && !isKeyPressed(KeyEvent.VK_RIGHT)){
controller.attack(0); player1Attack = true; update();
}
}
}
}
这仍然没有像我之前说过的那样完全正常,但对于这个应用来说已经足够好了(玩家无法从中获得优势)。
答案 2 :(得分:0)
您应该尝试使用Key Bindings。