在 Javafx 中使用键盘移动两个矩形

Moving Two Rectangles with keyboard in Javafx

我是编程新手,我一直在使用 JavaFX 开发一款两人游戏。一名玩家使用 W/S 移动他的 "paddle",另一名玩家使用 Up/Down 箭头移动他的。我一直遇到的问题是一次只能有一个玩家移动他的球拍。我不知道如何获得它,以便他们每个人都可以同时移动各自的桨。我让一个键盘事件处理程序控制两个桨,我认为这就是问题所在。我制作了两个单独的键盘处理程序,但遇到了另一组问题,我认为是由内置的 setFocusTraversable 方法引起的。我希望我正在尝试做的事情是有道理的。有什么想法吗?

import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;


public class Game{

Rectangle leftPaddle;
double leftPaddleY = 260;

Rectangle rightPaddle;
double rightPaddleY = 260;

public void createGame(Group gameDisplay){

    //creates background
    Rectangle background = new Rectangle(0,0,800,600);
    background.getStyleClass().add("background");

    //draws field lines
    Canvas game = new Canvas(800, 600);
    GraphicsContext gc = game.getGraphicsContext2D();
    gc.setStroke(Paint.valueOf("WHITE"));
    gc.setLineWidth(5);
    gc.strokeLine(400, 0, 400, 600);
    gc.strokeOval(300, 200, 200, 200);
    gc.strokeRect(0, 150, 100, 300);
    gc.strokeRect(700, 150, 100, 300);
    gc.setStroke(Paint.valueOf("BLACK"));
    gc.setLineWidth(8);
    gc.strokeRect(0, 0, 800, 600);

    //creates red paddle
    leftPaddle = new Rectangle(30, leftPaddleY, 20, 70);
    leftPaddle.setOnKeyPressed(paddleMovement);
    leftPaddle.setFocusTraversable(true);
    leftPaddle.setFill(Color.RED);

    //creates blue paddle
    rightPaddle = new Rectangle(750, rightPaddleY, 20, 70);
    rightPaddle.setOnKeyPressed(paddleMovement);
    rightPaddle.setFocusTraversable(true);
    rightPaddle.setFill(Color.BLUE);


    gameDisplay.getStylesheets().add(getClass().getResource("GameDisplay.css").toExternalForm());
    gameDisplay.getChildren().addAll(background, game, leftPaddle, rightPaddle);
}

public EventHandler<KeyEvent> paddleMovement = new EventHandler<KeyEvent>(){

    @Override
    public void handle(KeyEvent event) {

        //red paddle movement
        if(event.getCode().equals(KeyCode.W)){
            leftPaddle.setY(leftPaddleY -= 6);
            if(leftPaddle.getY() < 0){
                leftPaddle.setY(0);
                leftPaddleY = 0;
            }
        }
        if(event.getCode().equals(KeyCode.S)){
            leftPaddle.setY(leftPaddleY += 6);
            if(leftPaddle.getY() < 0){
                leftPaddle.setY(0);
                leftPaddleY = 0;
            }
        }

        //blue paddle movement
        if(event.getCode().equals(KeyCode.UP)){
            rightPaddle.setY(rightPaddleY -= 6);
            if(rightPaddle.getY() < 0){
                rightPaddle.setY(0);
                rightPaddleY = 0;
            }
        }
        if(event.getCode().equals(KeyCode.DOWN)){
            rightPaddle.setY(rightPaddleY += 6);
            if(rightPaddle.getY() < 0){
                rightPaddle.setY(0);
                rightPaddleY = 0;
            }
        }

    }
};
}

keyPressed事件只会在最后一个按键按下时重复触发。

要解决此问题,只需 "remember" 每个球拍的所需运动并同时收听 keyReleased 事件。您可以使用 AnimationTimer 来执行动作以每帧执行更新(您也可以使用 Timeline 来更好地控制更新频率)。

此外,我建议对成员变量的可见性进行更多限制,因为通常您不希望其他 classes 能够直接写入您的 class 的字段.我还建议在单个 Node 中处理事件。一次只能有一个 Node 获得焦点,在不同地方处理事件只会导致重复代码。

public class Game {

    private Rectangle leftPaddle;
    private double leftPaddleY = 260;

    private Rectangle rightPaddle;
    private double rightPaddleY = 260;

    private double leftPaddleDY;
    private double rightPaddleDY;
    private AnimationTimer timer = new AnimationTimer() {

        @Override
        public void handle(long now) {
            // update paddle positions
            leftPaddleY += leftPaddleDY;
            rightPaddleY += rightPaddleDY;
            if (leftPaddleY < 0) {
                leftPaddleY = 0;
            }
            if (rightPaddleY < 0) {
                rightPaddleY = 0;
            }

            leftPaddle.setY(leftPaddleY);
            rightPaddle.setY(rightPaddleY);
        }

    };

    public void createGame(Group gameDisplay) {

        //creates background
        Rectangle background = new Rectangle(0, 0, 800, 600);
        background.getStyleClass().add("background");

        //draws field lines
        Canvas game = new Canvas(800, 600);
        GraphicsContext gc = game.getGraphicsContext2D();
        gc.setStroke(Paint.valueOf("WHITE"));
        gc.setLineWidth(5);
        gc.strokeLine(400, 0, 400, 600);
        gc.strokeOval(300, 200, 200, 200);
        gc.strokeRect(0, 150, 100, 300);
        gc.strokeRect(700, 150, 100, 300);
        gc.setStroke(Paint.valueOf("BLACK"));
        gc.setLineWidth(8);
        gc.strokeRect(0, 0, 800, 600);

        //creates red paddle
        leftPaddle = new Rectangle(30, leftPaddleY, 20, 70);
        leftPaddle.setFill(Color.RED);

        //creates blue paddle
        rightPaddle = new Rectangle(750, rightPaddleY, 20, 70);
        rightPaddle.setFill(Color.BLUE);

        // register event handlers to Canvas
        game.setFocusTraversable(true);
        game.setOnKeyPressed(keyPressed);
        game.setOnKeyReleased(keyReleased);

        gameDisplay.getStylesheets().add(getClass().getResource("GameDisplay.css").toExternalForm());
        gameDisplay.getChildren().addAll(background, game, leftPaddle, rightPaddle);
        // start updates of paddle positions
        timer.start();
    }

    private EventHandler<KeyEvent> keyReleased = new EventHandler<KeyEvent>() {

        @Override
        public void handle(KeyEvent event) {
            // set movement to 0, if the released key was responsible for the paddle
            switch (event.getCode()) {
                case W:
                case S:
                    leftPaddleDY = 0;
                    break;
                case UP:
                case DOWN:
                    rightPaddleDY = 0;
                    break;
            }
        }

    };

    private EventHandler<KeyEvent> keyPressed = new EventHandler<KeyEvent>() {

        @Override
        public void handle(KeyEvent event) {
            // start movement according to key pressed
            switch (event.getCode()) {
                case W:
                    leftPaddleDY = -6;
                    break;
                case S:
                    leftPaddleDY = 6;
                    break;
                case UP:
                    rightPaddleDY = -6;
                    break;
                case DOWN:
                    rightPaddleDY = 6;
                    break;
            }

        }
    };
}