0
\$\begingroup\$

My player is a cube. I want to restrict/prevent its rotation around global y axis. I am doing this using Quaternior.Lerp since I do not want to completely constraint rotation around y axis, I just want it to quickly snap back to original rotation after a small angular displacement. The problem I have is that every time the cube flips to the side around x or z axis, the local y axis is not parallel to the global y axis anymore and I need to select new local axis and restrict/prevent cube's rotation around the new axis. My way of trying to resolve this problem was the following:

  1. Constantly keep track of which side of the cube is "at the bottom". I do this by figuring out which side center has lowest y position value;
  2. Based on that, figure out which local axis is the closest to being parallel to the global y axis;
  3. Use Quaternion.Lerp to interpolate from the current rotation to new rotation. Each time, two of the axis values stay the same, while the third one is being set to the closest 90° angle from the current value (e.g., for x axis: Mathf.Round(transform.rotation.eulerAngles.x / 90) * 90)

I wrote a method named autoCorrectRotation(Vector3 rotation) that I call in FixedUpdate() as autoCorrectRotation(transform.eulerAngles);.

Here is that method:

private void autoCorrectRotation(Vector3 rotation)
    {
        float eulerXX = rotation.x;
        float eulerYY = rotation.y;
        float eulerZZ = rotation.z;

        // Create 6 normal vectors for 6 sides of the cube
        Vector3 vectorDown = Quaternion.Euler(eulerXX, eulerYY, eulerZZ) * Vector3.down;
        Vector3 vectorRight = Quaternion.Euler(eulerXX, eulerYY, eulerZZ) * Vector3.right;
        Vector3 vectorUp = Quaternion.Euler(eulerXX, eulerYY, eulerZZ) * Vector3.up;
        Vector3 vectorLeft = Quaternion.Euler(eulerXX, eulerYY, eulerZZ) * Vector3.left;
        Vector3 vectorForward = Quaternion.Euler(eulerXX, eulerYY, eulerZZ) * Vector3.forward;
        Vector3 vectorBack = Quaternion.Euler(eulerXX, eulerYY, eulerZZ) * Vector3.back;

        List<Vector3> cubeNormalVectorAllA = new List<Vector3>();
        cubeNormalVectorAllA.Add(vectorDown);
        cubeNormalVectorAllA.Add(vectorRight);
        cubeNormalVectorAllA.Add(vectorUp);
        cubeNormalVectorAllA.Add(vectorLeft);
        cubeNormalVectorAllA.Add(vectorForward);
        cubeNormalVectorAllA.Add(vectorBack);

        // Sort normal vectors by ascending y value
        cubeNormalVectorAllA.Sort((a, b) => a.y.CompareTo(b.y));


        Vector3 vectorLowestY = cubeNormalVectorAllA[0];
        
        // Restrict the movement based on which side is "at the bottom"
        if (vectorLowestY == vectorDown || vectorLowestY == vectorUp)
        {
            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(transform.rotation.eulerAngles.x, Mathf.Round(transform.rotation.eulerAngles.y / 90) * 90, transform.rotation.eulerAngles.z), 1f);
            Debug.Log(gameObject.tag + ": DOWN or UP");
        } 
        else if (vectorLowestY == vectorLeft || vectorLowestY == vectorRight)
        {
            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(Mathf.Round(transform.rotation.eulerAngles.x / 90) * 90, transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z), 1f);
            Debug.Log(gameObject.tag + ": LEFT or RIGHT");
        }
        else if (vectorLowestY == vectorForward || vectorLowestY == vectorBack)
        {
            transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(transform.rotation.eulerAngles.x, transform.rotation.eulerAngles.y, Mathf.Round(transform.rotation.eulerAngles.z / 90) * 90), 1f);
            
            Debug.Log(gameObject.tag + ": FORWARD or BACKWARD");  
        }

    }

This works exactly as I wanted only in the first case, where local y and global y are parallel, meaning when either one of original top and bottom sides of the cube are on the bottom. As soon as any of the other 4 sides are on the bottom and the rotation needs to be restricted/prevented for x or z axis, the Quaternion.Lerp does not work. Some laggy/jittering movement is present, but the rotation is not snapping to the closest 90° angle.

Any ideas on what could be wrong or if there is some other way to achieve what I want?

\$\endgroup\$
4
  • \$\begingroup\$ Can you show us how you move this object in the first place? It might be easier to incorporate the snapping in that movement logic, rather than applying it as a fix-up afterward. \$\endgroup\$
    – DMGregory
    Apr 19, 2021 at 1:25
  • \$\begingroup\$ Hey, thanks for responding. I somewhat achieved what I wanted by using the restriction around y axis in all 3 if sentences, instead of also restricting around x and z based on the cube's rotation. Here's a video of the movement: youtu.be/aX9iaN3XnzU I am looking for better solutions though, since this results in weird jittery movement (sometimes spinning) when going over corners and it also turns cubes into jelly when jumping on one another, since they all carry the same script. Any suggestions are welcome! \$\endgroup\$
    – Liisjak
    Apr 19, 2021 at 17:02
  • \$\begingroup\$ Can you show us that movement script as part of your question? \$\endgroup\$
    – DMGregory
    Apr 19, 2021 at 17:04
  • \$\begingroup\$ If the code in your new edit solved your problem, you should post it as an Answer, not embed it in the Question. \$\endgroup\$
    – DMGregory
    Apr 19, 2021 at 17:16

1 Answer 1

0
\$\begingroup\$

I achieved the movement I wanted by using

transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(transform.rotation.eulerAngles.x, Mathf.Round(transform.rotation.eulerAngles.y / 90) * 90, transform.rotation.eulerAngles.z), 1f);

in all 3 if sentences, insted of just in the first one. Here is a video of the resulting movement. This also brought some unwated side effects like spinning or jittery movement when going over corners, so I am still looking for the solutions for the same logic of movement. That is: you must be able to roll "to the side" when going over the edges, but no arbitrary spining around y axis is allowed.

Edit: The whole script now becomes a single line:

transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(transform.rotation.eulerAngles.x, Mathf.Round(transform.rotation.eulerAngles.y / 90) * 90, transform.rotation.eulerAngles.z), 1f);
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .