- Home /
How to delay time consuming loop on several frames ? Yield on progress-bar ?
Hello everyone. My mistakes if this case is obvious, but my researches failed me:
I have a huge loop, very time consuming, and want to do a progress bar during the game to inform the player about it. So I started learning about yield statement and manage to do that:
public IEnumerator ComputePI(int _Iterations)
{
for(int i=0;i<_Iterations;i++)
{
//Do so many stupid calculations for no particular results.
yield return null; //or yield return new WaitForEndOfFrame();
}
}
StartCoroutine(ComputePI(double.MaxValue));
It works well, iterations follow and the game does NOT freeze (as it were freezing during all computation for a standard loop in Update function).
But now I want to be warned at each frame about the progression of my for-loop iterator, as a percent of a progress bar for example.
IEnumerable<float> ComputePI(int _Iterations)
{
for(int i=0;i<_Iterations;i++)
{
//Do so many stupid calculations for no particular results.
yield return (float)i / (float)_Iterations;
}
}
StartCoroutine(ComputePI(double.MaxValue));
//==> error CS1502: The best overloaded method match for `UnityEngine.MonoBehaviour.StartCoroutine(System.Collections.IEnumerator)' has some invalid arguments
So apparently StartCoroutine does only accept Enumerator, not IEnumerable... What is the best pattern to do that ? Having a yield returning a progression float ?
Answer by Bovine · Jan 18, 2013 at 08:24 PM
You need to make your loop int value i and _Iterations public fields or public getters from your class so that they can be accessed by other parts of your game.
public class Computation : MonoBehaviour
{
// public fields are probably cheaper or you could provide your own
// backing ints and just have getters return them and not setters
public int Iteration { get; private set; }
public int IterationMax { get; private set; }
public bool IsComputing { get; private set; }
void Awake()
{
Iteration = 0;
IterationMax = 1;
IsComputing = false;
}
IEnumerator ComputationFunction()
{
for(Iteration=0;i<IterationMax; Iteration++)
{
// insert heavy computational part
// you could also check to see if 1 or more MS has elapsed and yield only then
// so you are sort of saying 'I will do around n MS of work' on this each frame
// you could go for finer grain than MS as well, or elect to do 5 passes through your computation before yielding
yield return null;
}
IsComputing = false;
}
public void DoComputation(int count)
{
if(IsComputing)
{
return;
}
Iteration = 0;
IterationMax = count;
IsComputing = true;
StartCoroutine(ComputationFunction());
}
}
Sorry, I haven't compiled it, but it should work okay. As I say, accessors are safer but may be slower - I would have to check, but you could always use backing variables. If you can (you'll need to check it's called right away) it would feel cleaner to put IsComputing = true; inside the ComputationFunction().
Answer by pad406 · Jan 08, 2013 at 11:06 PM
Have a look at this, it'll give you some idea of the problem and possible workarounds
Your answer
Follow this Question
Related Questions
Can the Unity editor break out of an infinite loop in a script? 13 Answers
Yield Not Working - While Loop 3 Answers
Often use of WaitForSeconds()? 1 Answer
While loop yield lag? 1 Answer
while loop not looping 2 Answers