0

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!

  • Thanks for the answer, but where should I write this code and how should I use it? – CheeseGunner Dec 18 '22 at 09:18
  • Now it says " 'Mathf' does not contain a definition for 'ApproximatelyEqual'". – CheeseGunner Dec 18 '22 at 09:24
  • Thanks, but now it gives me this error "Operator '>=' cannot be applied to operands of type 'method group' and 'float'" – CheeseGunner Dec 18 '22 at 09:30
  • 1
    Either `while(transformZ < 90)` or `while(!Mathf.Approximately(transformZ, 90)`. – aybe Dec 18 '22 at 09:32
  • Thanks, the error is now gone, but the problem which I had in the beginning still remains. But I noticed that transformZ doesn't count the rotation around the Z-axis, but it counts the time passed until it hits 90 seconds, why is this? – CheeseGunner Dec 18 '22 at 09:43

2 Answers2

3

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)
  • 2
    In general careful though with [eulerAngles](https://docs.unity3d.com/ScriptReference/Transform-eulerAngles.html)! Instead I would rather keep track of the value you actually rotated and check if that exceeds the 90. In general are we sure OP wants to rotate exponentially via `Rotate(0, 0, transformZ) ` where `tranaformZ` is getting bigger with each iteration? – derHugo Dec 19 '22 at 15:11
  • Yes, I said that the code worked (which it did, it turned 90°) because I wasn't home and so couldn't check it, but the problem that I have is that it turns 90° instantly but I want it to happen over a certain amount of time. How can I do that? – CheeseGunner Dec 19 '22 at 20:40
  • EDIT: Nevermind, I used your code below and it worked! Thank you! – CheeseGunner Dec 19 '22 at 21:22
2

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;
}
derHugo
  • 83,094
  • 9
  • 75
  • 115