java - 两个线程调用相同的 keyEvents 方法

标签 java multithreading javafx netbeans thread-safety

我有一个用于player1 keyevents的线程,它可以工作,但是当我为player2 keyevents添加另一个线程时,只有player2按键被注册。是线程问题还是 keyEvents() 方法问题?我不知道接下来该去哪里。

    //keyEvents thread for player 1
    AnimationTimer player1_timer = new AnimationTimer(){
        @Override
        public void handle(long now){
            keyEvents(player1, 1);
        }
    };
    player1_timer.start();

    if(true){ //if I set this to false, player1 keyEvents are registered again.
        //keyEvents thread for player 2
        AnimationTimer player2_timer = new AnimationTimer(){
            @Override
            public void handle(long now){
                keyEvents(player2, 2);
            }
        };
        player2_timer.start();
    }

private void keyEvents(Unit player, int playerNumber){
    map.setOnKeyPressed((KeyEvent e) -> {
        if(playerNumber == 1){
            if(null != e.getCode())switch (e.getCode()) {
                case LEFT:
                    player1.rotateLeft();
                    break;
                default:
                    break;
            }
        }
        if(playerNumber == 2){
            if(null != e.getCode())switch (e.getCode()) {
                case A:
                    player2.rotateLeft();
                    break;
                default:
                    break;
            }
        }
    });
}

更新的代码:

//keyEvents thread for players
    AnimationTimer playerTimer = new AnimationTimer(){
        @Override
        public void handle(long now){
            keyEvents();
        }
    };
    playerTimer.start();
private void keyEvents(){
        Bullet bullet = new Bullet();
        map.setOnKeyPressed((KeyEvent e) -> {
            if(null != e.getCode())switch (e.getCode()) {
                case A:
                    player2.rotateLeft();
                    break;
                case LEFT:
                    player1.rotateLeft();
                    break;
                default:
                    break;
            }
        });
    }

最佳答案

首先,这就像我的第二个 JavaFX 程序,因此我确信可以进行许多改进。

您必须认识到您仍然只处理单个线程,即 JavaFX 的事件调度线程。

AnimationTimerEventHandler 都在此线程的上下文中调用,因此添加更多它们不会为您提供任何额外的好处,并且实际上可能是部门。

相反,您希望为 keyPressedkeyReleased 添加单个处理程序。在这些处理程序中,您想要为给定玩家设置给定方向的状态。

最简单的方法之一是通过Set,您可以添加删除“关键”对象,这将代表给定玩家的方向。

然后,您将使用单个 AnimationTimer 充当“主循环”,它将根据 Set 的状态确定动画的方向玩家对象应该被移动。

这是游戏开发中很常见的概念,很简单,但也将逻辑与状态解耦,这样可以通过任何方式(鼠标/摇杆/AI/网络)设置状态,而逻辑不会护理。

import java.util.Set;
import java.util.TreeSet;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import static javafx.scene.input.KeyCode.DOWN;
import static javafx.scene.input.KeyCode.SHIFT;
import static javafx.scene.input.KeyCode.UP;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Test extends Application {

    enum Direction {
        UP, DOWN, LEFT, RIGHT;
    }

    private static final double W = 600, H = 400;

    private Image playerOneImage;
    private Node playerOneNode;

    private Image playerTwoImage;
    private Node playerTwoNode;

    private Set<Direction> playerOneDirection = new TreeSet<>();
    private Set<Direction> playerTwoDirection = new TreeSet<>();

    boolean running;

    @Override
    public void start(Stage stage) throws Exception {
        playerOneImage = makePlayerOne();
        playerOneNode = new ImageView(playerOneImage);

        playerTwoImage = makePlayerTwo();
        playerTwoNode = new ImageView(playerTwoImage);

        Group dungeon = new Group(playerOneNode, playerTwoNode);

        movePlayerTo(playerOneNode, 0, 0);
        movePlayerTo(playerTwoNode, W - 12.5, H - 12.5);

        Scene scene = new Scene(dungeon, W, H, Color.FORESTGREEN);

        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                switch (event.getCode()) {
                    case UP:
                        playerOneDirection.add(Direction.UP);
                        break;
                    case DOWN:
                        playerOneDirection.add(Direction.DOWN);
                        break;
                    case LEFT:
                        playerOneDirection.add(Direction.LEFT);
                        break;
                    case RIGHT:
                        playerOneDirection.add(Direction.RIGHT);
                        break;
                    case W:
                        playerTwoDirection.add(Direction.UP);
                        break;
                    case S:
                        playerTwoDirection.add(Direction.DOWN);
                        break;
                    case A:
                        playerTwoDirection.add(Direction.LEFT);
                        break;
                    case D:
                        playerTwoDirection.add(Direction.RIGHT);
                        break;
                }
            }
        });

        scene.setOnKeyReleased(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                switch (event.getCode()) {
                    case UP:
                        playerOneDirection.remove(Direction.UP);
                        break;
                    case DOWN:
                        playerOneDirection.remove(Direction.DOWN);
                        break;
                    case LEFT:
                        playerOneDirection.remove(Direction.LEFT);
                        break;
                    case RIGHT:
                        playerOneDirection.remove(Direction.RIGHT);
                        break;
                    case W:
                        playerTwoDirection.remove(Direction.UP);
                        break;
                    case S:
                        playerTwoDirection.remove(Direction.DOWN);
                        break;
                    case A:
                        playerTwoDirection.remove(Direction.LEFT);
                        break;
                    case D:
                        playerTwoDirection.remove(Direction.RIGHT);
                        break;
                }
            }
        });

        stage.setScene(scene);
        stage.show();

        AnimationTimer timer = new AnimationTimer() {
            @Override
            public void handle(long now) {
                movePlayer(playerOneNode, playerOneDirection);
                movePlayer(playerTwoNode, playerTwoDirection);
            }
        };
        timer.start();
    }

    private void movePlayer(Node playerNode, Set<Direction> direction) {
        int dx = 0;
        int dy = 0;
        if (direction.contains(Direction.UP)) {
            dy -= 1;
        }
        if (direction.contains(Direction.DOWN)) {
            dy += 1;
        }
        if (direction.contains(Direction.RIGHT)) {
            dx += 1;
        }
        if (direction.contains(Direction.LEFT)) {
            dx -= 1;
        }
        if (running) {
            dx *= 3;
            dy *= 3;
        }
        if (dx == 0 && dy == 0) {
            return;
        }

        final double cx = playerNode.getBoundsInLocal().getWidth() / 2;
        final double cy = playerNode.getBoundsInLocal().getHeight() / 2;

        double x = cx + playerNode.getLayoutX() + dx;
        double y = cy + playerNode.getLayoutY() + dy;

        movePlayerTo(playerNode, x, y);
    }

    private void movePlayerTo(Node playerNode, double x, double y) {
        final double cx = playerNode.getBoundsInLocal().getWidth() / 2;
        final double cy = playerNode.getBoundsInLocal().getHeight() / 2;

        if (x - cx >= 0
                        && x + cx <= W
                        && y - cy >= 0
                        && y + cy <= H) {
            playerNode.relocate(x - cx, y - cy);
        }
    }

    protected Image makePlayerOne() {
        return makePlayer(Color.RED);
    }

    protected Image makePlayerTwo() {
        return makePlayer(Color.BLUE);
    }

    protected Image makePlayer(Color color) {
        WritableImage image = new WritableImage(25, 25);
        PixelWriter writer = image.getPixelWriter();
        for (int y = 0; y < 25; y++) {
            for (int x = 0; x < 25; x++) {
                writer.setColor(x, y, color);
            }
        }
        return image;
    }

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

关于java - 两个线程调用相同的 keyEvents 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47613335/

相关文章:

java - 使用 collect() 而不是 filter() 过滤流

java - 我们可以将 Oracle RECORD TYPE 与 java Callable 语句集成吗?

c# - 中止线程(Backgroundworker)..需要建议

java - JavaFX TableView 中的重复列

intellij-idea - JavaFX Fontawesomefx - 主应用程序图标

java - 如何迭代 JsonObject (gson)

Java 子类化或实现具有相同边界的泛型类型

c - 创建和内存泄漏后立即分离线程

c# - 为什么进程会丢失线程?

Java FX从不同场景更改标签的值