JavaFX密钥中断?

时间:2015-02-26 18:11:29

标签: java javafx javafx-8

我正在尝试用JavaFX编写一个游戏,但我遇到了一个小问题,那就是关键的听众被其他按键打断了。我正在使用scene.setOnKeyPressed(KeyEvent)并且它有效。当玩家按下一个键时,他们会持续移动,但如果他们按下另一个键,即使他们放开了第二个键,也会忘记第一个键。我试图找出如何允许他们做一个动作然后当他们持有的钥匙被释放时,如果他们仍然持有另一个回到那个。

2 个答案:

答案 0 :(得分:1)

这与this question类似,但要求略有不同。类似的技术可以工作,但是如果你只想回应最近按下的键,你可能需要某种堆栈。

此示例允许您使用向上,向下,向左或向右光标键在屏幕上移动矩形;仅使用仍处于关闭状态的最近按下的键。

import java.util.LinkedList;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class KeyStackExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        Rectangle rect = new Rectangle(50, 50, 100, 50);
        rect.setFill(Color.SALMON);

        Pane pane = new Pane(rect);
        Scene scene = new Scene(pane, 800, 600);

        final double rectangleHSpeed = 100 ; // pixels per second
        final double rectangleVSpeed = 100 ;
        final double minX = 0 ;
        final double maxX = 800 ; 
        final double minY = 0 ;
        final double maxY = 600 ;

        final LinkedList<KeyCode> keyStack = new LinkedList<>();
        scene.setOnKeyPressed(event -> {
            KeyCode code = event.getCode();
            if (! keyStack.contains(code)) {
                keyStack.push(code); 
            }
        });

        scene.setOnKeyReleased(event -> 
            keyStack.remove(event.getCode()));


        final LongProperty lastUpdateTime = new SimpleLongProperty();
        final AnimationTimer rectangleAnimation = new AnimationTimer() {
          @Override
          public void handle(long timestamp) {
            if (! keyStack.isEmpty() && lastUpdateTime.get() > 0) {
              final double elapsedSeconds = (timestamp - lastUpdateTime.get()) / 1_000_000_000.0 ;
              double deltaX = 0 ;
              double deltaY = 0 ;
              switch(keyStack.peek()) {
              case UP:
                  deltaY = -rectangleVSpeed * elapsedSeconds;
                  break ;
              case DOWN: 
                  deltaY = rectangleVSpeed * elapsedSeconds ;
                  break ;
              case LEFT:
                  deltaX = -rectangleHSpeed * elapsedSeconds ;
                  break ;
              case RIGHT:
                  deltaX = rectangleHSpeed * elapsedSeconds ;
              default:
                  break ;
              }
              double oldX = rect.getTranslateX() ;
              double oldY = rect.getTranslateY() ;
              rect.setTranslateX(clamp(oldX + deltaX, minX, maxX));
              rect.setTranslateY(clamp(oldY + deltaY, minY, maxY));
            }
            lastUpdateTime.set(timestamp);
          }
        };
        rectangleAnimation.start();

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private double clamp(double value, double min, double max) {
        return Math.max(min, Math.min(max, value));
    }

    public static void main(String[] args) {
        launch(args);
    }
}

答案 1 :(得分:0)

我会按下当前按下的一组所有按键,然后在计时器中检查它们。它不会在我的旧机器上吃掉很多cpu周期。在运动中它是5%,但是没有按下键时为0%。

对于可能更快的解决方案,使用ObservableSet并在添加或删除密钥时启动和停止操作。

package keytest;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class KeyTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        ObservableSet<KeyCode> downKeys = FXCollections.observableSet();

        Circle circle = new Circle(10);
        Pane root = new Pane(circle);
        Scene scene = new Scene(root, 500, 500);

        scene.setOnKeyPressed(evt->{
            downKeys.add(evt.getCode());
        });

        scene.setOnKeyReleased(evt->{
            downKeys.remove(evt.getCode());
        });


        Timeline timer = new Timeline(new KeyFrame(
            javafx.util.Duration.millis(16), ae -> {
                downKeys.stream().parallel().forEach(kc -> {
                    Platform.runLater(() -> {//why?
                        switch(kc){
                            case UP: circle.setTranslateY(circle.getTranslateY()-2);
                                break;
                            case DOWN: circle.setTranslateY(circle.getTranslateY()+2);
                                break;
                            case LEFT: circle.setTranslateX(circle.getTranslateX()-2);
                                break;
                            case RIGHT: circle.setTranslateX(circle.getTranslateX()+2);
                                break;
                        }
                    });
                });
            }));
        timer.setCycleCount(Animation.INDEFINITE);
        timer.play();

        //I think this might be faster
        downKeys.addListener((SetChangeListener.Change<? extends KeyCode> change) -> {
            if (change.wasAdded()){
                System.out.println("start action "+change.getElementAdded());
            }
            if (change.wasRemoved()){
                System.out.println("stop action "+change.getElementRemoved());
            }
        });

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

我认为并行是愚蠢的,而Platform.runLater的成本高于+2或-2的线程。但是嘿,java8就是这个原因