2d 场景中的 JavaFX 3d 子场景
JavaFX 3d SubScene in 2d Scene
我正在为游戏开发 GUI,我想在 JavaFX 中混合 3D SubScene 和 2D Pane。我有一个名为 root3D 的组,其中包含我已正确设置的所有 3d 对象,然后我使用通过 JavaFX Scene Builder 设置的 FXML 文件创建一个 Pane。但是没有任何显示,我只能看到我的 3D 对象。
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-30);
Group root3D = new Group(model1,model2,model3); //various 3d models I imported
SubScene subScene = new SubScene(root3D, 1280, 700, true,SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/file.fxml"));
AnchorPane pane = loader.load();
pane.getChildren().add(subScene);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="700.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane prefHeight="150.0" prefWidth="200.0">
<children>
<ImageView fitHeight="214.0" fitWidth="169.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../Sprite/Cards/Small/podium-characters-Poseidon.png" />
</image>
</ImageView>
<TextField prefHeight="62.0" prefWidth="274.0" text="APOLLO">
<font>
<Font name="Trebuchet MS Bold Italic" size="22.0" />
</font>
</TextField>
</children>
</StackPane>
</children>
</AnchorPane>
编辑:我发现调整子场景的大小,我可以看到 FXML 元素。显然他们正在被子场景"covered"。有谁知道如何将 FXML 元素放在子场景之上而不是像现在这样相反?
正如@Slaw 间接指出的那样,我需要的是设置 Subscene.toBack() 以在我的场景中设置正确的 Z 顺序。非常感谢你!在阅读有关 Subscenes 的内容时,我没有找到任何相关信息,所以我希望这可以帮助将来的一些 3d 初学者。
虽然您已经找到了解决方案,但我发布了一个答案,希望能够更清楚地说明为什么 在您的 toBack()
上调用 toBack()
SubScene
有效。
Z-Order
所有布局继承的每个Parent
可以有一个或多个children。如果两个(或更多)children 在 Parent
中占据相同的 space,那么一个将被渲染在另一个之上。哪个 child 被绘制在另一个之上取决于两件事:
- children在
Parent
的children list中的顺序。
- (自 JavaFX 9 起)每个 child 的
viewOrder
属性 相对于同一 Parent
中的其他 children 的值。
Z-Order in JavaFX 问答更详细。
你的代码中的问题
您的 FXML 文件描述了一个 AnchorPane
,它只有一个 StackPane
child。这意味着以下内容:
AnchorPane pane = loader.load();
给你一个 AnchorPane
,在它的 children 列表中,在第零个索引处有一个 StackPane
。然后你立即添加你的 SubScene
使用:
pane.getChildren().add(subScene);
您使用的 add
方法 将元素追加 到列表的末尾。所以这意味着 AnchorPane
的 children 列表的顺序是:
StackPane
SubScene
由于 SubScene
在 StackPane
之后,前者呈现在后者之上。
你的解决方案
您选择的解决方案是在将 SubScene
添加到 AnchorPane
后调用 toBack()
。这是该方法的 the documentation:
Moves this Node
to the back of its sibling nodes in terms of z-order. This is accomplished by moving this Node
to the first position in its parent's content ObservableList
. This function has no effect if this Node
is not part of a group.
换句话说,调用该方法后 AnchorPane
的 children 列表的顺序变为:
SubScene
StackPane
这就是 SubScene
现在呈现在 StackPane
下方的原因,因为您更改了 children 的顺序。
请注意 toBack()
方法是 Node
class 的一部分,而不是 SubScene
。后者继承于前者。我提出这个问题是为了指出您遇到的问题并非特定于 SubScene
或什至将 2D 和 3D 场景图混合在一起。您的 StackPane
和 SubScene
都是 2D Scene
的一部分(即没有深度缓冲),这意味着它们的 z-order 完全受上面讨论的内容约束 *。 SubScene
是 3D(即启用深度缓冲)这一事实与手头的问题无关;该事实仅影响所述 SubScene
.
的 后代
* 在 3D 场景中,节点的 z-coordinate 变得相关。
其他解决方案
以下是一些可用于解决您的问题的其他方法:
将 SubScene
添加到 AnchorPane
的 children 列表的开头:
pane.getChildren().add(0, subScene);
如果您使用的是 JavaFX 9+,请将 SubScene
的 viewOrder
属性 设置为小于 AnchorPane
的值的 属性(默认情况下,属性 的值为 0
):
subScene.setViewOrder(-1);
在 StackPane
之前的 FXML 文件中定义您的 SubScene
(注意此方法需要使用 an FXML controller):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.Group?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.SceneAntialiasing?>
<?import javafx.scene.SubScene?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="700.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1">
<children>
<!-- Define the SubScene before the StackPane -->
<SubScene fx:id="subScene" width="1280" height="700" depthBuffer="true">
<antiAliasing>
<SceneAntialiasing fx:constant="BALANCED"/>
</antiAliasing>
<!--
Unfortunately, as far as I can tell, you can't set your PerspectiveCamera in FXML because you
want 'fixedEyeAtCameraZero' to be true. That property can only be set during construction but
the constructor with that parameter does not annotate said parameter with @NamedArg, thus the
FXMLLoader can't see it. And the no-arg constructor sets the value to false, not true. This
means you have to inject the SubScene into your controller and add the PerspectiveCamera in
code.
-->
<root>
<!-- Inject the root into the controller in order to add your models to it in code -->
<Group fx:id="root3D"/>
</root>
</SubScene>
<StackPane prefHeight="150.0" prefWidth="200.0">
<children>
<ImageView fitHeight="214.0" fitWidth="169.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../Sprite/Cards/Small/podium-characters-Poseidon.png"/>
</image>
</ImageView>
<TextField prefHeight="62.0" prefWidth="274.0" text="APOLLO">
<font>
<Font name="Trebuchet MS Bold Italic" size="22.0"/>
</font>
</TextField>
</children>
</StackPane>
</children>
</AnchorPane>
我正在为游戏开发 GUI,我想在 JavaFX 中混合 3D SubScene 和 2D Pane。我有一个名为 root3D 的组,其中包含我已正确设置的所有 3d 对象,然后我使用通过 JavaFX Scene Builder 设置的 FXML 文件创建一个 Pane。但是没有任何显示,我只能看到我的 3D 对象。
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-30);
Group root3D = new Group(model1,model2,model3); //various 3d models I imported
SubScene subScene = new SubScene(root3D, 1280, 700, true,SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/file.fxml"));
AnchorPane pane = loader.load();
pane.getChildren().add(subScene);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="700.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane prefHeight="150.0" prefWidth="200.0">
<children>
<ImageView fitHeight="214.0" fitWidth="169.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../Sprite/Cards/Small/podium-characters-Poseidon.png" />
</image>
</ImageView>
<TextField prefHeight="62.0" prefWidth="274.0" text="APOLLO">
<font>
<Font name="Trebuchet MS Bold Italic" size="22.0" />
</font>
</TextField>
</children>
</StackPane>
</children>
</AnchorPane>
编辑:我发现调整子场景的大小,我可以看到 FXML 元素。显然他们正在被子场景"covered"。有谁知道如何将 FXML 元素放在子场景之上而不是像现在这样相反?
正如@Slaw 间接指出的那样,我需要的是设置 Subscene.toBack() 以在我的场景中设置正确的 Z 顺序。非常感谢你!在阅读有关 Subscenes 的内容时,我没有找到任何相关信息,所以我希望这可以帮助将来的一些 3d 初学者。
虽然您已经找到了解决方案,但我发布了一个答案,希望能够更清楚地说明为什么 在您的 toBack()
上调用 toBack()
SubScene
有效。
Z-Order
所有布局继承的每个Parent
可以有一个或多个children。如果两个(或更多)children 在 Parent
中占据相同的 space,那么一个将被渲染在另一个之上。哪个 child 被绘制在另一个之上取决于两件事:
- children在
Parent
的children list中的顺序。 - (自 JavaFX 9 起)每个 child 的
viewOrder
属性 相对于同一Parent
中的其他 children 的值。
Z-Order in JavaFX 问答更详细。
你的代码中的问题
您的 FXML 文件描述了一个 AnchorPane
,它只有一个 StackPane
child。这意味着以下内容:
AnchorPane pane = loader.load();
给你一个 AnchorPane
,在它的 children 列表中,在第零个索引处有一个 StackPane
。然后你立即添加你的 SubScene
使用:
pane.getChildren().add(subScene);
您使用的 add
方法 将元素追加 到列表的末尾。所以这意味着 AnchorPane
的 children 列表的顺序是:
StackPane
SubScene
由于 SubScene
在 StackPane
之后,前者呈现在后者之上。
你的解决方案
您选择的解决方案是在将 SubScene
添加到 AnchorPane
后调用 toBack()
。这是该方法的 the documentation:
Moves this
Node
to the back of its sibling nodes in terms of z-order. This is accomplished by moving thisNode
to the first position in its parent's contentObservableList
. This function has no effect if thisNode
is not part of a group.
换句话说,调用该方法后 AnchorPane
的 children 列表的顺序变为:
SubScene
StackPane
这就是 SubScene
现在呈现在 StackPane
下方的原因,因为您更改了 children 的顺序。
请注意 toBack()
方法是 Node
class 的一部分,而不是 SubScene
。后者继承于前者。我提出这个问题是为了指出您遇到的问题并非特定于 SubScene
或什至将 2D 和 3D 场景图混合在一起。您的 StackPane
和 SubScene
都是 2D Scene
的一部分(即没有深度缓冲),这意味着它们的 z-order 完全受上面讨论的内容约束 *。 SubScene
是 3D(即启用深度缓冲)这一事实与手头的问题无关;该事实仅影响所述 SubScene
.
* 在 3D 场景中,节点的 z-coordinate 变得相关。
其他解决方案
以下是一些可用于解决您的问题的其他方法:
将
SubScene
添加到AnchorPane
的 children 列表的开头:pane.getChildren().add(0, subScene);
如果您使用的是 JavaFX 9+,请将
SubScene
的viewOrder
属性 设置为小于AnchorPane
的值的 属性(默认情况下,属性 的值为0
):subScene.setViewOrder(-1);
在
StackPane
之前的 FXML 文件中定义您的SubScene
(注意此方法需要使用 an FXML controller):<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.TextField?> <?import javafx.scene.Group?> <?import javafx.scene.image.Image?> <?import javafx.scene.image.ImageView?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.StackPane?> <?import javafx.scene.SceneAntialiasing?> <?import javafx.scene.SubScene?> <?import javafx.scene.text.Font?> <AnchorPane prefHeight="700.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"> <children> <!-- Define the SubScene before the StackPane --> <SubScene fx:id="subScene" width="1280" height="700" depthBuffer="true"> <antiAliasing> <SceneAntialiasing fx:constant="BALANCED"/> </antiAliasing> <!-- Unfortunately, as far as I can tell, you can't set your PerspectiveCamera in FXML because you want 'fixedEyeAtCameraZero' to be true. That property can only be set during construction but the constructor with that parameter does not annotate said parameter with @NamedArg, thus the FXMLLoader can't see it. And the no-arg constructor sets the value to false, not true. This means you have to inject the SubScene into your controller and add the PerspectiveCamera in code. --> <root> <!-- Inject the root into the controller in order to add your models to it in code --> <Group fx:id="root3D"/> </root> </SubScene> <StackPane prefHeight="150.0" prefWidth="200.0"> <children> <ImageView fitHeight="214.0" fitWidth="169.0" pickOnBounds="true" preserveRatio="true"> <image> <Image url="@../Sprite/Cards/Small/podium-characters-Poseidon.png"/> </image> </ImageView> <TextField prefHeight="62.0" prefWidth="274.0" text="APOLLO"> <font> <Font name="Trebuchet MS Bold Italic" size="22.0"/> </font> </TextField> </children> </StackPane> </children> </AnchorPane>