Search code examples
c#unity-game-enginequaternions

Unity3D Adjusting CUBE "endRotation" on right ( x or z) axis


I am new in Unity3D scripting especially when it comes to specifics of Transform, Quaternions, Vector3. Down there is a script. When I roll the cube it flips in the right direction. However when cubes' local axis doesn't match the world axis the cube's endRotation is performed on the wrong axis. Can anybody help me to resolve this issue : "endRotation" would flip the cube on right axis regardless of the relation between LOCAL axis <=> WorldAxis. I was trying to resolve it for a week. Of cause no success. Further down there code and video.

      if (Input.GetAxis("Horizontal") > buttonDown)
        {
            StartCoroutine(FlipTheCube(Vector3.right));
            return
        }...

. . .

    public IEnumerator FlipTheCube(Vector3 direction)
{
    startFliping = false;

    float rollStartTime = 0;
    float rollAngle = 90;
    float halfWidth = transform.localScale.z / 2;
    Vector3 pointAround = transform.position + (Vector3.down * halfWidth) + (direction * halfWidth);
    Vector3 rollAxis = Vector3.Cross(Vector3.up, direction);

    Quaternion rotation = transform.rotation;
    Quaternion **endRotation** = rotation * Quaternion.Euler(rollAxis * rollAngle);
    Vector3 endPlacement = transform.position + direction;

    float oldAngle = 0;

    while (rollStartTime < rollDurtnTime)
    {
        yield return new WaitForEndOfFrame();
        rollStartTime += Time.deltaTime;
        float newAngle = (rollStartTime / rollDurtnTime) * rollAngle;
        float rotateThrough = newAngle - oldAngle;
        oldAngle = newAngle;

        transform.RotateAround(pointAround, rollAxis, rotateThrough);
    }

    transform.position = endPlacement;
    transform.rotation = **endRotation**;

    startFliping = true;
}

Here is a link to the youtube video First part of video If cubes local axis is matching world axis the "endRotation" is flipping correctly Second part of video as soon cubes local axis is not matching world axis "endRotation" goes wrong.


Solution

  • Your problems lie within these lines:

    Vector3 rollAxis = Vector3.Cross(Vector3.up, direction);
    
    Quaternion rotation = transform.rotation;
    Quaternion endRotation = rotation * Quaternion.Euler(rollAxis * rollAngle);
    
    // ...
    
        transform.RotateAround(pointAround, rollAxis, rotateThrough);
    

    First problem is Euler vs angle/axis usage.

    If you've only been rotating against Vector3.right, Vector3.forward, and their negatives you might not have even noticed this yet.

    In the first part, you use rollAxis as an Euler angle representation, and in the second part as an axis.

    The problem is that for a rotation, its axis is usually not the same as a Euler representation of that rotation! For example, the rotation made by rotating around (0.7, 0.0, 0.7) by 90° is completely different from an Euler rotation of (63°, 0°, 63°)!

    Instead, just use rollAxis consistently as an angle/axis representation. You can get the quaternion form using Quaternion.AngleAxis.


    Second problem is global vs local rotation.

    This is a problem whose effects you're definitely already noticing.

    In the first part, you apply rollAxis as a local rotation. This is because rollAxis is the second term of the * operator.

    In the second part, RotateAround rotates around a global axis defined by rollAxis.

    Keep it global or local for both. Global is simpler, and it appears that's what you're trying to do (judging from transform.position + direction), so you should have rotation as the second term of the * operator.


    Altogether, the problems can be fixed by just changing the Quaternion endRotation = rotation * Quaternion.Euler(rollAxis * rollAngle); line:

    Vector3 rollAxis = Vector3.Cross(Vector3.up, direction);
    
    Quaternion rotation = transform.rotation;
    Quaternion endRotation = Quaternion.AngleAxis(rollAngle, rollAxis) * rotation;
    
    // ...
    
        transform.RotateAround(pointAround, rollAxis, rotateThrough);
    

    See here for more information about the quaternion * operator.