Java 如何防止按键被按下时重复动作?
我试图在一个JavaFX“游戏”中做一个简单的动作,在这个游戏中,每当按下空格键时,一只猪的图像就会向上“跳跃”。下面是关键事件处理程序和动画计时器的代码,我正在使用它们来实际执行操作 密钥处理程序:Java 如何防止按键被按下时重复动作?,java,javafx,Java,Javafx,我试图在一个JavaFX“游戏”中做一个简单的动作,在这个游戏中,每当按下空格键时,一只猪的图像就会向上“跳跃”。下面是关键事件处理程序和动画计时器的代码,我正在使用它们来实际执行操作 密钥处理程序: ArrayList<String> in = new ArrayList<String>(); s.setOnKeyPressed( new EventHandler<KeyEvent>() {
ArrayList<String> in = new ArrayList<String>();
s.setOnKeyPressed(
new EventHandler<KeyEvent>()
{
public void handle(KeyEvent e)
{
String code = e.getCode().toString();
if ( !in.contains(code) ){
in.add( code );
}
}
});
s.setOnKeyReleased(
new EventHandler<KeyEvent>()
{
public void handle(KeyEvent e)
{
String code = e.getCode().toString();
in.remove( code );
}
});
如你所见,动画定时器只是让“猪”逐渐沿y轴下落,当按下空格键时,它会稍微向上提升
问题是,如果按住空格键,清管器就会不停地向上飞。我想防止这种情况发生,这样空格键就必须反复敲击,而不仅仅是按住。所以每次按空格键我只想跳一次。我试图解决的问题都没有奏效。我该怎么做?编辑:我修改了答案。最初的解决方案使用了一个计数器,防止按下的键在一段时间内产生任何影响。不幸的是,这不是这个问题的主题。:)当前的解决方案更直接,只使用一个简单的布尔锁
在回答这个问题之前,这里有一些恼人的提示:我建议使用Map
而不是List
来存储关于当前按下哪些键的信息。它将简化代码的可读性,同时提高性能。接下来,创建一个专用对象来存储关于猪的信息(哈哈!)可能是个好主意。最后,使用常量而不是硬编码的文字值是一种很好的做法
另外,请注意,您实际上不需要存储有关是否按下空格键的信息,然后从计时器线程引用它。只有当您希望通过按住空格键来控制清管器时,才需要执行此操作。但是,由于您希望它只在按下空格键时跳转,因此可以直接从处理程序告诉清管器切换到“跳转”状态。当然,这并不能解决您的问题,因为当长时间按住某个键时,onKeyPressed
处理程序会被重复调用。但我认为这值得一提
现在,回答这个问题。如果您想快速修复当前的解决方案并忽略所有“良好实践”垃圾,请只关注Pig
类的jumpLock
字段。诀窍是不断告诉清管器重复跳转,就像你现在做的那样,但要确保清管器只有在jumpLock
允许的情况下才会服从
注意:以下解决方案假设您将使用固定的时间间隔(如每30毫秒)更新游戏状态。但正如最后指出的,这个解决方案可以很容易地修改为使用基于FPS的计时器
以下类包含将来调整游戏时可能需要更改的常量:
public final class Settings {
private Settings() {
}
public static final double BOOST_VELOCITY = 10.0;
public static final double GRAVITY = 0.3;
}
这个类代表猪。x
和y
字段存储关于清管器当前位置的信息velocityX
和velocityY
分别是包含关于清管器在X轴和Y轴方向和“速度”信息的向量jumpLock
是一个简单的布尔标志,它实际上是问题的解决方案。每当用户进行跳转时,此锁定设置为true
。它将一直保持这种状态,直到它被告知释放锁,这将在用户释放空格键时发生
public final class Pig {
private double x;
private double y;
private double velocityX;
private double velocityY;
private boolean jumpLock;
public Pig() {
// ...
}
public void timeChanged() {
x += velocityX;
y += velocityY;
velocityY -= Settings.GRAVITY;
}
public void jumpBoost() {
if (!jumpLock) {
velocityY = Settings.BOOST_VELOCITY;
jumpLock = true;
}
}
public void releaseLock() {
jumpLock = false;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
}
您的处理程序可能是这样的。请注意,Map
用于存储有关当前按下的键的信息。在这种情况下,它的性能优于List
。此外,添加@Override
注释是一种很好的做法,即使在重写抽象方法时也是如此:
final Map<KeyCode, Boolean> keyboard = new HashMap<>();
keyboard.put(KeyCode.SPACE, false);
scene.setOnKeyPressed(
new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.SPACE) {
keyboard.put(e.getCode(), true);
// You could alternately call pig.jumpBoost()
// directly from this handler and not having to
// deal with the 'keyboard' map at all
// as illustrated with by pig.releaseLock()
// in the next handler
}
}
});
scene.setOnKeyReleased(
new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.SPACE) {
keyboard.put(e.getCode(), false);
pig.releaseLock(); // IMPORTANT!!!
}
}
});
希望我没弄错。写这篇文章的时候我几乎睡着了,一开始我误解了这个问题。但我真的需要赢得一些声誉点,才能对一个目前对我很重要的问题发表评论。哈哈D:D当在AnimationTimer的
handle
方法(在q-=20
之后)中处理时,可以尝试从中的列表中删除“SPACE”,而不是等待onkeyreased
被触发。现在你要做的是清管器向上移动,直到空格键被释放,这不是你想要的,你只是希望每次新闻都发生一次,对吗?我试过了。出于某种原因,这会导致“跳跃”只上升一点点,然后立即结束,好像删除“空格”会取消动画。这是正确的。非常感谢您花时间提供帮助!这对我帮助很大。好东西。
final Map<KeyCode, Boolean> keyboard = new HashMap<>();
keyboard.put(KeyCode.SPACE, false);
scene.setOnKeyPressed(
new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.SPACE) {
keyboard.put(e.getCode(), true);
// You could alternately call pig.jumpBoost()
// directly from this handler and not having to
// deal with the 'keyboard' map at all
// as illustrated with by pig.releaseLock()
// in the next handler
}
}
});
scene.setOnKeyReleased(
new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent e) {
if (e.getCode() == KeyCode.SPACE) {
keyboard.put(e.getCode(), false);
pig.releaseLock(); // IMPORTANT!!!
}
}
});
pig.timeChanged();
if (keyboard.get(KeyCode.SPACE)) {
pig.jumpBoost();
}
// Note that pig.releaseLock() could be called in else
// branch here and not in the onKeyReleased handler.
// Choose whatever solution suits you best.
// + draw image of the pig on pig.getX() and pig.getY() coordinates