0

I am trying to automatically spawn a desired amount of enemies within an area, which is currently a test area. The idea is that if the current amount of enemies are less than the amount of desired enemies, then the keepSpawning boolean is set to true which activates the while loop.

Below is my current script, but the issue is that on start, it doesn't instantiate the correct amount of enemies. Additionally, while the game is running, it doesn't instantiate the correct amount of enemies. I have a minus 1 for the enemiesCurrentCount variable when an enemy is killed, and the while loop does replace the killed enemy which is great, but still does not instantiate to the desire amount of enemies.

This is the result I get from setting the enemies desired value;

enemiesDesired Resulting amount of enemies
1 0
2 1
3 1
4 2
5 2
6 3
7 3
8 3
9 3
10 4
11 4
12 5
13 5
14 6

Code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnEnemies : MonoBehaviour
{
        //Enemies array
        public GameObject[] Enemies;
        public int enemiesInArray;

        //Spawn enemy within these positions on the map
        private int xPos;
        private int zPos;
        private int yPos;

        private int randomEnemy;

        public static int enemiesCurrentCount; //Amount of enemies in game currently
        public int enemiesDesired; //Amount of enemies desired in game at the same time

        public bool keepSpawning;

        //Wait timers
        public float startWaitSeconds;
        public float spawnWaitSeconds;


    void Start()
    {
        enemiesCurrentCount = 0;
        keepSpawning = true;
    }

    void Update()
    {
        //Check if new enemies should be spawned
        if (enemiesCurrentCount <= enemiesDesired)
        {
            keepSpawning = true;
            StartCoroutine(RandomSpawnEnemies());
        }
        else
        {
            keepSpawning = false;
        }
    }

    //Spawn Enemies
    IEnumerator RandomSpawnEnemies()
    {
        yield return new WaitForSeconds(startWaitSeconds);

        while (keepSpawning)
        {
            randomEnemy = Random.Range(0, enemiesInArray);
            xPos = Random.Range(-10, 8);
            zPos = Random.Range(17, 30);
            Instantiate(Enemies[randomEnemy], new Vector3(xPos, yPos, zPos), Quaternion.identity);
            enemiesCurrentCount += 1;
            yield return new WaitForSeconds(spawnWaitSeconds);
        }
    }

}

As a test and from comments received, I have amended the code to run the spawner only on start, just to see if it would produce the desired amount of enemies, but still it doesn't. I tried to make the code re-producable also for better assistance however you'll still require an 'enemy' to add and also change the co-ordinates so they spawn in the area you have.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnEnemies : MonoBehaviour
{
        //Enemies array
        public GameObject[] Enemies;
        public int enemiesInArray;

        //Spawn enemy within these positions on the map
        private int xPos;
        private int zPos;
        private int yPos;

        private int randomEnemy;

        public int enemiesCurrentCount = 0; //Amount of enemies in game currently
        public int enemiesDesired = 5; //Amount of enemies desired in game at the same time

        //Wait timers
        public float spawnWaitSeconds;


    void Start()
    {
        StartCoroutine(SpawnRandomEnemies());
    }

    void Update()
    {

    }

    //Spawn Enemies
    IEnumerator SpawnRandomEnemies()
    {
        while (enemiesCurrentCount < enemiesDesired)
        {
            randomEnemy = Random.Range(0, enemiesInArray);
            xPos = Random.Range(-10, 8);
            zPos = Random.Range(17, 30);
            Instantiate(Enemies[randomEnemy], new Vector3(xPos, 1, zPos), Quaternion.identity);
            enemiesCurrentCount += 1;
            yield return new WaitForSeconds(0.1f);
        }
    }
}

The new above code sets the enemy count at 0, the desired count at 5, but still only spawns 2 enemies.

WolfPG
  • 25
  • 5
  • 1
    why is `enemiesCurrentCount` a `static`? And why not rather put the check into the Coroutine itself / use proper for loop? I would rather for every killed enemy start a check and Coroutine if required instead of eventually ending up with concurrent routines etc .. you basically keep tarting hundreds of parallel Coroutines each frame until the condition is not matched – derHugo Aug 23 '22 at 14:45
  • As it stands this is not reproducible .. (besides the concurrency issues I mentioned before) I don't see why it should stop unexpectedly ... I would rather expect to spawn way too many ^^ – derHugo Aug 23 '22 at 14:50
  • Exactly how many is "the correct amount" and how many are actually showing up? – Joe Aug 23 '22 at 15:37
  • @derHugo Thanks for your response. I made it a static so I could access it from another script. Should I remove static and reference the namespace instead, to access it? I'm only about a month into learning C#, putting in around 8 hours a day into learning so that should explain also why my code might not be completely correct/efficient. I'll get there. Thanks for pointing our the parallel Coroutines, I see that now so i'll try to make it work another way. Possibly trigger a new spawn upon an enemy death. I'm still not sure why the loop stops short though, more trial and error needed it seems. – WolfPG Aug 24 '22 at 07:31
  • @Joe The correct amount is the amount I set as the enemiesDesired value. The table I produced above shows in the left column the value I set, the column on the right shows what I got. It should have matched, so enemiesDesired = 5 should spawn 5 enemies but instead it produced 2. The code logic from my perspective should be working, but it's not and i'm confused. Thought a different perspective from more experienced minds could help me work this out. – WolfPG Aug 24 '22 at 07:34
  • @derHugo I made some amendments and i've posted my new code above. This should be re-producible now. Still only spawns 2 enemies on start even though the current count is set to 0 and desired count is set to 5. From my understanding it should be producing 4 enemies on spawn. – WolfPG Aug 24 '22 at 07:44
  • 1
    @WolfPG The code seems fine now at first glance. Just to make sure everything else is fine, when you're checking for spawned enemies are doing it in inspector (cause some may be overlapping and not visible)? Are you accessing this script from any other script? There are no errors (like not assigned references)? Also try to Debug.Log(enemiesCurrentCount) inside the while loop to see what it's doing exactly. – Shrimp Aug 24 '22 at 08:38
  • @Shrimp I created an empty game object and attached the script to it. For the purpose of testing to get this working first, I removed any access to this script from any other script and kept all its code within the same script. So this should mean there is no overlap or external dependancies. No errors being received. Oddly, after adding the debug to see what exactly it's doing, it counts 5 (1, 2, 3, 4, 5) but doesn't instantiate 5, only 2. So does that possibly mean there is an issue with the instantiate line? I don't see how though, as you said, the code seems fine. – WolfPG Aug 24 '22 at 08:49
  • @Shrimp that said, it seems the issue is with counting even though it is counting. The reason I think this is because when I comment out the line enemiesCurrentCount += 1; it spawns infinitely. – WolfPG Aug 24 '22 at 08:51
  • @Shrimp omg I fixed it! But this makes no sense! I changed enemiesCurrentCount from an int to a double, and changed enemiesCurrentCount += 1; to enemiesCurrentCount += 0.5; and now it works perfectly. How though lol – WolfPG Aug 24 '22 at 08:55
  • 1
    @WolfPG Hm, then maybe you did not change the variable in inspector earlier, but in script only? Remember that setting a variable to public shows it in inspector. You can then change it in inspector, but changing it in script does not actually override that variable. So if your inspector variable was ex. 3 (spawned only 1 enemy) and in script you had `public int enemiesCurrentCount = 0;`, the result would still only spawn 1 enemy. By changing your variable to double you reset the inspector value again. – Shrimp Aug 24 '22 at 08:58
  • Does using `Enemies[0]` twice add two enemies? `Random.Range(0, enemiesInArray)` may return duplicate values – Hans Kesting Aug 24 '22 at 08:58
  • @Shrimp Oh I see what you mean now by changing it in inspector. I actually tried multiple ways. Setting it to 0 in script and only changing via inspector. Setting 0 in inspector and only changing the value in code, and then changing both the value in code and in the inspector to match. None of those resolved the issue when I did try it. – WolfPG Aug 24 '22 at 09:04
  • @HansKesting I did consider that maybe it was random rolling say 0 and 1 and counting those as spawns even though I only currently have one enemy in my array. I tried removing random roll and the array but that didn't work. Plus I realised it being random shouldn't give the same wrong result each time. I resolved this now but i'm confused by how I resolved it as it makes no sense lol. – WolfPG Aug 24 '22 at 09:06
  • 1
    @WolfPG if it was the case of having it set to other value in inspector earlier, then you might try to change it back to int and set 0 both in script and inspector. If it works then that's what it was. – Shrimp Aug 24 '22 at 09:08
  • @Shrimp What it ended up being to resolve it was to change enemiesCurrentCount += 1; in the script to enemiesCurrentCount += 0.5; . Now when I set say '5' for desired enemies in the inspector, it spawns 5 enemies. So now it's working correctly, although the debug is showing it counting double the amount. That's why i'm confused by this. I found a similar case with my enemy damage amount which I also resolved by dividing the damage by 2. Maybe I have a setting in unity somewhere causing it to double some values? Not everything is doubling though. – WolfPG Aug 24 '22 at 09:13
  • 1
    @WolfPG I understand exactly what you mean. I'm just saying that changing variable type to double had reset its' inspector value. Try changing it back to int = 0 and += 1 to see if it works like that too again (as the inspector value will reset to 0 again, so the same code you had previously might work fine now). If it does then we know what the problem was :) – Shrimp Aug 24 '22 at 09:17
  • @Shrimp Ah sorry, I understand what you're saying now. Ok so i've done that to see, and back to the old result of under spawning. Changed it back to an int = 0 in the script, and change the 0.5 back to a 1 for the count up. If I do work out what it was i'll remember to come back here and let you know though. – WolfPG Aug 24 '22 at 09:26
  • @WolfPG Ah, so it wasn't that. Then I'm clueless why double type is working and int is not :P – Shrimp Aug 24 '22 at 09:29
  • @Shrimp I even tried keeping it as a double but setting 1.0 as the value but that makes it under spawn again. So the count is double the amount it actually produces, so setting it again to 0.5 resolves it. – WolfPG Aug 24 '22 at 09:33
  • 1
    @WolfPG Got another idea. Don't your enemies have any scripts that may destroy them? for example a collider script that destroys them on collision? Then maybe right after being spawned on top of another enemy it's being destroyed? Try filling Enemies array with empty gameobject prefab to make sure it's nothing to do with your enemies prefab. Then by spawning ex. 5 it's destroying half of them that overlap and spawning 10 leaves around 5 (case with double instead of int). – Shrimp Aug 24 '22 at 09:34
  • @Shrimp I tried that and it works. I added a capsule game object instead of my enemy to spawn. So yeah, definitely something to do with my enemy game object. It's a free asset I downloaded from Unity asset store to practice with so maybe it has something in its script that is causing issues as you mentioned. On it's death, its script contains ```this.gameObject.SetActive(false);``` and ```GetComponent().enabled = false;```. I don't have any other scripts that destroy them or deactivate them. – WolfPG Aug 24 '22 at 09:56
  • The 0.5 makes little sense for an amount of objects ... are there maybe multiple instances of this components running parallel? Or maybe some entries in the enemies array are not assigned? – derHugo Aug 24 '22 at 18:21

2 Answers2

0

It happens because when it runs the RandomSpawnEnemies and check the condition after startWaitSeconds seconds the Update method have already set keepSpawning to false. You can verify it removing the yield return new WaitForSeconds(startWaitSeconds);.

  • Thanks for your feedback. I tried this however It still doesn't produce the correct amount of enemies. I posted amended code above and still it's the same which is confusing. If I remove the condition it does spawn infinitely, but with the (enemiesCurrentCount < enemiesDesired) condition it doesn't respond how I would expect it to. – WolfPG Aug 24 '22 at 07:46
0

I manage to fix it so the code now works. Thanks to everyone for your help and guidance, it helped me achieve a working result.

I resolved the under spawning issue by changing enemiesCurrentCount from an integer to a double, and then I changed enemiesCurrentCount += 1; to enemiesCurrentCount += 0.5; and now it works perfectly.

After various additional tests to determine why this change works but the code that is seemingly fine does not, I concluded that the issue is with the enemy game object I was using which was downloaded from Unity asset store.

I changed my code back again and attempted to spawn enemies using a capsule game object as a test, and it works perfectly.

Final code if anyone would like it, to spawn their own enemies;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SpawnEnemies : MonoBehaviour
{
        //Enemies array
        public GameObject[] Enemies;
        public int enemiesInArray;

        //Spawn enemy within these positions on the map
        private int xPos;
        private int zPos;
        private int yPos;

        private int randomEnemy;

        public int enemiesCurrentCount; //Amount of enemies in game currently
        public int enemiesDesired; //Amount of enemies desired in game at the same time

        //Wait timers
        public float spawnWaitSeconds;


    void Start()
    {
        StartCoroutine(SpawnRandomEnemies());
    }

    void Update()
    {

    }

    //Spawn Enemies
    IEnumerator SpawnRandomEnemies()
    {
        while (enemiesCurrentCount < enemiesDesired)
        {
            randomEnemy = Random.Range(0, enemiesInArray);
            xPos = Random.Range(-10, 8);
            zPos = Random.Range(17, 30);
            Instantiate(Enemies[randomEnemy], new Vector3(xPos, 1, zPos), Quaternion.identity);
            enemiesCurrentCount += 1;
            yield return new WaitForSeconds(0f);
        }
    }

}
WolfPG
  • 25
  • 5