JavaFX key interruptions?

醉酒当歌 提交于 2019-11-28 06:38:33

问题


I'm trying to write a game in JavaFX but I'm having a slight issue with it and that's the key listeners getting interrupted by other key presses. I'm using scene.setOnKeyPressed(KeyEvent) and it works. When the player holds down a key they move continuously but if they hit another key it forgets about the first key even if they let go of the second key. I'm trying to figure out how to allow them to do one action and then when the key they were holding is released if they're still holding the other one go back to that.


回答1:


This is similar to this question, though the requirements look slightly different. Similar techniques work, but if you just want to respond to the most-recently-pressed key, you probably need some kind of stack.

This example allows you to move a rectangle around the screen, with the up, down, left, or right cursor keys; only the most recently pressed key that is still down is used.

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);
    }
}



回答2:


I would make a set of all keys currently pressed and just check them in a timer. It doesn't eat many cpu cycles on my old machine. In motion it's 5%, but 0% when no key is pressed.

For a possibly faster solution use an ObservableSet and start and stop actions when a key is added or removed.

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);
    }

}

I'm thinking the parallel is stupid and the Platform.runLater costs more than the threading of +2 or -2 . But hey, java8 that's why



来源:https://stackoverflow.com/questions/28749737/javafx-key-interruptions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!