1
\$\begingroup\$

I'm creating an enemy spawner in Unity (2D). At long last, I have something that is quite close to working, but the problem is that I want it to destroy all instances of the enemy when the player can no longer see the spawn area.

That is, the player happens upon the spawn area to find several enemies, but if they move so that the camera can no longer see the spawn area, I want those enemies to be deleted and then a different set of the same enemies to be instantiated when the player (camera) happens upon the spawn area again. But when I attempt this initial delete, the script component no longer has an object to instantiate when the player returns.

Here's a snippet of what I have:

    if (!notOnScreen && spawned == false)
    {
        int numberOfEnemies = Random.Range(1, enemyMax);

        for (int i = 1; i <= numberOfEnemies; i++)
        {

            spawnX = Random.Range(minX + 1, maxX - 1);
            spawnY = Random.Range(minY + 1, maxY - 1);

            Vector3 spawnpoint = new Vector3 (spawnX, spawnY, 0);

            if (enemies.Length == 1) {
                typeOfEnemy = 0;
            }
            else if (enemies.Length > 1) {
                typeOfEnemy = Random.Range(0, enemies.Length);
            }
            else {
                typeOfEnemy = -1;
            }

            Instantiate(enemies[typeOfEnemy], spawnpoint, Quaternion.identity);
        }

        spawned = true;
    }

    if (notOnScreen) {
        spawned = false;

        currentEnemies = GameObject.FindGameObjectsWithTag("Enemy");
        for (int i = 0; i <= currentEnemies.Length; i++) {
            Destroy(currentEnemies[i]);
        }
    }


}

}

I'm sure there's a way to preserve an instance of the enemy so that they can be instantiated again later, but I'm not sure how (sorry--very new to this). I thought about cheating and having one enemy remain at a different z coordinate behind the map (as this is a 2D game), but that doesn't seem like an appropriate solution. Any thoughts or advice?

\$\endgroup\$
  • \$\begingroup\$ Depending on the requirements spawners can sometimes be tricky. Maybe you can do it like: You add a GameObject field in your spawner MonoBehaviour and then drag the enemy prefab to that field in the inspector. Then when your spawn area detects a "trigger collision" with the camera it spawns enemies (using the prefab field) and adds them to a list. When enemy is out of camera(or killed) it despawns itself and removes self from the list. Next time the camera enters the spawn area it can can use the list to know how many enemies are still alive/visible and only spawns the rest. \$\endgroup\$ – Nikaas Aug 3 '18 at 6:12
  • \$\begingroup\$ Anyway think about that you should not despawn an enemy when it is visible on screen no matter how far it (or the camera) is from the spawner. \$\endgroup\$ – Nikaas Aug 3 '18 at 6:26
  • \$\begingroup\$ Not an answer to your question, but having a double negative (!notOnScreen) is much more confusing than (!onScreen). Also you're using the boolean comparison ! and then right next to that doing a == false comparison. I would recommend doing if (!onScreen && !spawned). Also, you're starting your iteration in your first for loop with i = 1, but doing <= numberOfEnemies, which may result in you spawning one too few enemies. \$\endgroup\$ – Nic Foster Aug 3 '18 at 17:03
  • \$\begingroup\$ Also, you're setting spawned to true no matter what if notOnScreen is false, and then if notOnScreen is true you're setting spawned to false. You could just take spawned and move it outside of all of this code and end with spawned = !notOnScreen, or preferably spawned = onScreen. \$\endgroup\$ – Nic Foster Aug 3 '18 at 17:06
  • \$\begingroup\$ @NicFoster Ah, thank you. Time to clean up this code! Honestly, the notOnScreen bit was a lazy move on my part since the code worked the opposite way that I wanted to haha Your feedback is great and much appreciated. \$\endgroup\$ – jeepers mcface Aug 3 '18 at 22:25
0
\$\begingroup\$

I agree with Pascal Lohscheidt that for a case like this, where you're repeatedly spawning & despawning many enemies, recycling them may be a better option.

But, for the general problem of saving an out-of-scene object that you want to be able to spawn on demand, you can use Prefabs.

Take one of the object you want to spawn multiple times, and drag it into your Project folder. This creates a Prefab asset, that exists independent of any scene. Now your spawning scripts can reference the prefab as the source to copy newly-instantiated objects from, instead of relying on an object that's already in the scene.

One often-overlooked trick for Prefabs is that they can be referred to by the type of any component they contain. So if you ultimately want to access an Enemy component on your newly-spawned critter, you can declare its prefab variable as public Enemy enemyPrefab; and instantiate it as Enemy myEnemy = Instantiate<Enemy>(enemyPrefab, ...) to get a reference to the newly created enemy's Enemy component without GetComponent or as. This still spawns a copy of the whole Prefab, not just a free-floating Enemy component. ;)

\$\endgroup\$
  • \$\begingroup\$ Ah, this is the answer I am looking for! Thank you very much! \$\endgroup\$ – jeepers mcface Aug 3 '18 at 22:24
2
\$\begingroup\$

If I understand you right, you want to reposition the enemies at a random position anytime you look at them again. A really Simple way would be to have an array (maybe a list) of enemy objects and method which replaces them and every time the player camera leaves the spawn area you simply disable the objects and replace them and any time your camera enters the area you simply enable them. That would be solution if you really want the same set. Code example:

GameObject[] enemies = FindGameObjectsWithTag(...);

if(camLeaves)
{
   foreach(GameObject enemy in enemies) //place them into another 
                                           position
   {
      enemy.transform.position = new Vector(RandomX, 0, RandomY);
      enemy.SetActive(false);
   }
}

if(camEnters)
{
   foreach(GameObject enemy in enemies)
   {
       enemy.SetActive(true);
   }
}

Anyway this way you won´t lose your references. But that are always the same enemies. And also if an enemy dies you won´t have any reference to him anymore. Maybe it´s worth using a list and removing the instance once it dies. Or you ask anytime you change the positions or enable & disable the objects for new reference with the method FindObjectsWithTag(). If you have questions or when I understood something wrong. Feel free to comment below & ask :)

\$\endgroup\$
  • \$\begingroup\$ Ah, that would be a very good solution. Except for (and this is my fault as I did not specify this) my spawner picks from a list of different enemies and spawns a random number of them (dependent on the spawner itself), so that it may spawn two slimes in one instance, but may spawn a slime and two trolls in another instance. \$\endgroup\$ – jeepers mcface Aug 3 '18 at 22:21
  • \$\begingroup\$ okay :). I see you´ve already got another answer :P which helped you \$\endgroup\$ – Pascal Lohscheidt Aug 4 '18 at 23:53

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.