JavaFX key interruptions?

后端 未结 2 587
清酒与你
清酒与你 2020-12-20 08:44

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.s

相关标签:
2条回答
  • 2020-12-20 09:43

    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

    0 讨论(0)
  • 2020-12-20 09:48

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题