【问题标题】:Quaternion.Slerp on X and Z axis without Y axisQuaternion.Slerp 在 X 和 Z 轴上,没有 Y 轴
【发布时间】:2019-07-06 04:31:09
【问题描述】:

我正在尝试围绕 X、Y 和 Z 轴旋转播放器。 Y 轴不应从最后一个角度移动。例如,如果我向左旋转 45 度,播放器不应该旋转回 0。播放器 X 和 Z 轴最多旋转 30 度,然后当 Input 不再使用时,设置为 0。

通过反复试验,我终于让我的 Y 角不 Slerp 回到 0。但是,X 和 Z,仍然认为 Y 是 0 度。玩家旋转(假设向左 45 度),但沿 X 和 Z 移动就像 Y 是 0 度。

我一直在阅读文章和主题,并观看多个领域的视频,包括但不限于 StackOverflow、Unity 论坛、Unity API 和 YouTube 视频。

Video of Current Game - 注意引擎排气 - X 和 Z 永远不会更改为摄像机视图/玩家 Y 方向的新法线。

        void Update()
    {
        if(!controller.isGrounded)
        {

            //Three degree's
            moveDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Thrust"), Input.GetAxis("Vertical"));
            moveDirection *= speed;

            //rotate around Y-Axis
            transform.Rotate(0, Input.GetAxis("Yaw") * rotationSpeed, 0);
            float currentY = transform.eulerAngles.y; //save Y for later

            //rotation around X and Z
            float tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
            float tiltAroundZ = -1 * (Input.GetAxis("Horizontal") * tiltAngle);

            Quaternion targetRotation = Quaternion.Euler(tiltAroundX, currentY, tiltAroundZ);

            Vector3 finalRotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth).eulerAngles;
            finalRotation.y = currentY; //reintroduce Y
            transform.rotation = Quaternion.Euler(finalRotation);

        controller.Move(moveDirection * Time.deltaTime);
    }

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    经过进一步研究,引导我走上不同的道路,我发现存在两个问题。这两个问题都围绕着这样一个事实,即 Z 轴在旋转后从未被标准化为新的 Y 轴度数。 @Ruzihm,解决了旋转问题。我解决了当时可见的运动问题。一旦旋转正常工作,就会很容易看到。

    本质上,在 Y 轴旋转 (Vector3.up) 发生任何变化后,必须重新计算 Z 轴 (transform.forward)。一旦你有了新的法线(transform.forward),运动矢量需要平展到平面上,以防止玩家潜入世界表面。感谢@Ruzihm 的所有帮助。

    这是新代码:

    //Three degree's
    moveDirection = new Vector3(Input.GetAxis("Horizontal"),
                                Input.GetAxis("Thrust"),
                                Input.GetAxis("Vertical"));
    
    //Normalize the movement direction and flatten the Plane
    moveDirection = transform.TransformDirection(moveDirection);
    moveDirection = Vector3.ProjectOnPlane(moveDirection, Vector3.up);
    
    moveDirection *= speed;
    
    // collect inputs
    float yaw = Input.GetAxis("Yaw") * rotationSpeed;
    float pitch = Input.GetAxis("Vertical") * tiltAngle;
    float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
    
    // Get current forward direction projected to plane normal to up (horizontal plane)
    Vector3 forwardCurrent = transform.forward
                            - Vector3.Dot(transform.forward, Vector3.up) * Vector3.up;
    // Debug to view forwardCurrent
    Debug.DrawRay(transform.position, forwardCurrent * 2, Color.white);
    
    // create rotation based on forward
    Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
    
    // rotate based on yaw, then pitch, then roll. 
    // This order prevents changes to the projected forward direction
    
    targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
    
    
    // Debug to see forward after applying yaw
    Debug.DrawRay(transform.position, targetRotation * Vector3.forward, Color.red);
    
    targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
    targetRotation = targetRotation * Quaternion.AngleAxis(roll, Vector3.forward);
    
    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
    
    
    controller.Move(moveDirection * Time.deltaTime);
    

    【讨论】:

    • 很高兴我能帮上忙! :)
    【解决方案2】:

    在使用欧拉角时,似乎有一些关于旋转顺序的错误假设。应用滚动,然后俯仰,最后偏航。这意味着保持相同的偏航然后将横滚和俯仰设置为零(甚至只是改变横滚)可以完全改变你所面对的扁平方向。

    通过偏航旋转可能会有所帮助,将前向变平(也就是将其投影到完全水平的平面)然后基于该旋转创建一个旋转(使用Quaternion.LookRotation),然后您可以手动旋转每个轴。

    if(!controller.isGrounded)
    {
    
        //Three degree's
        moveDirection = new Vector3(Input.GetAxis("Horizontal"), 
                                    Input.GetAxis("Thrust"), 
                                    Input.GetAxis("Vertical"));
        moveDirection *= speed;
    
        // collect inputs
        float yaw = Input.GetAxis("Yaw") * rotationSpeed;
        float pitch = Input.GetAxis("Vertical") * tiltAngle;
        float roll = -1 * (Input.GetAxis("Horizontal") * tiltAngle);
    
        // Get current forward direction projected to plane normal to up (horizontal plane)
        Vector3 forwardCurrent = transform.forward 
                                - Vector3.Dot(transform.forward,Vector3.up) * Vector3.up;
    
        // Debug to view forwardCurrent
        Debug.DrawRay(transform.location, forwardCurrent, Color.white, 0f, false);
    
        // create rotation based on forward
        Quaternion targetRotation = Quaternion.LookRotation(forwardCurrent);
    
        // rotate based on yaw, then pitch, then roll. 
        // This order prevents changes to the projected forward direction
    
        targetRotation = targetRotation * Quaternion.AngleAxis(yaw, Vector3.up);
    
    
        // Debug to see forward after applying yaw
        Debug.DrawRay(transform.location, targetRotation * Vector3.forward, Color.red, 0f, false);
    
        targetRotation = targetRotation * Quaternion.AngleAxis(pitch, Vector3.right);
        targetRotation = targetRotation  * Quaternion.AngleAxis(roll, Vector3.forward);
    
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, smooth);
    
        //debug new forward/up
        Debug.DrawRay(transform.location, Transform.forward, Color.blue, 0f, false);
        Debug.DrawRay(transform.location, Transform.up, Color.green, 0f, false);
    
        controller.Move(moveDirection * Time.deltaTime);
    }
    

    这可能被视为部分答案,因为能够确定“扁平的向前”方向并重新排序应用组件旋转的过程对于回答您的问题很有用,但可能不足以获得您想要的全部效果,具体取决于详情。

    作为旁注,如果您想确保它实际到达目标旋转而不是无限接近它,您可能需要考虑使用Quaternion.RotateTowards 而不是Quaternion.Slerp

    【讨论】:

    • 感谢您的详细回复。提供的信息非常有见地。不幸的是,使用您提供的代码并没有改变结果。我正在尝试以图形方式解释问题和期望的结果。我可以制作一个非常短的视频,但不确定是否可以将其嵌入此处。
    • @LeePaulison 如果您可以将视频放在任何视频托管网站上,那么您可以内联/嵌入链接。不幸的是,2019 年 Stackoverflow 上的视频支持不佳。也许在 2030 年。
    • 我已经编辑了原始帖子以包含图片和视频(发布到 YouTube)。我希望他们提供足够的信息。
    • @LeePaulison 我更改了代码以将偏航应用于扁平的向前方向,并添加一些调试光线。让我知道这是否解决了问题,但如果没有,您能否通过视频显示绘制的白色和红色光线发表评论?
    • @Ruzihm 感谢您到目前为止的帮助。我必须对代码进行一些小的更改,transform.location -> transform.position,从光线中删除持续时间,并翻转 Vector3.forward * targetRotation -> targetRotation * Vector3.forward 的顺序。 Unity 不会乘以向量 * 四元数,只会乘以四元数 * 向量。这是显示光线的新视频。红光和白光永远不变。 youtu.be/QRudvMy77gs
    猜你喜欢
    • 1970-01-01
    • 2022-09-28
    • 2021-07-12
    • 2021-06-11
    • 2014-05-06
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多