0
\$\begingroup\$

I'm trying to increase the difficulty of the game over time. I'm using InvokeRepeating to spawn enemies and in the update function I calculate the next spawn time using the following code:

public Vector2 secondsBetweenSpawnsMinMax;
float nextSpawnTime;

void Update()
 {
    if (Time.deltaTime >= nextSpawnTime)
    {
        float secondsBetweenSpawns = Mathf.Lerp(secondsBetweenSpawnsMinMax.y, secondsBetweenSpawnsMinMax.x, Mathf.Clamp01(Time.time / 60));
        nextSpawnTime = Time.deltaTime + secondsBetweenSpawns;            
    }        
 }

public void StartNewGame()
{       
    InvokeRepeating("Spawn", 0.1f, nextSpawnTime);                   
}

public void Spawn()
 {        
    Vector3 enemySpawnPos = spawnPoint.position;       
    Instantiate(enemy, enemySpawnPos, Quaternion.identity);       
 }

I'm fixing the secondsBetweenSpawnsMinMax.y to 3 and secondsBetweenSpawnsMinMax.x to 1. The issue I'm facing is that the nextSpawnTime doesn't change. It starts at 3 and stays at that value when it should go down to 1. I'm not sure what's wrong with the code here.

\$\endgroup\$
1
\$\begingroup\$

The problem is that when you call InvokeRepeating, the current value of nextSpawnTime gets copied to the Unity coroutine system. When you change the value of that variable later, the coroutine system doesn't even notice it. Its copy of the value won't be affected. There is no way (I am aware of) to retroactively change the time interval of a repeating invoke. All you can do is deschedule it with CancelInvoke and reschedule it again with the new value.

However, I would not use InvokeRepeating at all in this case. I would instead replicate the functionality on my own in Update, just like you did it with the logic which decreases nextSpawnTime in regular intervals.

My solution for this would look something like this:

private float speedFactor = 1.0f; // start with regular speed
public float spawnInterval = 2.0f;  // default spawn rate = one every 2 seconds
private float spawnTimeLeft;

void Update()
{
    // spawning logic
    spawnTimeLeft -= Time.deltaTime * speedFactor; // higher speed factor = faster spawns;
    while (spawnTimeLeft < 0.0f) {
        spawnTimeLeft += spawnInterval;
        Spawn();
    }
}

Now you just need to add some logic which manipulates the variable speedFactor in order to control the spawning speed. For example, a value of 2.0 will cause spawnTimeLeft to decrease twice as fast from then on and thus cause Spawn() to get called twice as often.

By the way, you might wonder why I use while (spawnTimeLeft < 0.0f) instead of if (spawnTimeLeft < 0.0f). The reason is that your game might get so fast that you occasionally need to spawn two or more enemies in a single update.


However, if you actually like the Unity coroutine system (I don't, but that's just my preference), then you could use a Coroutine instead of InvokeRepeating which uses yield return new WaitForSeconds(nextSpawnTime) to wait until it's time to spawn another enemy. This will use the current value of nextSpawnTime at that moment. But remember that it again uses a copy of the variable. If you change it while the coroutine is waiting, the wait time will not be affected.

public void StartNewGame()
{       
    StartCoroutine(Spawn);                   
}

private IEnumerator Spawn() {
     while(true) {
         yield return new WaitForSeconds(nextSpawnTime); 
         Vector3 enemySpawnPos = spawnPoint.position;       
         Instantiate(enemy, enemySpawnPos, Quaternion.identity);     
     }
}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.