javafx应用程序中的多个视口

问题描述 投票:0回答:1

我试图在JavaFX中创建一个多用户,多屏幕的应用程序,我遇到了多屏部分的问题。

想想与沙发合作的FPS:屏幕根据本地连接的人数分配均匀。每个不同的观点都在不同的方向,在不同的地方,但在同一个“世界”。

我学到了很难(在评论here中确认)每个节点只能出现在活动场景图中一次,因此,例如,我不能将相同的节点分布在多个不同的窗格中(这在概念上是理想的)。而这就是我不知道下一步该去哪里的地方。

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

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

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

我如何在每个不同的视图之间创建同一组节点的多个视图,同时仍然保持单独的用户控制,以及更改持久性/移动节点?

谢谢。

java javafx viewport split-screen
1个回答
0
投票

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

然后,更新循环必须仅更新一个背景模型,并且所有副本都会自动更新。每个节点副本都有一个对其模拟的模型节点的引用,因此当用户输入节点的更改时,模型节点会更改,这会更改复制节点。

解决方案不是太优雅,我将不得不更多地考虑多线程(多任务处理?)与JavaFX的Tasks (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);
        }
    }
}

update object.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

© www.soinside.com 2019 - 2024. All rights reserved.