【问题标题】:How to select a 2D node in a 3D scene?如何在 3D 场景中选择 2D 节点?
【发布时间】:2023-04-01 12:21:01
【问题描述】:

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

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. 如果我不使用深度缓冲区设置场景

或两者兼而有之。但是,如果我同时添加框并设置深度缓冲区,则方块不会接收到事件。取而代之的是 boardPane 接收它。我想这与 3D 场景中的 2D 节点有关。

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

解决办法是什么?

【问题讨论】:

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


    【解决方案1】:

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

    当您在根边框窗格中放置一个框时,该根边框窗格将采用该框的 3D 坐标。出于拾取目的,您定义的框在 3D 空间中从 z 坐标 -10 到 10 表示,因此根边框窗格定义为 -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 空间中的定位,您应该自己管理坐标,只使用组而不是从窗格派生的任何东西。至少不要尝试将 3D 元素添加到 2D 布局窗格中,因为结果可能会令人困惑(正如您所发现的那样)。

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

    【讨论】:

    • 谢谢。我认为有一种方法可以使根窗格不接收事件,但让子窗格使用pickOnBoundssetMouseTransparent 之类的东西接收它们。确实,真正的解决方案似乎是您所说的不要混合 2D 和 3D。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 2020-10-06
    • 1970-01-01
    • 2016-12-18
    相关资源
    最近更新 更多