如何在3D场景中选择2D节点?

时间:2016-10-14 16:54:17

标签: javafx event-handling javafx-2 javafx-8 depth-buffer

这是我的代码。您可以复制粘贴并按照我下面写的内容自行查看问题。

public class MyApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        Scene scene = new Scene(new MyView(), 100, 150);
        stage.setScene(scene);
        stage.show();
    }

    private class MyView extends BorderPane {

        MyView() {

            GridPane board = new GridPane();
            int size = 3;
            for (int i = 0; i < size*size; i++) {
                BorderPane pane = new BorderPane();
                pane.setMinSize(30, 30);
                pane.setBackground(new Background(new BackgroundFill(Color.RED, null, null)));
                pane.setBorder(new Border(new BorderStroke(null, BorderStrokeStyle.SOLID,
                                                           null, null, null)));
                pane.setOnMousePressed(e -> {
                    PickResult pick = e.getPickResult();
                    Pane selectedNode = (Pane) pick.getIntersectedNode();
                    selectedNode.setBackground(new Background(new BackgroundFill(Color.GREEN, null, null)));
                });
                board.add(pane, i / size, i % size);
            }
            Box box = new Box(20d, 20d, 20d);
            BorderPane boardPane = new BorderPane(box, null, null, board, null);
            Group root = new Group(boardPane);

            SubScene scene = new SubScene(root, USE_PREF_SIZE, USE_PREF_SIZE, true, SceneAntialiasing.BALANCED);
            scene.widthProperty().bind(widthProperty());
            scene.heightProperty().bind(heightProperty());

            setCenter(scene);
        }
    }

    public static void main(String[] args) throws Exception {

        launch(args);
    }
}

我创建了一个带有正方形网格的子场景。当我按下一个正方形时,我希望它的背景能够改变颜色。这适用于两种情况:

  1. 如果我没有将Box添加到boardPane
  2. 如果我没有使用深度缓冲区设置场景
  3. 或两者兼而有之。但如果我同时添加框并设置深度缓冲区,则方块不会收到该事件。相反,boardPane收到它。我猜它与3D场景中的2D节点有关。

    我尝试设置这些方法的组合:setPickOnBoundssetDepthTestsetMouseTransparent但没有任何效果。

    解决方案是什么?

1 个答案:

答案 0 :(得分:1)

一旦有了3D场景,就没有二维节点这样的东西,场景图中的所有东西都有x,y和z坐标;甚至当深度缓冲区设置为false时,之前被视为2D节点的东西。

当您在根边框窗格中放置一个框时,该根边框窗格将采用该框的3D坐标。对于拾取目的,您定义的框在z坐标-10到10的3D空间中表示,因此根边界窗格被定义为-10以用于拾取目的。您放置在根边框窗格内的边框窗格没有为它们定义z坐标,因此它们最终为z坐标0,从查看器角度来看,它位于根边框窗格的后面。

因此,根边界窗格正在接收点击,但由于它现在位于与其余内容不同的z平面上,因此您定义的其他2D方形内容不会获得点击。有人可能认为根边框窗格根本没有渲染,因为它没有颜色,所以它应该被视为透明,点击只是通过子节点,但似乎不是3D拾取算法的方式JavaFX可以工作。

对于您的示例,要使所有内容都在同一Z平面中并且选择正常工作,请在for循环中添加以下行:pane.setTranslateZ(-10);

注意:我通过在源代码中添加以下行来调试它(向我报告了鼠标点击的目标以及每次点击的x,y,z选择结果坐标):

root.setOnMouseClicked(System.out::println);

我的建议是避免使用专为2D目的设计的布局窗格(例如边框窗格)来尝试在3D空间中布局元素。 JavaFX布局窗格实际上仅用于2D布局。要处理3D空间中的定位,您应该自己管理坐标,只需使用Group而不是从Pane派生的任何内容。至少不要尝试将3D元素添加到2D布局窗格中,因为结果可能会令人困惑(正如您所发现的那样)。

您可以通过将2D和3D项目放在不同的子场景中来进一步分离它们(这就是我认为JavaFX中的intention of the sub scene概念)。以下答案中显示了具有多个子场景的应用程序示例:How to create custom 3d model in JavaFX 8?