拖动 3D 杠杆(基于方向和视角)

Dragging a 3D lever (based on orientation & viewing angle)

本文关键字:方向 于方向 3D 杠杆 拖动      更新时间:2023-10-16

在我的项目(c++/UE4)中,我有一个杠杆网格从地板伸出来。按住此杠杆上的鼠标左键并移动鼠标将启动拖动操作。此拖动操作负责计算2D增量鼠标移动,并利用此数据旋转杠杆*在本地空间*,它只能在单个轴上旋转(负或正,但仍然只有一个轴)。

但是,如果我不是在杠杆前面,而是在杠杆后面呢?如果我在它的一边呢?如果杠杆实际上是伸出墙壁而不是地板呢?我如何使它,使鼠标移动实际上旋转杠杆适当的角度,在它被观看,不管杠杆的方向?


为了进一步解释我自己…
下面是一个场景列表,以及我希望鼠标如何控制它们:

当杠杆在地板上并且你在它前面时:

  • 如果你向上移动鼠标(-Y),它应该旋转远离相机
  • 如果你向下移动鼠标(+Y),它应该向摄像机方向旋转

当杠杆在地板上而你在它后面时:

  • 如果你向上移动鼠标(-Y),它应该旋转远离摄像机
    (这与你在它前面的世界空间方向相反)
  • 如果你向下移动鼠标(+Y),它应该向摄像机方向旋转
    (这与你在它前面的世界空间方向相反)

当杠杆在地板上而你在它旁边时:

  • 如果你向左移动鼠标(-X),它应该旋转到相机的左边
    (这是相反的方向,当你在它的另一边)
  • 如果你向右移动鼠标(+X),它应该旋转到相机的右边
    (这是相反的方向,当你在它的另一边)

当杠杆在墙上而你在它前面时:

  • 如果你向上移动鼠标,它应该向上旋转(朝向天空)
  • 如果你向下移动鼠标,它应该向下旋转(朝向地板)

当杠杆在墙上而你在它旁边时:

  • 就像它在墙上,你在它前面一样

请注意,如果它有帮助的话,UE4确实有内置的2D/3D矢量数学功能,以及简单的方法来投影和解投影坐标到/从3D世界或2D屏幕。因此,我总是知道鼠标位置的确切世界空间和屏幕空间坐标,杠杆的枢轴(基础)位置,杠杆的手柄(顶部)位置,以及鼠标每帧移动的量(增量)。

获取杠杆的枢轴(它周围的点旋转),并将其投影到屏幕坐标上。然后,当你第一次点击时,你存储点击的坐标。

现在,当你需要知道旋转的方式时,你计算向量枢轴到第一次点击和向量枢轴到当前位置之间的点积(你应该在点积之前规范化向量)。这给了你鼠标移动的cos(角度),你可以使用它(取arccos(值)来获得角度)在3d中移动杠杆。因为屏幕上的角度与投影角度不一样,所以它会有点不稳定,但它更容易控制这种方式(如果你移动鼠标90度,即使它们没有正确对齐,杠杆也会移动90度)。试玩一下设置,看看哪个最适合你的项目。

另一种方法是:当你第一次点击时,你将杠杆末端的点(或者你点击杠杆的点)存储在3d空间中。你可以使用相机投影平面在3d中移动点(你可以使用相机向上向量,在你使它与相机视图方向正交之后,然后使用视图方向交叉向上向量来获得正确的方向)。你将鼠标的增量移动应用到点上,然后将其投影到旋转平面上,并移动杠杆使点与投影的点对齐(数学原理与上面的类似,只是使用3d点而不是屏幕投影)。

注意:如果相机非常靠近旋转平面,因为它并不总是清楚杠杆应该向前或向后移动,因此此操作不能很好地工作。

我不是unreal-engine4的专家(只是自己学习),但所有这些都是基本的矢量数学,应该得到很好的支持。查看维基百科上的点积和叉积,因为它们对这些技巧非常有用。

这里有一个方法:

  1. 当用户点击杠杆时,假设有一个平面穿过杠杆的枢轴,其法线与相机到枢轴的方向相同。计算光标光线与该平面的交点

    FVector rayOrigin;
    FVector rayDirection;
    FVector cameraPosition;
    FVector leverPivotPosition;
    FVector planeNormal = (leverPivotPosition-cameraPosition).GetSafeNormal(0.0001f);
    float t = ((leverPivotPosition - rayOrigin) | planeNormal) / (planeNormal | rayDirection);
    FVector planeHitPosition = rayOrigin + rayDirection * t;
    
  2. 将其标量投影到杠杆的局部上/下轴上。让我们假设它是本地上下轴:

    FVector leverLocalUpDirectionNormalized;
    float scalarPosition = planeHitPosition | leverLocalUpDirectionNormalized;
    
  3. 然后,在按下杠杆的每一帧中,计算该帧的新scalarPosition。当scalarPosition在帧间增加时,杠杆应该旋转,使其向杠杆的上方移动。当它在帧间减小时,杠杆应该朝杠杆的下端旋转。