- Home /
GameObject.FindGameObjectsWithTag still finding destroyed object (C#)
I have a few methods that perform some tasks. Some are stored publicly on my "Manager" that always exists. My levels have a "Level_Manager" that call them as needed.
When I try the following I get incorrect results.
character_spawn_manager_inst.DestroyLevel (); //first level has 1 spawn
character_spawn_manager_inst.LoadLevel (); //new level has 4 spawns
character_spawn_manager_inst.SpawnFinder();
result: 5 spawns?
Here is the main code of SpawnFinder:
spawnPoints = new GameObject[GameObject.FindGameObjectsWithTag ("Spawn_Point").Length];
spawnPoints = GameObject.FindGameObjectsWithTag ("Spawn_Point");
Why is GameObject.FindGameObjectsWithTag still finding the recently destroyed objects? I'm guessing the reasoning is something simple that I'm just not seeing. Any input would be great. Thanks!
It's not necessary to initialize the array this way (it's pointless, wasteful). This is sufficient:
spawnPoints = GameObject.FindGameObjectsWithTag("Spawn_Point");
Regarding your question, I assume LoadLevel() will destroy the current contents of the scene. If you're getting this result, I imagine you're not properly cleaning up the level. Note that GameObject.Destroy() is not necessarily immediate - it often completes at the end of the frame in which it was called. So that might be happening, too. I'd probably be lazy and insert a one-frame delay between destroying the old level and creating the new one.
Thanks for your response. I added the first line initializing a new one that way out of desperation to make sure that nothing was left behind in it. You have confirmed my suspicion that it is redundant.
Some clarification: I am using a simple customized level load system, where each level is a prefab. $$anonymous$$y DestroyLevel() just removes any of my prefabs that are tagged with "Level", then I load the new one and make a count of the spawns. I'll look into the delay idea, but something tells me that might not be a good long term solution. Thanks again.
Answer by Baste · Nov 25, 2014 at 05:30 PM
As @AlwaysSunny pointed out, your array initialization is way off. Actually, what you're doing is first making a new array with the length of the array returned by the FindGameObjectsWithTag method, and then immediately discarding that array in favor of the one returned by the array.
So, I assume you're doing an Application.LoadLevel somewhere in the LoadLevel method. In that case, all of the gameObjects in the current scene should be destroyed. But, unity destruction is kinda wonky at times, and objects can be around on the same frame as you're destroying them. If you're using LoadLevelAsync, then the behavior you're seeing should be expected.
What I'd do to solve this, is to move the call to the SpawnFinder method to OnLevelWasLoaded. That should make sure that all of the old objects are gone before the call to the method.
Thanks for the feedback. I do see now that the first array initialization was unnecessary. I'm afraid I'm not using the LoadLevel, but a simple custom process listed in the above note to AlwaysSunny. Since I cannot use OnLevelWasLoaded, I was thinking of keeping lists and removing the initial spawns from the post level-load results. I'm still not quite sure if that's the best way to go about it (and I'm still unsure why it is finding destroyed objects). Thanks for the help.
Why would you load in prefabs ins$$anonymous$$d of just using the built in scene loading system?
$$anonymous$$y "levels" are very small and simple one screen areas that the player is in that will be swapped out frequently, so I opted to make them out of prefabs and use this method. Other than the gameobject.find problem, this seems to work out very well. I had this exact same problem when finding all players in a different situation a couple weeks ago. I found a workaround, but I would love to understand why this occurs.
I've made a super simplified example of what is occurring, as this is a problem I have been running into in different situations.
//Finds all old objects in scene marked Finish and destroys them (3 objects in this example)
GameObject[] oldObjects = GameObject.FindGameObjectsWithTag ("Finish");
foreach(GameObject oldObject in oldObjects ){
Destroy( oldObject );
}
//Creates a new gameObject with the tag "Finish", meaning there should be ONE sphere in game with the tag
GameObject newObject = new GameObject ();
newObject = GameObject.CreatePrimitive (PrimitiveType.Sphere);
newObject.transform.position = Vector3.zero ;
newObject.tag = "Finish";
//Printing the number of gameobjects with "Finish" as a tag
Debug.Log ( GameObject.FindGameObjectsWithTag ("Finish").Length );
//This results in 4, which means it still finds destroyed objects at this time.
There must be a step in traditional workflow that I am missing to avoid/work around this problem.
Replace 'Destroy(oldObject)' with 'DestroyImmediate(oldObject)', and your debug will return 1.
DestroyImmediate ensures that the object is considered removed later on the same frame, but I assume that it's a bit slower, as it can't be removed together with all of the other things that should be destroyed on the same frame.
In general, if you're destroying something, use Destroy, unless you need the object to be registered as destroyed on the same frame.
Thank you so much for that solution! I realize it's a more expensive call, but waiting until the next frame could be risky in terms of other functionality trying to access that destroyed object.