javafx 应用程序中的多个视口
Multiple viewports in javafx application
我正在尝试在 JavaFX 中创建多用户、多屏幕应用程序,但我在多屏幕部分遇到了问题。
想一想沙发合作的第一人称射击游戏:屏幕会根据本地连接的人数平均分配。每个不同的视图都在不同的方向和不同的地方看,但在相同的地方 'world'.
我通过艰难的方式了解到(在评论 here 中确认)每个节点只能在活动场景图中出现一次,因此,例如,我不能将同一个节点分布在多个不同的窗格中(这在概念上是理想的)。这就是我不确定下一步要去哪里的地方。
看看其他类似的技术,如 OpenGL,(example) 大多数都有为应用程序创建另一个视口的能力,但 JavaFX 似乎没有这个。
有些东西我已经排除为unreasonable/impossible(如果我错了请纠正我):
- 使用形状为窗格创建剪切遮罩(每个节点只能使用一个遮罩)
- 每个视图都有每个节点的完整深拷贝(太贵了,节点不断移动)
- 有 x 个用户,每个用户都有自己的一组节点,并且有一个更新循环更新每个视图中的每个节点(太昂贵,场景图的节点太多,太多)
我将如何创建同一组节点的多个视图,同时仍然保持个人用户控制,并在每个不同视图之间更改 persistence/moving 个节点?
谢谢。
感谢评论中提供解决方案的人。我最终为每个要镜像的视图创建了一个背景模型,然后为每个视图创建了一组新的节点,这些节点具有绑定到背景模型的相关属性。
更新循环只需要更新一个背景模型,所有副本都会自动更新。每个节点副本都有一个对其模仿的模型节点的引用,因此当用户输入节点的更改时,模型节点会更改,从而更改副本节点。
解决方案不太优雅,我将不得不更多地研究多线程(多任务处理?)和 JavaFX 的 Task
s and Platform.runLater()
函数以增加功能。
这是我完成的一个简单示例:
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;
}
}
我正在尝试在 JavaFX 中创建多用户、多屏幕应用程序,但我在多屏幕部分遇到了问题。
想一想沙发合作的第一人称射击游戏:屏幕会根据本地连接的人数平均分配。每个不同的视图都在不同的方向和不同的地方看,但在相同的地方 'world'.
我通过艰难的方式了解到(在评论 here 中确认)每个节点只能在活动场景图中出现一次,因此,例如,我不能将同一个节点分布在多个不同的窗格中(这在概念上是理想的)。这就是我不确定下一步要去哪里的地方。
看看其他类似的技术,如 OpenGL,(example) 大多数都有为应用程序创建另一个视口的能力,但 JavaFX 似乎没有这个。
有些东西我已经排除为unreasonable/impossible(如果我错了请纠正我):
- 使用形状为窗格创建剪切遮罩(每个节点只能使用一个遮罩)
- 每个视图都有每个节点的完整深拷贝(太贵了,节点不断移动)
- 有 x 个用户,每个用户都有自己的一组节点,并且有一个更新循环更新每个视图中的每个节点(太昂贵,场景图的节点太多,太多)
我将如何创建同一组节点的多个视图,同时仍然保持个人用户控制,并在每个不同视图之间更改 persistence/moving 个节点?
谢谢。
感谢评论中提供解决方案的人。我最终为每个要镜像的视图创建了一个背景模型,然后为每个视图创建了一组新的节点,这些节点具有绑定到背景模型的相关属性。
更新循环只需要更新一个背景模型,所有副本都会自动更新。每个节点副本都有一个对其模仿的模型节点的引用,因此当用户输入节点的更改时,模型节点会更改,从而更改副本节点。
解决方案不太优雅,我将不得不更多地研究多线程(多任务处理?)和 JavaFX 的 Task
s Platform.runLater()
这是我完成的一个简单示例:
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;
}
}