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
by0.1
every0.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 thatRotate
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 valuesusing
Rotate
there is always the possibility that you overshoot the target rotationand 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;
}