- Home /
FindObjectsOfTypeAll() returns old objects
In my save game system, items that need to be saved are marked with a small script, StableId, which has an Id property of GUID. I use these to find GameObjects in a newly loaded Scene or when the player loads the game and a scene is loaded.
The problem I have, is that if the player is in Scene A and I load a saved game that is in Scene A, the game loads the data, reloads Scene A and that using the data makes changes to scene A to bring it back to the state the scene was in when the game was saved.
So, pushing aside any better solutions for saving, I have a problem ATM.
I call FindObjectOfTypeAll(typeof(StableId) and get back an array of all the StableIds in my scene. However, when I load the game, having reloaded the same Scene, the StableIds that were available in the previously loaded scene are still available, so I get the same GameObjects added to my Dictionary of StableIds twice, which is obviously incorrect.
Is this a known issue? Why is FindObjectsOfTypeAll() returning components that should be dead?
Here's the StableId code:
public class StableId : MonoBehaviour
{
// a string GUID to uniqely identify this GameObject
public string Id;
// if true then the object is part of the scene and stored as such
// otherwise it is considered an out of scene object and stored only
// with the game
public bool StoreWithScene = true;
// if true the component has awoken in the scene,
// so prefabs containing StableIds will NOT be visible to the
// save game system
public bool IsInScene = false;
void Awake()
{
IsInScene = true;
}
void OnDestroy()
{
IsInScene = false;
}
}
The Serialization code is huge, so I cannot include that.
The reason I know that there are old instances, is that I've been outputting the InstanceId for the GameObjects my StableIds are attached to, and I can see that the ones that were loaded prior to loading the scene again, are still there.
Note that I have checked the scene - there are no duplicates, just the latest ones. I guess I could hack in a check to reject all InstanceIds that are below a threshold and update that every time a level has finished loading, but it feels more than a bit nasty and makes the assumption that the InstanceIds are never re-used.
Any help/suggestions greatly appreciated!
I guess the questions are:
Should FindObjectsOfTypeAll() return old objects?
Either way, can anyone think of a good way to detect an old object?
Thanks Ian H
Are you specifically only looking for scene objects Ian? I can think of a way around that.
Hmm, yes $$anonymous$$ike. But I want inactive ones as well - happy to have any solution. I'm sure you've had to do a bit of this for your save system.
Ours used to work, it's just that I added a new thing to be serialized and it's exploding :/
It's either something changed with this API or I am missing something/doing something daft, but I've spent a good few hours debugging it, and it certainly looks like there are old objects laying around - I am pretty sure I don't have a reference to them either.
So if all of the root scene objects were live you could scan those and use GetComponentsInChildren on each also searching for inactive. You could have a helper that help all of the scene root objects that was initialized in the editor in case any were deactivated.
FindObjectsOfTypeAll is looking at available project items and all sort I think.
Yes that's not a bad solution tbh, typically not many root objects tbh, and probably everything to be saved is below one root node thinking about it, though the serialization code doesn't contain any specialization for the current game, so yes, I might as well do that.
$$anonymous$$ight as well convert to answer and I can accept, though I guess it doesn't solve the issue I have with that method, but that's probably not a problem, at least, it's not a problem for me :D
Yeah - messy really. You can FindObjectsOfType(Transform)
and filter out everything with a parent. That's why I was wondering about a scene register for them where you just drop them in or run that in an Editor script.
Answer by Bovine · Mar 04, 2014 at 03:55 PM
So, I'll answer this with my answer, plus Mike's answer.
Firstly, getting the top level objects and FindComponentsInChildren is probably a bit more efficient and StableId could be a bit simpler. However, in my case it didn't solve the problem, because I had loaded the level and without yielding or forcing the garbage collector to run, my call to either FindObjectsOfType() or FindObjectsOfTypeAll() was returning the newly loaded objects along with the, not-quite-dead objects. So the answer here is to ensure that you have garbage collected or yielded. It may be only one of these that is required, but it may be both.
If someone really needs to know I can check, but basically I now have the following code surrounding loading a scene:
Debug.Log("loading " + levelName + "...");
Application.LoadLevel(levelName);
do
{
yield return null;
}
while(Application.loadedLevelName != levelName);
// lets force a collect, seems the old level is still here :-|
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
yield return null;
And it now works, the dead objects are now indeed dead. It's possible that the yield was enough while I waited for the requested level to be loaded in a previous version of Unity, or it may just be a bug I hadn't spotted when loading the same level as the current level.
Anyway, enjoy!
This was very helpful. Thank you, Bovine! I came across this as well as I need to support recognition of inactive objects, which can't be done with GameObject.FindObjectsOfType.
I wish there was some way to track if an object is actually already destroyed and waiting for GC.
Answer by KainAsylum · Mar 19, 2014 at 12:00 AM
On a related note, if anyone is noticing duplicates when playing from editor, it's because FindObjectsOfTypeAll() will also return the version of your GameObject that is in the editor as opposed to the Scene that is in play.
I was able to filter these out with by checking the 'hideFlags' of the Transform (not the gameObject) and making sure it is set to HideFlags.None before I can trust it.
Interesting, I haven't noticed that but I only consider GameObjects whose StableId IsInScene.
Are you sure about that? I don't see duplicates but I do see some strange objects that look like they are only scene editor objects (e.g. one called HandlesGO).
Answer by Masked_Riddler · Feb 20, 2019 at 06:10 PM
Found a much better method by using scene.name and comparing to scenemanager.getactivescene().name.
using UnityEngine;
using UnityEngine.SceneManagement;
public static GameObject FindByName(string name) // Finds in the entire scene by discrete name, even inactives!
{
string sceneName = SceneManager.GetActiveScene().name;
foreach (GameObject obj in Resources.FindObjectsOfTypeAll(typeof(GameObject)))
if (obj.name == name)
if (obj.scene.name == sceneName) return obj;
return null;
}
Answer by IsaiahKelly · Mar 13, 2017 at 08:08 PM
FindObjectsOfTypeAll() Returns Everything!
If you're using Resources.FindObjectsOfTypeAll() (replacement for Obsolete Object.FindObjectsOfTypeAll) it will return all GameObjects in the scene and project folder. So you need to filter them if you only want active scene objects. Please check out my answer to another question for a way to filter results based on the active scene.