当前位置: 首页 > 知识库问答 >
问题:

当键在Javafx中持有时,如何限制键输入?

龙德润
2023-03-14

我在写一个地牢风格的游戏,地牢基本上是一个网格窗格。我想让玩家每0.5秒移动一格,如果他保持控制键。但我不知道如何做到这一点。我读过JavaFX:如何检测钥匙是否被按下。但这个问题与我的问题并没有特别的关系(除了我可以追踪发生了多少关键事件,或许可以在此基础上做更多)。所以我跟随这篇文章并尝试使用线程。sleep()解决了我的问题,但事实证明玩家只是停了几秒钟,然后突然移动了几格。

有人知道怎么解决吗?

@FXML
public void handleKeyPress(KeyEvent event) {

    switch (event.getCode()) {
    case UP:
        System.out.println("Up");
        player.moveUp();
        break;
    case DOWN:
        System.out.println("Down");
        player.moveDown();
        break;
    case LEFT:
        System.out.println("Left");
        player.moveLeft();
        break;
    case RIGHT:
        System.out.println("Right");
        player.moveRight();
        break;
    default:
        break;
    }
}

共有1个答案

蒋奕
2023-03-14

基本上,你必须记录你最后一次允许移动的时间,并且只有在经过指定的时间后才能再次移动。以下是概念证明:

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Main extends Application {

    private static final long THRESHOLD = 500_000_000L; // 500 ms

    private long lastMoveNanos;
    private Rectangle rect;

    @Override
    public void start(Stage primaryStage) {
        rect = new Rectangle(25, 25, Color.DODGERBLUE);
        rect.setStroke(Color.BLACK);
        rect.setStrokeWidth(1);

        var root = new Group(rect);
        root.setOnKeyPressed(this::handleKeyPressed);

        var scene = new Scene(root, 600, 400);
        rect.setX(scene.getWidth() / 2 - rect.getWidth() / 2);
        rect.setY(scene.getHeight() / 2 - rect.getHeight() / 2);

        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();

        root.requestFocus();
    }

    private void handleKeyPressed(KeyEvent event) {
        if (event.getCode().isArrowKey()) {
            event.consume();

            long now = System.nanoTime();
            if (lastMoveNanos <= 0L || now - lastMoveNanos >= THRESHOLD) {
                switch (event.getCode()) {
                    case UP:
                        rect.setY(rect.getY() - rect.getHeight());
                        break;
                    case DOWN:
                        rect.setY(rect.getY() + rect.getHeight());
                        break;
                    case LEFT:
                        rect.setX(rect.getX() - rect.getWidth());
                        break;
                    case RIGHT:
                        rect.setX(rect.getX() + rect.getWidth());
                        break;
                    default:
                        throw new AssertionError();
                }
                lastMoveNanos = now;
            }
        }
    }

}

上面以每500毫秒移动1次的最大速度移动矩形,移动量等于其宽度/高度(以模拟“网格”)。如果位置变化应该是连续的,则可以使用动画进行移动(例如,设置了byXbyY属性的translation)。使用动画时,如果持续时间与所需的调节时间一样长,则可以使用动画本身来跟踪下一步的允许时间。

更可靠的示例可能使用AnimationTimer,并跟踪所有已按下但尚未释放的移动键(例如,在释放“当前”键时拾取最后一个仍按下的键,如果有)。下面是另一个概念证明:

import java.util.ArrayDeque;
import java.util.Deque;
import javafx.animation.AnimationTimer;
import javafx.scene.input.KeyCode;
import javafx.scene.shape.Rectangle;

class MoveAnimationTimer extends AnimationTimer {

    private static final long THRESHOLD = 500_000_000L; // 500 ms

    private final Rectangle node;

    private final Deque<KeyCode> keyStack = new ArrayDeque<>(4);
    private long then;

    MoveAnimationTimer(Rectangle node) {
        this.node = node;
    }

    void pushKeyIfAbsent(KeyCode code) {
        if (code.isArrowKey() && !keyStack.contains(code)) {
            keyStack.push(code);
            start();
        }
    }

    void removeKey(KeyCode code) {
        if (code.isArrowKey()) {
            keyStack.remove(code);
            if (keyStack.isEmpty()) {
                stop();
            }
        }
    }

    @Override
    public void handle(long now) {
        if (then <= 0L || now - then >= THRESHOLD) {
            switch (keyStack.getFirst()) {
                case UP:
                    node.setY(node.getY() - node.getHeight());
                    break;
                case DOWN:
                    node.setY(node.getY() + node.getHeight());
                    break;
                case LEFT:
                    node.setX(node.getX() - node.getWidth());
                    break;
                case RIGHT:
                    node.setX(node.getX() + node.getWidth());
                    break;
                default:
                    throw new AssertionError();
            }
            then = now;
        }
    }

}
 类似资料:
  • 问题内容: 我想控制Javafx TextField中的输入,以便只允许数字输入,这样,如果超出了最大字符数,则不会对文本框进行任何更改。 编辑:根据评论中的建议,我使用了JavaFX项目负责人建议的方法。阻止输入字母非常有用。我只需要它也可以过滤特殊字符。我尝试将过滤器更改为(text.matchs(“ [0-9]”),但不允许输入退格键。 edit2:找出一个特殊字符和长度的过滤器。这是我的最

  • 新用户:从一月份开始学习Java,现在我正在使用NetBeans,如果我写这个简单的快捷方式,我会得到大量的错误。 所以IDE只是告诉我,我几乎做错了一切。在setOnKeyPressed的线路上,我得到了 我导入了所有内容,并从StackOverflow.com上的另一个问题(可能是有效的)中复制了这段代码。我只想按“esc”,并在root被聚焦的情况下关闭我的primarystage(不管是不

  • 我正在开发一个javafx扫雷游戏,目前只使用鼠标左键输入。我想用鼠标右键也让用户可以标记可能的炸弹。我查看了Button类的Oracle网页,它说: “当按下并释放按钮时,将发送ActionEvent。应用程序可以通过实现EventHandler来处理ActionEvent来执行基于此事件的某些操作。按钮也可以通过实现EventHandler来处理MouseEvent来响应鼠标事件。” http

  • 我想控制Javafx TextField中的输入,以便只允许数字输入,并且如果超过了最大字符数,则不会对TextBox进行任何更改。 编辑:根据注释中的建议,我使用了JavaFX项目负责人建议的方法。阻止信件被输入很有效。我只需要它也过滤特殊字符。我尝试将筛选器更改为(text.matchs([0-9]“),但这不允许输入退格符。

  • 问题内容: 我有UISearchDisplayController的UISearchBar部分,该部分用于显示来自本地CoreData和远程API的搜索结果。我要实现的是在远程API上进行搜索的“延迟”。当前,对于用户键入的每个字符,都会发送一个请求。但是,如果用户输入速度特别快,那么发送许多请求就没有意义:这将有助于等待用户停止输入。有办法实现吗? 阅读文档建议您等到用户明确点击搜索后,但就我而