- Home /
While loop won't end. [closed]
Hello all =)
I am trying to make a simple rotation script for a door. The door will open and close, but the while loop for closing the door won't end. Here is my script:
var startRot : Vector3; //the door's starting rotation
var newRot : Vector3; //the door's new (open) rotation
var rotateSpeed : int; //how fast the door opens
var opened = false; //whether the door is opened or closed
function Start() {
startRot = transform.rotation.eulerAngles;
}
function RotateObject() {
//if door is shut
if(opened == false) {
//while door's rotation is not equal to the new rotation
while(transform.rotation.eulerAngles != newRot) {
//rotate door
transform.rotation.eulerAngles = Vector3.MoveTowards(transform.rotation.eulerAngles, newRot, Time.deltaTime * rotateSpeed);
yield;
}
//when while loop ends, opened is true
opened = true;
}//if door is open
else {
//while door's rotation is not equal to its starting rotation
while(transform.rotation.eulerAngles != startRot) {
//rotate door
transform.rotation.eulerAngles = Vector3.MoveTowards(transform.rotation.eulerAngles, startRot, Time.deltaTime * rotateSpeed);
//print coordinates of startRot
Debug.Log("(" + startRot.x + ", " + startRot.y + ", " + startRot.z + ")");
//print coordinates or current rotation
Debug.Log("(" + transform.rotation.eulerAngles.x + ", " + transform.rotation.eulerAngles.y + ", " + transform.rotation.eulerAngles.z + ")");
yield;
}
//when while loop ends, opened is false
opened = false;
}
}
Once the door closes, the debug logs in the second while loop both print the same coordinates (270, 180, 0), so the while loop should end... but it never stops. I am confused. Thank you for any help.
Thank you for your comment, but if I did that the while loop would break on the first frame and I need it to run until the door is closed.
create another boolen variable doorclose=false. change the doorclose=true when all the frames completed playing.
if(doorclose)
{
break;
}
I do not see any reason to add a second variable... I tried this in the while loop:
if(transform.rotation.eulerAngles == startRot) {
break;
}
Doesn't work though. It's like it doesn't realized they're the same...
if you try something like
Debug.Log((transform.rotation.eulerAngles).ToString("F7") + " --- " + newRot.ToString("F7"));
are they the same value or both different? I would suspect that the greater the precision displayed for the float values, the more likely they're not all EXACTLY identical, which is the condition for your while loop.
The reason I bring this up is because of your use of (Time.deltaTime * rotateSpeed) for your "time" value in $$anonymous$$oveTowards. You're moving a small percentage of the distance per frame between the current angle and the intended angle, but never genuinely reaching 100%.
Answer by Lylek · May 15, 2015 at 09:20 AM
Thanks for everyone's posts. @Dave29483 I wasn't sure how to implement your code, or how to convert it to JS, but if close enough is good enough, I ended up doing this to solve it:
while(transform.rotation.eulerAngles != startRot) {
//rotate door
transform.rotation.eulerAngles = Vector3.MoveTowards(transform.rotation.eulerAngles, startRot, Time.deltaTime * rotateSpeed);
var distanceBetween : float = Vector3.Distance(transform.rotation.eulerAngles, startRot);
if(distanceBetween < 0.1) {
break;
}
yield;
}
Thought about that earlier, then forgot. >.< Still very confused as to why the while loop would end properly when opening the door, just not when closing it... Thank you all though.
What is the value fo newRot? $$anonymous$$aybe it turns out to be some round value that converting from Quaternion to euler is not creating any problem. Like some (0,0,0) where the sin of it is a sharp 0.
Answer by Eric5h5 · May 15, 2015 at 06:54 AM
Comparing floating point values for equality is typically doomed to failure for various technical reasons, so don't do it. Use Lerp instead of MoveTowards.
Actually, that is why I used $$anonymous$$oveTowards. Lerp slows down immensely as it approaches its destination, and takes a very long time until it actually reaches the point.
Tried it anyway though, and increased rotateSpeed greatly so the door pretty much fully opens instantly. I get the same problem though. The door opens and closes - but the while loop for closing just doesn't end.
No, Lerp doesn't do that when used correctly. Lerp stands for Linear intER$$anonymous$$tion and doesn't have any inherent slowdown-at-the-end. http://answers.unity3d.com/questions/14288/can-someone-explain-how-using-timedeltatime-as-t-i.html
I set the third parameter to 1 (removed Time.deltaTime) and the loop still continues.
Answer by Dave29483 · May 15, 2015 at 07:06 AM
It seems highly likely that the two values will never be truly equal due to floating point innacuracies. You are still converting back to float when you create a new Vector3 from the 3 Ints.
When comparing floats you always need to use an epsilon value or threshold to determine if the two values are "close enough" rather than equal.
E.g. ( threshold is how many degrees are considered close enough )
float threshold = 1.0f;
bool isCloseEnough(Vector3 vecA, Vector3 vecB)
{
if ( Mathf.Abs( vecB.x - vecA.x ) < threshold &&
Mathf.Abs( vecB.y - vecA.y ) < threshold &&
Mathf.Abs( vecB.z - vecA.z ) < threshold )
{
return true;
}
return false;
}
Answer by Griffo · May 15, 2015 at 08:25 AM
Like Eric5h5 said use Lerp instead ..
#pragma strict
var openTime : float;
var openPosition : Vector3;
private var originalRotation : Vector3;
private var targetRotation : Vector3;
private var originalTime : float;
private var originalOpenTime : float;
private var z : int;
function Update ()
{
if (Input.GetKeyDown ("y"))
{
RotateObject();
}
}
function RotateObject ()
{
z = (z % 2) + 1;
if(z == 1)
{
originalRotation = transform.localRotation.eulerAngles;
targetRotation = transform.localRotation.eulerAngles + openPosition;
originalOpenTime = openTime;
while (openTime > 0.0f)
{
openTime -= Time.deltaTime;
transform.localRotation.eulerAngles = Vector3.Lerp(targetRotation, originalRotation, openTime / originalOpenTime);
yield;
}
openTime = originalOpenTime;
}else{
originalRotation = transform.localRotation.eulerAngles;
targetRotation = transform.localRotation.eulerAngles - openPosition;
originalOpenTime = openTime;
while (openTime > 0.0f)
{
openTime -= Time.deltaTime;
transform.localRotation.eulerAngles = Vector3.Lerp(targetRotation, originalRotation, openTime / originalOpenTime);
yield;
}
openTime = originalOpenTime;
}
}
Using Lerp or $$anonymous$$oveTowards is not really the issue here. The problem is that the equality is not happening. Lerp or $$anonymous$$oveTowards is just a matter of how the movement should be performed, using a ratio or the remaining distance or using a step value regardless of the remaining distance.
In both cases, the final value will be the exact target with exact same value since it is copied from target to current, including the float inaccuracy. So if you expect 0.0f and it is actually stored as 0.00000001f in target then current will be 0,00000001f (is there enough 0's?) and the comparison will return true.
I often use the code below and it always works for me.
while (current != target){
current = Type.$$anonymous$$oveTowards(current, target, step);
yield return null;
}
But I use this for position comparison and I think the inaccuracy here comes from the fact that the rotation is stored as quaternions and requires a calculation to convert to euler angles and that is where the inaccuracy comes in.
So using an approximation to define the target may solve the problem as mentioned in this answer.
Griffo, this works somewhat, but am getting very strange, obscure rotations. I solved the problem by doing this:
while(transform.rotation.eulerAngles != startRot) {
//rotate door
transform.rotation.eulerAngles = Vector3.$$anonymous$$oveTowards(transform.rotation.eulerAngles, startRot, Time.deltaTime * rotateSpeed);
var distanceBetween : float = Vector3.Distance(transform.rotation.eulerAngles, startRot);
if(distanceBetween < 0.1) {
break;
}
yield;
}
Thank you though.
You should use Quaternion.Lerp/Slerp for your purpose. Those are the appropriate methods for rotation movement.
I agree with fafase .. you should really use Quaternion.Lerp/Slerp, it looks better, but hey whatever works for me :)
Ya... that's what I tried to use at first but I was getting see$$anonymous$$gly random rotations.