没有 rolling/banking 的 Unity 相机 Quaternion.RotateTowards

Unity Camera Quaternion.RotateTowards without rolling/banking

我有一个面向地面的相机,我想向上平移以查看远处的目标 object。

目前,我通过以下方式实现了这一点:

Vector3 dir = targetPoint - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
transform.rotation = newRotation;

相机执行旋转并最终正确指向目标 object,但当相机向上平移时它会向一侧倾斜,使我的游戏世界与观看者成一定角度,这很漂亮迷失方向:

如何以某种方式限制相机角度,使 horizon 始终与相机保持平直?

谢谢!

更新

添加下面@Isaac 建议的行会产生相对于 horizon 的正确旋转,但它在开始时突然捕捉到 z=0,这仍然不是我正在寻找的。

transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, 0);

经过实验,我根据您的需要找到了 2 种可能的解决方案。

如果您只是想跟随目标,我建议您使用 LookAt,它会自动与世界对齐。在您的代码中(在更新中)transform.LookAt(dir);.

如果 need/want 平移效果在更新旋转后设置 localEulerAngles。这就是我所做的工作:

//this is your code
    Vector3 dir = targetPoint - transform.position;
    Quaternion lookRotation = Quaternion.LookRotation(dir);
    Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
    transform.rotation = newRotation;

//this is what I added
    transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, 0);

在使用四元数更新旋转并将 z 旋转设置为零后,添加简单地采用相机所面对的方式。

如果您有任何问题,请告诉我:)

////////////////////////////////////////// //////////////////////////////////////////////// /////////////////////////////////////////////////

新 information/edits:

我相信我已经找到了解决方案,但它很丑陋,我希望得到有关它是否卡顿等方面的反馈。

此代码与以前基本相同,但现在它会检查 z 角度并使用我称为 zDamping 的变量更手动地对其进行编辑,该变量会影响相机围绕 z 旋转的速度仅限访问。

我在更新之外添加了:

public float zDamping; //public only for testing, it's convenient for finding an optimal value
private bool rotationCheck = false;

然后在里面 update():

//This is your code (unchanged)
        Vector3 targetPoint = target.transform.position;
        Vector3 dir = targetPoint - transform.position;
        Quaternion lookRotation = Quaternion.LookRotation (dir);
        Quaternion newRotation = Quaternion.RotateTowards (transform.rotation, lookRotation, rotationDamping * Time.deltaTime);

//This is what is new (remove my addition from before edits or it won't work)
        if (transform.localEulerAngles.z >= 180f && transform.localEulerAngles.z <= 359f && !rotationCheck) {
            transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, transform.localEulerAngles.z + (rotationDamping * zDamping));
            transform.rotation = newRotation;
        } 
        else if (transform.localEulerAngles.z <= -180f && transform.localEulerAngles.z >= 1f && !rotationCheck) {
            transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, transform.localEulerAngles.z - (rotationDamping * zDamping));
            transform.rotation = newRotation;
        }
        else {
            transform.rotation = newRotation;
            transform.localEulerAngles = new Vector3 (transform.localEulerAngles.x, transform.localEulerAngles.y, 0);
            rotationCheck = true;
        }

正如我所说,这个解决方案非常丑陋,但它可能会奏效。您必须查看哪些 zDamping 值能让您的速度看起来自然(我建议从 .01 开始)。一旦你接近该值,也会有一个小的 "jump",但是你越接近 359f 到 360 和 1f 到 0,跳跃就会越小。将它设置得太小的危险是如果你过冲,但即使它过冲它也应该工作,但它会花费少量时间。

测试一下,让我知道你的想法,抱歉,我现在找不到更优雅的东西。我还尝试添加一个单独的四元数来专门旋转 z 轴,但它没有用;随意尝试一下,如果你愿意,我可以提供更多关于我所做的细节。

再次祝你好运,对于草率的解决方案感到抱歉。

有一篇关于这个主题的优秀文章Q/A on gamedev.stackexchange。您应该尝试那里建议的 pitch/yaw 系统。
另一个建议是在旋转过程中校正相机的滚动。

public float rollCorrectionSpeed; 
public void Update()
{
    float roll = Vector3.Dot(transform.right, Vector3.up);
    transform.Rotate(0, 0, -roll * rollCorrectionSpeed);

    Vector3 dir = targetPoint.position - transform.position;
    Quaternion lookRotation = Quaternion.LookRotation(dir);
    Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
    transform.rotation = newRotation;
} 

编辑:
有一个更简单的解决方案:只需将要旋转的四元数的 z 旋转保持为 0。

public void Update()
{
    Vector3 angles = transform.rotation.eulerAngles;
    Quaternion from = Quaternion.Euler(angles.x, angles.y, 0);

    Vector3 dir = targetPoint.position - transform.position;        
    Quaternion to = Quaternion.LookRotation(dir);

    transform.rotation = Quaternion.RotateTowards(from, to, rotationDamping * Time.deltaTime);
}

在您的行中添加了代码,希望它能解决问题。

Vector3 dir = targetPoint - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Quaternion newRotation = Quaternion.RotateTowards(transform.rotation, lookRotation, rotationDamping * Time.deltaTime);
newRotation.eulerAngles = new Vector3(newRotation.eulerAngles.x,newRotation.eulerAngles.y,transform.rotation.eulerAngles.z);
transform.rotation = newRotation;