0
\$\begingroup\$

How can I add a delay between spawning my enemies in Greenfoot? I'm doing it from a class that is neither a world nor an actor, its made totally by me. I need to it spawn 3 enemies, 3 times, with a 1-second delay between each time. Here's what I've got now.

void attack()
{
    long beginTime = System.nanoTime();
    int i = 0;
    while(i < 3)
    {
        if ((System.nanoTime()-beginTime)/1000000000>1f)
        {
            for (int j = 0; j < 3; j++)
            {
                world.spawnEnemy();
            }
            i++;
        beginTime = System.nanoTime();
        }
    }
}

It's based off of this answer to this question. However, it does not at all give me the desired results. Instead, it entirely pauses everything else in the game (all of the actors, probably the world too, but I'm not sure) until the makeshift timer ends 1 second into it, and then spawns all 3 waves (all 9 enemies) at once.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Let's walk through this code, pretending that nanoTime = 0 at the start for simplicity.

  1. We initialize beginTime = 0 and i = 0

  2. We check the while loop's condition, and i is indeed less than 3, so we enter the loop.

  3. We check if the current time is 1 second later than begin time yet, but of course it isn't! We've barely executed two instructions since begin time. So we skip over the loop body.

  4. We hit the end of the while loop, and check the condition again. i is still less than 3, so we re-enter the loop and go back to step 3.

    • We keep doing this BILLIONS of times, because we do so little work inside the loop each time, nanoTime isn't much bigger each time we loop around.

      This is called a "busy-wait" or "spinning" - the processor is kept busy checking the clock then looping then re-checking the clock, so it's never allowed to exit this loop, return from this function, update the rest of your game state, process player input, or draw a frame. You've effectively stalled your game here.

  5. Finally, an eternity of wasted CPU time later, we've managed to burn a second of time and our inner if condition finally passes. Huzzah! We enter its body.

  6. We spawn 3 enemies.

  7. We increment i to 1.

  8. We set beginTime to 1000000100 or so.

  9. We hit the end of the while loop and check the condition again. Oh no. i is still less than 3, so we're stil not allowing the CPU to move on yet! Back to step 3 we go, and burn another second doing nothing...

etc.

So, don't put a wait loop inside your game loop. Instead, what you really want to do is call this function periodically, and each time check if it's time to spawn a new wave, rather than hold the CPU hostage until it's time.

That might look something like this:

// Call this at the start of your level.
void SetUpWaves() {
    nextWaveSpawnTime = System.nanoTime() + 1000000000;
    wavesRemaining = 3;
}

// Call this periodically, say once per frame update,
// or at a coarser frequency if you prefer, since it changes state infrequently.
void SpawnWaveIfReady() {
    // Cheap early-out if it's not time to spawn a wave yet.
    if(System.nanoTime < nextWaveSpawnTime)
        return;

    // It is time! Spawn a wave!
    for (int j = 0; j < 3; j++)
    {
         world.spawnEnemy();
    }
    wavesRemaining--;

    // Work out when to spawn the next wave, which might be "never again."
    if(wavesRemaining > 0)
        nextWaveSpawnTime += 1000000000;
    else
        nextWaveSpawnTime = long.MAX_VALUE;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .