javafx应用程序中的多个视口

时间:2019-04-18 01:31:32

标签: java javafx viewport split-screen

我正在尝试在JavaFX中创建一个多用户,多屏幕应用程序,但是我在多屏幕部分遇到了麻烦。

想想带沙发合作社的FPS:根据本地连接的人数,屏幕平均分配。每个不同的视图都朝着不同的方向,在不同的位置,但在相同的“世界”中。

我学会了一种艰难的方法(在评论here中得到确认),每个节点只能在活动场景图中出现一次,因此,例如,我不能使同一节点分布在多个不同的窗格中(在概念上是理想的)。那就是我不确定下一步去哪里的地方。

看看其他类似的技术,例如OpenGL(example),大多数能够为应用程序创建另一个视口,但是JavaFX似乎没有。

我排除了一些不合理/不可能的事情(如果我错了,请纠正我):

  • 使用形状为窗格创建剪贴蒙版(每个节点只能使用一个蒙版)
  • 每个视图都有每个节点的完整深层副本(太昂贵,节点不断移动)
  • 拥有x个用户,每个用户都有自己的一组节点,并且有一个更新循环来更新每个视图中的每个节点(太昂贵,场景图的节点太多,太多)

我将如何在同一组节点上创建多个视图,同时仍保持各自的用户控制权,并在每个不同的视图之间更改持久性/移动节点?

谢谢。

1 个答案:

答案 0 :(得分:0)

感谢评论中的人员提供解决方案。最后,我为每个要镜像的视图创建了一个背景模型,然后为每个视图创建了一组具有绑定到背景模型的相关属性的新节点。

然后,更新循环只需更新一个后台模型,所有副本自动更新。每个节点副本都有对它要模仿的模型节点的引用,因此,当用户输入节点的更改时,模型节点也会更改,从而更改了副本节点。

解决方案不是太优雅,我将不得不更多地研究JavaFX的Task s (here)Platform.runLater() (here)函数的多线程(多任务处理)功能。

这是我所完成工作的一个简短示例:

Proof Of Concept Gif

Main.java

public class Main extends Application {

    private static Group root = new Group();
    private static Scene initialScene = new Scene(root, Color.BLACK);

    private static final int NUM_OF_CLIENTS = 8;

    private static long updateSpeed = 20_666_666L;
    private static double deltaTime;
    private static double counter = 0;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setFullScreen(true);
        primaryStage.setScene(initialScene);
        primaryStage.show();

        initModel();
        initModelViews();
        startUpdates();
    }

    private void initModel() {
        for (int i = 0; i < NUM_OF_CLIENTS; i++) {
            Model.add(new UpdateObject());
        }
    }

    private void initModelViews() {
        //Correctly positioning the views
        int xPanes = (NUM_OF_CLIENTS / 4.0 > 1.0) ? 4 : NUM_OF_CLIENTS;
        int yPanes = (NUM_OF_CLIENTS / 4) + ((NUM_OF_CLIENTS % 4 > 0) ? 1 : 0);

        for (int i = 0; i < NUM_OF_CLIENTS; i++) {
            Pane clientView = new Pane(copyModelNodes());
            clientView.setBackground(new Background(new BackgroundFill(Color.color(Math.random(), Math.random(), Math.random()), CornerRadii.EMPTY, Insets.EMPTY)));
            System.out.println(clientView.getChildren());
            clientView.relocate((i % 4) * (Main.initialScene.getWidth() / xPanes), (i / 4) * (Main.initialScene.getHeight() / yPanes)) ;
            clientView.setPrefSize((Main.initialScene.getWidth() / xPanes), (Main.initialScene.getHeight() / yPanes));
            root.getChildren().add(clientView);
        }
    }

    private Node[] copyModelNodes() {
        ObservableList<UpdateObject> model = Model.getModel();
        Node[] modelCopy = new Node[model.size()];

        for (int i = 0; i < model.size(); i++) {
            ImageView testNode = new ImageView();
            testNode.setImage(model.get(i).getImage());
            testNode.layoutXProperty().bind(model.get(i).layoutXProperty());
            testNode.layoutYProperty().bind(model.get(i).layoutYProperty());
            testNode.rotateProperty().bind(model.get(i).rotateProperty());
            modelCopy[i] = testNode;
        }
        return modelCopy;
    }

    private void startUpdates() {
        AnimationTimer mainLoop = new AnimationTimer() {
            private long lastUpdate = 0;

            @Override
            public void handle(long frameTime) {

                //Time difference from last frame
                deltaTime = 0.00000001 * (frameTime - lastUpdate);

                if (deltaTime <= 0.1 || deltaTime >= 1.0)
                    deltaTime = 0.00000001 * updateSpeed;

                if (frameTime - lastUpdate >= updateSpeed) {
                    update();
                    lastUpdate = frameTime;
                }
            }
        };
        mainLoop.start();
    }

    private void update() {
        counter += 0.1;

        if (counter > 10.0) {
            counter = 0;
        }
        for (UpdateObject objectToUpdate : Model.getModel()) {
            objectToUpdate.setLayoutX(objectToUpdate.getLayoutX() + 0.02 * counter * deltaTime);
            objectToUpdate.setLayoutY(objectToUpdate.getLayoutY() + 0.02 * counter * deltaTime);
            objectToUpdate.setRotate(objectToUpdate.getRotate() + 5);
        }
    }
}

UpdateObject.java

class UpdateObject extends ImageView {

    private static Random random = new Random();
    private static Image testImage = new Image("duckTest.png");

    UpdateObject() {
        this.setImage(testImage);
        this.setLayoutX(random.nextInt(50));
        this.setLayoutY(random.nextInt(50));
        this.setRotate(random.nextInt(360));
    }
}

Model.java

class Model {

    private static ObservableList<UpdateObject> modelList = FXCollections.observableArrayList();

    static void add(UpdateObject objectToAdd) {
        modelList.add(objectToAdd);
    }

    static ObservableList<UpdateObject> getModel() {
        return modelList;
    }
}

Test image used