I'm generating a level, and I have an optional delay to show the steps level generation one by one.
If my generationDelay
boolean is true, I want to use StartCoroutine to spread the generation out over time. If it's false, I want to call the same method but without StartCoroutine.
The problem is that the method is type IEnumerator.
I can make two methods one type of IEnumerator and one not. But I wonder if there is a way to use one method.
In the first script top i did:
public bool generationDelay = false;
Then in a method:
private void BeginGame()
{
mazeInstance = Instantiate(mazePrefab) as Maze;
if (generationDelay == true)
{
StartCoroutine(mazeInstance.Generate(generationDelay));
}
else
{
mazeInstance.Generate(generationDelay);
}
}
Then the other script where the method Generate is: In the top:
public float generationStepDelay = 0.01f;
Then the method Generate:
public IEnumerator Generate (bool generationDelay) {
if (generationDelay == true)
WaitForSeconds delay = new WaitForSeconds(generationStepDelay);
cells = new MazeCell[size.x, size.z];
List activeCells = new List();
DoFirstGenerationStep(activeCells);
while (activeCells.Count > 0) {
yield return delay;
DoNextGenerationStep(activeCells);
}
}
I'm getting an error on the line:
WaitForSeconds delay = new WaitForSeconds(generationStepDelay);
"Embedded statement cannot be a declaration or labeled statement"
And an error on the line:
yield return delay;
"The name 'delay' does not exist in the current context"
The reason I want to use a bool variable is that when i'm using StartCoroutine it's kind of slow, even if I change the value of generationStepDelay
to 0 it's kind of slow. And if I'm not using StartCoroutine and not using the Generate method as IEnumerator it will work fast.
So i wonder what should I do and how?
Answer
First of all: The errors you cite have nothing to do with the problem you're solving - they're just syntax mistakes.
When you define delay
inside an if
like that, the definition isn't visible to the rest of the code - it exists only inside the scope of the if
itself (which is just that line defining the variable alone - not very useful, which is why the compiler tells you not to do that)
To conditionally assign a value, you first need do define a spot it can live in, unconditionally. Then fill that slot based on your condition:
WaitForSeconds delay = null;
if (generationDelay == true)
delay = new WaitForSeconds(generationStepDelay);
// If we fail out of the if, delay still has a well-defined value of null.
Secondly, if you want to complete the whole operation in a single frame, you don't need to conditionally define your WaitForSeconds
at all - you can simply ignore the IEnumerator's
current yield value, like so:
void RunCoroutine(IEnumerator routine, bool allAtOnce) {
if(allAtOnce == false) {
// Start the coroutine normally, and let the Unity
// engine handle scheduling it with appropriate waits.
StartCoroutine(routine);
} else {
// Race through all steps of the coroutine until MoveNext()
// returns false when the method finishes / yields break,
// and don't wait for anything in between!
while(routine.MoveNext());
}
}
Note that this is not safe to use for coroutines that do things like...
- Wait on a
WWW
orResourceRequest
object until some data is loaded from the disc/network (you'll skip this wait and try to use the data before it's loaded) - Chain control to a nested coroutine (you'll ignore this new coroutine and resume the parent routine immediately)
- Expect other game systems to do work between yields (eg. yielding
WaitForFixedUpdate
to let the physics step ahead)
It will also block the main thread until the coroutine finishes ALL of its work, which can cause your game to stutter or become non-responsive if the coroutine takes a long time.
To avoid these problems, you could just as easily stick to using vanilla StartCoroutine
all the time, and just set your WaitForSeconds
value to 0.0f if you don't want to delay. That way you'll wait the minimal amount of a single frame before resuming, giving the engine a chance to still run its other updates so the game remains responsive and you can show things like progress bars. And then you can still wait on file access/other coroutines/etc. without breaking anything.
If one step per frame is too slow, you can use a loop counter so you only yield
every 5th or 10th or 100th loop. eg.
int batchIndex = 0;
while (activeCells.Count > 0) {
if(++batchIndex > stepsPerBatch) {
yield return delay;
batchIndex = 1;
}
DoNextGenerationStep(activeCells);
}
No comments:
Post a Comment