Home > Software engineering >  Why does my object keep rotating even though I tell it to stop at 90 degrees
Why does my object keep rotating even though I tell it to stop at 90 degrees

Time:12-20

So, I wrote this code where I tell an object that it should keep rotating until it hits 90 degrees, but it keeps on rotating. Anyway here's the code:

public class playerScript : MonoBehaviour
{
    public float transformZ;
    // Start is called before the first frame update
    IEnumerator Start()
    {
        transformZ = 0;
        transform.position = new Vector3 (0, 0, 0);
        transform.Rotate(0, 0, transformZ);
        while (transformZ != 90)
        {
            transform.Rotate(0, 0, transformZ);
            transformZ = transformZ   0.1f;
            yield return new WaitForSeconds(0.1f);
        }

        
    }

Any help would be appreciated!

CodePudding user response:

TransformZ is Being Set Wrong

In your function, you are increasing transformZ by itself 0.1 every 0.1f seconds. Instead of this you should just directly usetransform.eulerAngles.z as that provides the exact rotation and you don't need to keep a variable that you increment.

Checking for Exact Inequality

You should not be checking exactly for whether transformZ is not equal to 90, considering this is a float value we are dealing with. There should either be a threshold:

while(Mathf.Abs(90f - transform.eulerAngles.z) < [THRESHOLD])

or you could simply just have the while loop check if its below 90 as Aybe pointed out.

while(transform.eulerAngles.z < 90)

CodePudding user response:

I see various issues here

  • you increase transformZ by 0.1 every 0.1 seconds => this will count seconds, not the actually applied rotation (see further below)

  • you rotate about a value transformZ that is getting bigger each time. Have in mind that Rotate does not set a final rotation but rather starts add the current rotation and adds to it.

    You are rotating like

     Iteration | current rotation | transformZ | resulting rotation
    
             1 | 0                           0 = 0
             2 | 0                         0.1 = 0.1
             3 | 0.1                       0.2 = 0.3
             4 | 0.3                       0.3 = 0.6
             5 | 0.6                       0.4 = 1.0
            ... 
    
  • then also in general never use == / ! = for floating point values

  • using Rotate there is always the possibility that you overshoot the target rotation

  • and then personally I would prefer a continuous rotation instead of 0.1 second jumps.


A solution depends a bit on what you want to control.

For a fixed rotation per second I would do

[SerializeField] private float anglePerSecond;
[SerializeField] private float maxAngle = 90;

IEnumerator Start() 
{
    ... 

    var initialRotation = transform.rotation;
    // precalculate the desired rotation going through Quaternion instead of directly Euler space 
    var targetRotation = initialRotation * Quaternion.Euler(0, 0, maxAngle);

    // here "Quaternion != Quaternion] uses a lower precise check that allows for small rounding errors
    while (transform.rotation != targetRotation)
    {
        // with linear anglePerSecond rotate towards the final rotation
        // Without ever overshooting the target rotation
        transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltaTime);
        yield return null;
    }

    // to be sure apply final rotation
    transform.rotation = targetRotation;
}

Or if you rather want to control the duration in seconds

[SerializeField] private float durationInSeconds = 1f;
[SerializeField] private float maxAngle = 90;

IEnumerator Start() 
{
    ... 

    var initialRotation = transform.rotation;
    var targetRotation = initialRotation * Quaternion.Euler(0, 0, maxAngle);

    // similar as before but this time iterate over a certain time
    for(var timePassed = 0f; timePassed < durationInSeconds; timePassed  = Time.deltaTime)
    {
        // factor linear growing from 0 to 1
        var factor = timePassed / durationInSeconds;
        // advantage: you can add ease in/out quote easily e.g.
        //factor = Mathf.SmoothStep(0, 1, factor);
        // interpolate via given factor
        transform.rotation = Quaternion.Slerp(initialRotation, targetRotation, factor);
        yield return null;
    }

    transform.rotation = targetRotation;
}
  • Related