有没有一种方法可以使用Quaternion.FromToRotation支持使用一个轴?

时间:2020-02-07 10:41:13

标签: c# unity3d quaternions

我已经为这个问题苦苦挣扎了好几个小时,现在该是我寻求帮助的时候了。

情况

我走动时有一个移动的角色,并与它行走的表面对齐。我通过射线投射到将要在其下的下一个曲面并使用

获取与该曲面对齐所需的旋转来完成此操作
Quaternion newRotation = Quaternion.FromToRotation(transform.up, foundSurface.normal) * transform.rotation;

角色是一个名为Modelholder的变换,可通过以下方式平滑旋转到新的旋转角度

modelHolder.rotation = Quaternion.Slerp(modelHolder.rotation, newRotation, Time.deltaTime * modelRotationSmoothing);

模型内部是模型。我根据鼠标下巴的运动来更改model.localRotation.y。

相机只是跟随模型持有者的变换,还有一个称为Rotator的子变换,该子变换也会根据鼠标的移动(这次是颚和俯仰)进行旋转。相机是此Rotator变换的子代。

有了这个,我就可以在步行和天花板上行走,同时可以使所有东西正确对齐,整齐美观。

问题

我做了一个抓钩,将角色移到抓钩的表面。飞行结束时的重新对齐与步行对齐相同。除了从地面到天花板抓地(反之亦然)时,这一切都很好。当我向东或向西看时,该模型似乎进行了“滚转”以重新对准新曲面,但在向北或向南看时,将进行后翻转或前翻转。

ModelHolder运动: https://streamable.com/qf94k

模型运动: https://streamable.com/4xkl4

桶状滚子很好,但我想以某种方式找到一种旋转方式,以使玩家着陆后不会朝相反的方向看(或者在向北或向南看时从天花板上跳下来,因为然后重新对准重力也会引起翻转),因为翻转会使人迷失方向。

我尝试过的事情:

  • 我试图在modelHolder和model中没有分离。这并不能解决问题,只会给我带来更多问题,那就是拥有平滑但反应灵敏的相机。

  • 我尝试在重新对齐之前保存外观方向,并在重新对齐时将其旋转到旧的外观方向。 这只是做一个超级怪异的翻转和翻转事情,这更加令人迷惑。

  • 我尝试尝试检测新旧旋转之间的差异,以查看我是否可以某种方式“检测”它何时要进行翻转以及何时要进行滚桶运动,以便对此进行应对。我只发现困惑和沮丧。

1 个答案:

答案 0 :(得分:2)

您需要计算一个newRotation,以使局部向上为表面的法线,并尽可能保持正向。

FromToRotation仅保证使用时的对齐方式。相反,您可以使用叉积和Quaternion.LookRotation进行所需的计算。

Vector3 newPlayerRight = Vector3.Cross(foundSurface.normal, modelHolder.forward);
Vector3 newPlayerForward = Vector3.Cross(newPlayerRight, foundSurface.normal);

Quaternion newRotation = Quaternion.LookRotation(newPlayerForward, foundSurface.normal);

然后,您可以像以前一样继续进行操作:

modelHolder.rotation = Quaternion.Slerp(modelHolder.rotation, newRotation, 
        Time.deltaTime * modelRotationSmoothing);

尽管我不赞成将Slerp / Lerp方法与无法保证达到或超过1的t一起使用,但我建议使用Quaternion.RotateTowards

float modelRotationSpeed = 180f;
modelHolder.rotation = Quaternion.RotateTowards(modelHolder.rotation, newRotation, 
        Time.deltaTime * modelRotationSpeed);

为了保持相对于刚经过的边缘的前倾角,可以尝试使用其他方法:

Quaternion newRotation;

// ..

Vector3 previousSurfaceNormal = modelHolder.up;
Vector3 previousForward = modelHolder.forward;
bool flyingOrFallingToNewSurface;

if (flyingOrFallingToNewSurface)
{
    Vector3 newPlayerRight = Vector3.Cross(foundSurface.normal, modelHolder.forward);
    Vector3 newPlayerForward = Vector3.Cross(newPlayerRight, foundSurface.normal);

    newRotation = Quaternion.LookRotation(newPlayerForward, foundSurface.normal);
} else
{
    // This direction lies in both surfaces. 
    Vector3 edgeTraversed = Vector3.Cross(previousSurfaceNormal, foundSurface.normal);

    // Find the angle from edgeTraversed to previousForward
    float ang = Vector3.SignedAngle(edgeTraversed, previousForward, previousSurfaceNormal);

    // Find newForward in new plane that's the same angle
    Vector3 newPlayerForward = Quaternion.AngleAxis(ang,foundSurface.normal) * edgeTraversed;

    newRotation = Quaternion.LookRotation(newPlayerForward, foundSurface.normal);
}

// ...

float modelRotationSpeed = 180f;
modelHolder.rotation = Quaternion.RotateTowards(modelHolder.rotation, newRotation, 
        Time.deltaTime * modelRotationSpeed);