- Home /
Spawned Enemies: How to Destroy child objects inside their own attached scripts without Destroying the parent object for InvokeRepeating??
I'm creating a (early version) tower defense shooter game with spawned enemies. I have GameObject called SpawnLocation that contains the location in which I would like to spawn my enemies, with a EnemyManager.cs script attached to it. In that, I call InvokeRepeating ("Spawn", 3f, 3f) in my Start(), and in Spawn(), I call Instantiate (enemy, transform.position, transform.rotation) (enemy being the game object prefab). Attached to that enemy prefab, I have an EnemyController.cs script which detects collisions and Destroy(gameObject).
When I destroy the first enemy, however, I receive an error preventing further spawns:
The object of type 'GameObject' has been destroyed but you are still trying to access it
I believe it is because since I passed in my enemy object, destroying itself after instantiation in its own separate function still destroys the parent object passed in, which means if I try to invokerepeat with that destroyed object, I'll have no object to instantiate. So how do I go about this?
I've been frustratingly searching for an answer for awhile but couldn't find one specific to destroying multiple child game objects within their own instantiated scripts. I know Instantiate() returns the clone object when called in the parent, but how do I destroy it from that other attached child function, not the parent function? Deactivate() likely does the same thing. If I instead Destroy() inside the parent function, how do I get the clone object if that object does not have a distinctive ID (and its position moves)? Using an array of enemy clone objects doesn't really allow me to know if it's been hit 10 (or however many) times and needs to be destroyed. I've tried using coroutines, but seemed to run into problems as well. Here's a sample code:
public class EnemyManager : MonoBehaviour {
public GameObject enemy;
public int playerHealth, spawnCount, deathCount, numEnemies;
void Start() {
numEnemies = playerHealth = spawnCount = 10;
deathCount = 0;
InvokeRepeating("Spawn", 3f, 3f); //give some time before spawning
}
void Update() {
if(playerHealth <= 0)
//end game somehow
}
void Spawn() {
if(playerHealth <= 0 || deathCount >= numEnemies)
return;
Instantiate(enemy, transform.position, transform.rotation);
spawnCount += 1; // keeps track of number of enemies spawned
}
}
public class EnemyController : MonoBehaviour {
public int enemyHealth;
void Start() {
enemyHealth = 10; //hit each enemy 10 times to destroy it
manager = GameObject.Find ("Spawn Location");
managerScript = manager.GetComponent<EnemyManager>();
}
void Update() {
//move player on a path
if(enemyHealth <= 0) {
managerScript.deathCount += 1; //successful kill of an enemy
Destroy(gameObject); //or deactivate
}
}
void OnTriggerEnter2D(Collider2D other) {
if(other.gameObject.CompareTag("Bullet")) { //many triggers on board
enemyHealth -= 1;
}
else if(other.gameObject.CompareTag("EndPath")) {
managerScript.playerHealth -= 1;
Destroy(gameObject); //so it doesn't keep on walkin forever
}
}
}
As I have added below, my main issue is Destroy()-ing each Instantiate()-ed child object within its own attached script without affecting my continued calls to Instantiate() in the parent script! I have noticed that I actually create ELEVEN new enemies even though spawnCount remains 10 at max. Additionally, I can Destroy() any of the enemies except the first one without affecting anything (aka I ignore the first enemy that appears and just attack the second onwards, which is ridiculous in actual gameplay). If I modify the first enemy, however, every additional enemy created using Instantiate() will have that modification... Why is this and how can I fix this so a player can kill all enemies on the field?
NOTE: (I'm not sure if my use of "parent" and "child" are correct since each Instantiation creates a new object in the first level of the hierarchy, not actually created as a child of the GameObject with the script that calls the Instantiate(). $$anonymous$$aybe if I can somehow make them actual children this will work??)
Answer by LiamofElites · Feb 14, 2018 at 12:33 PM
Here are a few things that I would do that might help you with your code logic,
- in the OnTriggerEnter2D in the first if statement (
if(other.gameObject.CompareTag("Bullet"))
) after enemyHealth -= 1 add theif(enemyHealth <= 0)
block after that line so right after taking damage you'll check if there's no health left instead of always checking in the update method
manager
in the EnemyController class use gameObject.transform.parent.gameObject
so if you want to create more spawn locations you don't have to worry about being limited to only 1 because you're using GameObject.Find();I hope this helps you somewhat, I'll be experiementing with your code in my practice project.
Thank you for your response. I think checking for enemyHealth is a great idea and good practice.
In regards to the second part, my problem isn't being able to spawn in different locations, but how to destroy each of those children after they die (aka their individual health reaches zero) without destroying the original game object used to continue spawning new enemies! Because in EnemyController, as soon as I call Destroy(gameObject), I seem to have an effect on the spawning capabilities of the parent going on at the same time. As I continued testing yesterday, I realized that Destroy()-ing the first enemy created will only produce me 4 enemies in total (aka it'll stop instantiating new children), likely because that's how long it took me to destroy the first enemy.
IN FACT, I put a Debug.Log("Enemy Spawned"); print right before the Instantiate() call in Spawn(), and I only get that log starting from the second enemy spawned!! This makes me suspect that the first time either Instantiate() or InvokeRepeating() is called, that first Instantiate() is called immediately with the actual GameObject passed in, and THEN the following Instantiate() calls return instances/clones of that original GameObject?? Not sure if I'm explaining this correctly.