Reputation: 1513
SOLVED: Updated Unity to version 2017.2
When adding more than 1 task to the execution list of UI button On Click() manager, the execution of those tasks gets unpredictable behavior. My countdown script counts twice as fast when there is more than one task added to that list (se the attached image). When I remove one task, the code runs as it should (prints the countdown to the screen every second 5..4..3..2..1 etc). Unity Bug?
public IEnumerator Countdown()
{
while (timeLeft > 0)
{
yield return new WaitForSeconds(1.0f);
countText.text = timeLeft.ToString("f0");
timeLeft--;
}
}
public void StartCountDown()
{
StartCoroutine("Countdown");
}
Upvotes: 0
Views: 1403
Reputation: 1
You can add a "yeld return null;
" at the end of the function to prevent strange behaviors
Upvotes: 0
Reputation: 125435
Each time the coroutine function is called, timeLeft
variable is being modified. If you call that coroutine function again while the first one is running then multiple coroutine functions will be modifying the timeLeft
variable at the-same time resulting to your current issue.
One solution would be to use a boolean variable to detect when coroutine is already running and start is again but you mentioned that you want to create and multiple timers at the-same time. You have two options:
1.Move the timeLeft
variable inside the Countdown
function so that each function call has a timeLeft
variable for it. Also do the-same for the countText
variable so that a different Text
component is used to modify the Text
in each function call.
public IEnumerator Countdown()
{
float timeLeft = 5f;
Text countText = GameObject.Find("TextForThisCounter").GetComponent<Text>();
while (timeLeft > 0)
{
yield return new WaitForSeconds(1.0f);
countText.text = timeLeft.ToString("f0");
timeLeft--;
}
}
2.Move the whole coroutine function to another class then use a callback Action
to notify you each second there is a tick in the timer and when the timer is done. I recommend this method since it is more portable and reusable. You can also implement an ID to determine which timer this is when it finish running.
Timer moved to another script:
public struct CountDownTimer
{
private static int sTimerID = 0;
private MonoBehaviour monoBehaviour;
public int timer { get { return localTimer; } }
private int localTimer;
public int timerID { get { return localID; } }
private int localID;
public CountDownTimer(MonoBehaviour monoBehaviour)
{
this.monoBehaviour = monoBehaviour;
localTimer = 0;
//Assign timer ID
sTimerID++;
localID = sTimerID;
}
public void Start(int interval, Action<int> tickCallBack, Action<int> finshedCallBack)
{
localTimer = interval;
monoBehaviour.StartCoroutine(beginCountDown(tickCallBack, finshedCallBack));
}
private IEnumerator beginCountDown(Action<int> tickCallBack, Action<int> finshedCallBack)
{
while (localTimer > 0)
{
yield return new WaitForSeconds(1.0f);
localTimer--;
//Notify tickCallBack in each clock tick
tickCallBack(localTimer);
}
//Notify finshedCallBack after timer is done
finshedCallBack(localID);
}
}
Usage:
Start timer 4 times each with different ID.
void Start()
{
createAndStartNewTimer();
createAndStartNewTimer();
createAndStartNewTimer();
createAndStartNewTimer();
}
public void createAndStartNewTimer()
{
//Create new Timer
CountDownTimer timer = new CountDownTimer(this);
//What to do each second time tick in the timer
Action<int> tickCallBack = (timeLeft) =>
{
Debug.Log(timeLeft.ToString("f0"));
};
//What to do each second time tick in the timer
Action<int> finshedCallBack = (timeriD) =>
{
Debug.Log("Count Down Timer Done! ID: " + timeriD);
};
//Start Countdown Timer from 5
timer.Start(5, tickCallBack, finshedCallBack);
}
Upvotes: 2