- Home /
Coroutine gets stuck randomly?
The question says it all, I have a Coroutine that I am using to create some spinning animation. The problem is that it suddenly stops randomly (at different times). I don't know what might be causing this as it works most of the times (8 times out of 10) on PC. I also built the game and tried it on an android phone but it only works (3 times out of 10). Any idea what might be causing this?
EDIT:
This is my Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpinWheelScript : MonoBehaviour {
public CanvasGroup spinWheelCanvas;
public GameObject[] rewards;
public GameObject[] pointerDir;
public GameObject[] ringDir;
public GameObject pointer;
public GameObject ring;
private Quaternion pointerTargetRotation;
private Quaternion ringTargetRotation;
private bool spinIsEnabled = false;
private bool isSpinning = false;
private bool lastSpin = false;
private bool animationIsEnabled = false;
private float time;
private float pSpeed;
private float rSpeed;
private float pointerRotateFloat;
private float ringRotateFloat;
private int rewardEnabler = 0;
private int randomReward;
private int pRandomDir;
private int rRandomDir;
private int plastRandomDir = 0;
private int rlastRandomDir = 0;
private void Update()
{
if (spinIsEnabled == false)
{
// Do Nothing
}
else if (spinIsEnabled == true && isSpinning == false)
{
// Select reward
if (rewardEnabler == 0)
{
RewardSelector();
rewardEnabler = 1;
}
StartCoroutine(Spin());
}
if (lastSpin == true)
{
//randomReward = 4;
Debug.Log(randomReward);
pointerRotateFloat = ((360 - (randomReward * 60)) - 30);
ringRotateFloat = ((360 - (randomReward * 60)) - 30);
if (pointerRotateFloat > ringRotateFloat)
{
pSpeed = (pointerRotateFloat);
rSpeed = (pointerRotateFloat / ringRotateFloat) * (ringRotateFloat);
}
else
{
rSpeed = (ringRotateFloat);
pSpeed = (ringRotateFloat / pointerRotateFloat) * (pointerRotateFloat);
}
Quaternion pointerTargetRotation = Quaternion.Euler(new Vector3(0, 0, pointerRotateFloat));
pointer.transform.rotation = Quaternion.RotateTowards(pointer.transform.rotation, pointerTargetRotation, pSpeed * Time.deltaTime);
Quaternion ringTargetRotation = Quaternion.Euler(new Vector3(0, 0, ringRotateFloat));
ring.transform.rotation = Quaternion.RotateTowards(ring.transform.rotation, ringTargetRotation, rSpeed * Time.deltaTime);
if ((pointer.transform.rotation == pointerTargetRotation) && (ring.transform.rotation == ringTargetRotation))
{
lastSpin = false;
isSpinning = false;
spinIsEnabled = false;
animationIsEnabled = true;
}
}
}
IEnumerator Spin()
{
isSpinning = true;
pSpeed = 0;
rSpeed = 0;
time = 0;
while (time < 15)
{
pRandomDir = PointerRandomDirection(); // Function to pick a random number.
rRandomDir = RingRandomDirection(); // Function to pick a random number.
for (;;)
{
pointerRotateFloat = (((pRandomDir + 1) * 60) - 30) - pointer.transform.rotation.z;
ringRotateFloat = (((rRandomDir + 1) * 60) - 30) - ring.transform.rotation.z;
if (pointerRotateFloat > ringRotateFloat)
{
pSpeed = (pointerRotateFloat);
rSpeed = (pointerRotateFloat / ringRotateFloat) * (ringRotateFloat);
}
else
{
rSpeed = (ringRotateFloat);
pSpeed = (ringRotateFloat / pointerRotateFloat) * (pointerRotateFloat);
}
pointerTargetRotation = Quaternion.Euler(new Vector3(0, 0, pointerRotateFloat));
pointer.transform.rotation = Quaternion.RotateTowards(pointer.transform.rotation, pointerTargetRotation, pSpeed * Time.deltaTime);
ringTargetRotation = Quaternion.Euler(new Vector3(0, 0, ringRotateFloat));
ring.transform.rotation = Quaternion.RotateTowards(ring.transform.rotation, ringTargetRotation, rSpeed * Time.deltaTime);
if ((pointer.transform.rotation == pointerTargetRotation) && (ring.transform.rotation == ringTargetRotation))
break;
yield return null;
}
time++;
}
lastSpin = true;
}
private int RewardSelector()
{
randomReward = Random.Range(0, rewards.Length);
return randomReward;
}
private int PointerRandomDirection()
{
int pRandomDir = plastRandomDir;
if (pointerDir.Length <= 1)
return 0;
while (pRandomDir == plastRandomDir)
{
pRandomDir = Random.Range(0, pointerDir.Length);
}
plastRandomDir = pRandomDir;
return pRandomDir;
}
private int RingRandomDirection()
{
int rRandomDir = rlastRandomDir;
if (ringDir.Length <= 1)
return 0;
while (rRandomDir == rlastRandomDir)
{
rRandomDir = Random.Range(0, ringDir.Length);
}
rlastRandomDir = rRandomDir;
return rRandomDir;
}
public void OnSpinButtonClick()
{
if(spinIsEnabled == false && isSpinning == false)
spinIsEnabled = true;
spinWheelCanvas.interactable = false;
}
}
Thanks in advance.
Do you call it multiple times? meaning, it is running and you call it again.
Yep, that's the most likely cause. With different target rotation each, none of them can ever finish as they work against each other.
@PizzaPie yeah I am sure I am only calling it once. Elsewise, why would it work most of the times and some other times it wouldn't?
Answer by FortisVenaliter · Oct 09, 2017 at 07:14 PM
I'd say it's probably line 123 in your example code.
You have for(;;) which is an infinite loop when it doesn't have a break.
You do have a break statement in there, but it only executes if two quaternions are exactly equal. With floating point precision issues, it doesn't surprise me that you end up with indeterminate behaviour.
The problem is that it's likely bouncing back and forth between -0.000000001 and 0.0000000001, never actually hitting that match you're looking for and keeping the infinite loop running.
No, both concerns you mentioned do not really apply. The for-loop is an intentional infinite loop. He has an unconditional yield return null;
at the end of the for loop, so no problem here. The loop does one iteration every frame.
RotateTowards is implemented like this:
public static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta)
{
float num = Quaternion.Angle(from, to);
Quaternion result;
if (num == 0f)
{
result = to;
}
else
{
float t = $$anonymous$$athf.$$anonymous$$in(1f, maxDegreesDelta / num);
result = Quaternion.SlerpUnclamped(from, to, t);
}
return result;
}
The == operator of the quaternion struct looks like this:
public static bool operator ==(Quaternion lhs, Quaternion rhs)
{
return Quaternion.Dot(lhs, rhs) > 0.999999f;
}
So as soon as the remaining delta becomes smaller than the "maxDegreesDelta" the t value is clamped to "1" so it will return "to" so the break condition should work fine.
Of course the computation of the target angle makes no sense as he subtracts one of the quaternion components from an angle in degree. However the effect is neglectable since we deal with unit quaternions so he just subtracts a value between -1 and 1. The overall logic how he rotates the object seems to be overcomplicated. It's hard to actually tell what the intention is, especially since the calculation of the angle makes not much sense.
Your answer
Follow this Question
Related Questions
How to delay gameobject from falling by time? 1 Answer
Executing coroutines consecutively 0 Answers
Terminal-like GUI, wait for input 1 Answer
Multiple Cars not working 1 Answer
Lerp in Coroutine (Crazy Behavior) 2 Answers