将 3D 对象鼠标拖动移动限制到 JavaFX 中的平面 [英] Restricting a 3D object mouse drag movement to a plane in JavaFX

查看:23
本文介绍了将 3D 对象鼠标拖动移动限制到 JavaFX 中的平面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 JavaFX 通过鼠标拖动来移动 3D 立方体.立方体应位于 x 轴和 z 轴跨越的平面上.我的解决方案效果很好,但是如果我用鼠标移动立方体太快或者当它遇到具有特定深度(y 轴)的对象时,则假定鼠标在 y 轴和立方体上移动开始向前或向后跳跃.有没有办法将鼠标限制在 xz 平面上?一个更复杂的解决方案是将 y 长度投影回 xz 平面,但我不知道如何.我查看了 JavaFX 移动 3D 对象,但无法适应我的情况.

I'm using JavaFX to move 3D cubes around by mouse drag. The cube should stay on the plane spanned by x and z axis. My solution works fairly well, however if I move the cube too fast with my mouse or when it encounters an object with a certain depth (y-Axis), it is assumed, that the mouse is moving on the y-Axis and the cube starts jumping forward or backwards. Is there a way to restrict the mouse to the xz-plane? A more complicated solution would be projecting the y length back to the xz-plane, but I got no clue how. I looked at JavaFX Moving 3D Objects, but couldn't adapt it to my case.

到目前为止我的代码:

    private volatile double relMousePosX;
    private volatile double relMousePosZ;

    public void enableDragMove(PaneBox paneBox) {
        Group paneBoxGroup = paneBox.get();

        paneBoxGroup.setOnMousePressed((MouseEvent me) -> {
            if(isSelected(paneBox) && MouseButton.PRIMARY.equals(me.getButton())) {
                relMousePosX = me.getX();
                relMousePosZ = me.getZ();
            }
        });

        paneBoxGroup.setOnMouseDragged((MouseEvent me) -> {
            if(paneBoxGroup.focusedProperty().get() && MouseButton.PRIMARY.equals(me.getButton())) {
                setDragInProgress(paneBox, true);
                System.out.println(me.getY()); // should stay small value, but jumps to higher values at times, creating the problem.
                paneBoxGroup.setCursor(Cursor.MOVE);
                paneBox.setTranslateX(paneBox.getTranslateX() + (me.getX() - relMousePosX));
                paneBox.setTranslateZ(paneBox.getTranslateZ() + (me.getZ() - relMousePosZ));
            }
        });

        paneBoxGroup.setOnMouseReleased((MouseEvent me) -> {
            if(paneBoxGroup.focusedProperty().get() && MouseButton.PRIMARY.equals(me.getButton())) {
                setDragInProgress(paneBox, false);
                paneBoxGroup.setCursor(Cursor.DEFAULT);
            }
        });
   }

推荐答案

正如您所提到的,3D 拖动有两种可能的方法:

As you have mentioned, there two possible approachs for 3D dragging:

  • 纯拖拽放下,就像你提议的
  • 取消投影方向,例如这个问题.

对于拖放事件,我们可以在事件开始和结束时检测 3D 形状上的拖动事件.

For drag and drop events, we can detect a drag event on the 3D shape, when the event starts and finishes.

这里的技巧是,不要监听形状上拖动事件之间的中间事件,而是监听可能目标上的拖动事件.

The trick here is, instead of listening to the in between dragging events on the shape, listen to drag over events on a possible target.

通过定义一个目标,您可以轻松限制形状移动,因为您只会在仅将其拖过该目标时更新其位置.

By defining a target, you can restrict easily the shape movements, since you will only update its position when you are dragging it only over this target.

所以如果你想限制平面上的移动,你可以使用Rectangle作为目标.

So if you want to restrict movements on a plane, you can use a Rectangle as the target.

此代码段展示了这种方法的工作原理:

This snippet shows how this approach works:

@Override 
public void start(Stage stage) {
    final PerspectiveCamera cam = new PerspectiveCamera();
    cam.setFieldOfView(20);
    cam.setFarClip(10000);
    cam.setNearClip(0.01);
    cam.getTransforms().addAll(new Rotate(60,Rotate.X_AXIS),new Translate(-200,-200,300));

    final Group root = new Group();

    final Box floor = new Box(500, 500, 1);
    floor.setTranslateX(200);
    floor.setTranslateY(200);
    floor.setTranslateZ(50);
    floor.setMaterial(new PhongMaterial(Color.YELLOW));
    root.getChildren().add(floor);

    final Box box = new Box(50, 50, 50);
    box.setMaterial(new PhongMaterial(Color.RED));
    root.getChildren().add(box);

    final Rectangle rectangle = new Rectangle(400, 400, Color.TRANSPARENT);
    rectangle.setMouseTransparent(true);
    rectangle.setDepthTest(DepthTest.DISABLE);
    root.getChildren().add(rectangle);

    // D&D starts
    box.setOnDragDetected((MouseEvent event)-> {
        box.setMouseTransparent(true);
        rectangle.setMouseTransparent(false);
        box.setCursor(Cursor.MOVE);
        box.startFullDrag();
    });

    // D&D ends
    box.setOnMouseReleased((MouseEvent event)-> {
        box.setMouseTransparent(false);
        rectangle.setMouseTransparent(true);
        box.setCursor(Cursor.DEFAULT);
    });

    // While D&D, only confined to the rectangle
    rectangle.setOnMouseDragOver((MouseDragEvent event)-> {
        Point3D coords = event.getPickResult().getIntersectedPoint();
        coords = rectangle.localToParent(coords);
        box.setTranslateX(coords.getX());
        box.setTranslateY(coords.getY());
        box.setTranslateZ(coords.getZ());
    });

    final Scene scene = new Scene(root, 800, 600, true);
    scene.setCamera(cam);

    stage.setScene(scene);
    stage.setTitle("JavaFX 3D Drag&Drop");
    stage.show();
}

如果你运行它,你会看到你可以选择盒子并将它拖到地板上.

If you run it, you will see that you can pick the box and drag it all over the floor.

在这张图片中,我为矩形添加了一些颜色和描边,以查看拖动的限制.

In this picture, I've added some color and stroke to the rectangle to see the limits of dragging.

还要注意盒子和矩形中鼠标透明的变化.矩形仅在拖动过程中不是鼠标透明的.

Note also the changes in mouse transparent both in box and rectangle. The rectangle is not mouse transparent only during the dragging.

这篇关于将 3D 对象鼠标拖动移动限制到 JavaFX 中的平面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆