4
\$\begingroup\$

I learned how to Load and unload scenes in unity. using LoadSceneMode.Additive.

I like to spit up my scenes as much as possible and retain the stuff I need as I go along.

I'd like to be able to find all objects loaded from a specific scene. Obviously Unity knows but I can't find how to get them.

For now, my workaround is to put them in a specifically named empty or have them tagged in a certain way, but that is a workaround.

Is there an easier way?

\$\endgroup\$

4 Answers 4

1
\$\begingroup\$

From this answer mixed with your needs, I would proceed in this way:

List<GameObject> FindSceneObjects(string sceneName){
    List<GameObject> objs = new List<GameObject>();
    foreach (GameObject obj in Object.FindObjectsOfType(typeof(GameObject)))
    {
        if(objs.scene.name.CompareTo(sceneName) == 0){
            objs.Add(objs);
        }
    }
    return objs;
}

Or you can compress the for-loop using a linqstatement

var objs = from GameObject go in GameObject.FindObjectsOfType(typeof(GameObject)) where go.scene.name == sceneName select go;
\$\endgroup\$
8
  • \$\begingroup\$ OP wants to get the objects that belongs to a "specific scene". This code piece returns all GameObjects in the game, regardless of the scene they belong. \$\endgroup\$ Commented Jul 27, 2016 at 17:06
  • \$\begingroup\$ As far as I know the object in a previous scene are deleted when a new scene is loaded. This happen in my project to when I switch from main menù scene to the core scene I have no more the previous objects \$\endgroup\$
    – Leggy7
    Commented Jul 27, 2016 at 17:08
  • \$\begingroup\$ Indeed I Have like 4 or more scenes open at a time; start, Menu, Debug, Background demo and high scores..... I want to be able to hide each of them as I want. \$\endgroup\$
    – Napivo
    Commented Jul 27, 2016 at 17:09
  • \$\begingroup\$ @Leggy7 Not necessarily, if you specify LoadSceneMode.Additive parameter in the loading method, the existing scenes will remain untouched, while the new one gets loaded "additively". \$\endgroup\$ Commented Jul 27, 2016 at 17:10
  • \$\begingroup\$ @Leggy7 DontDestroyOnLoad(this) during start or awake; will also preserve objects across loads. That is how we transfer data across levels most of the time \$\endgroup\$
    – Napivo
    Commented Jul 27, 2016 at 17:17
1
\$\begingroup\$

Cache all objects at the start and detect the changes in the array (or the list).

To elaborate more, have a list like this:

//This list holds all the GameObjects that are currently in the game.
private List<GameObject> _existingObjects = new List<GameObject>();

And call this method at the beginning of the game, and every time a scene is loaded:

//using System.Linq;

public List<GameObject> FindNewGameObjects()
{
    //Get all GameObjects.
    var allObjects = (FindObjectsOfType(typeof(GameObject)) as GameObject[]).ToList();

    //Substract the existing GameObjects from all GameObjects, so the result will be the new ones.
    var result = allObjects.Except(_existingObjects).ToList();

    //Update our list.
    _existingObjects = allObjects;

    //Return the result.
    return result;
}

The return of the FindNewGameObjects() method will be the list of GameObjects that came with the loaded scene.

Note: The code is not tested.

\$\endgroup\$
2
  • \$\begingroup\$ I see your point... Make a list of object loaded from scene one..... make a list of objects from scene one and 2 and delete those from scene one. Load scene 3 and delete the ones from scene 1 and 2 ..... etc.... is there no other way? \$\endgroup\$
    – Napivo
    Commented Jul 27, 2016 at 16:59
  • \$\begingroup\$ @user2888973 Yes, you got it right. This is the quickest way I am aware of to solve this situation. \$\endgroup\$ Commented Jul 27, 2016 at 17:02
1
\$\begingroup\$

Unity has a struct named Scene. From Scene you can get all of the root GameObjects of this particular scene.

Note that the code is not tested.

public static class SceneExtensions
{
    public static void GetAllGameObjects(this Scene scene, out List<GameObject> allGameObjects)
    {
        GameObject[] rootGameObjects = scene.GetRootGameObjects();

        allGameObjects = new List<GameObject>(rootGameObjects);

        for (int a = 0; a < rootGameObjects.Length; a++)
        {
            //? This is kind of a hack because you can use recursive or other methods to access children via `Transform - GetChild()`. I haven't tested which method is more efficient.
            Transform[] childrenTransform = rootGameObjects[a].GetComponentsInChildren<Transform>();

            for (int b = 0; b < childrenTransform.Length; b++)
                allGameObjects.Add(childrenTransform[b].gameObject);
        }
    }

    public static GameObject[] GetAllGameObjects(this Scene scene)
    {
        scene.GetAllGameObjects(out List<GameObject> allGameObjects);

        return allGameObjects.ToArray();
    }
}

Then the usual approach is to get scene by build index.

Scene scene = SceneManager.GetSceneByBuildIndex(sceneBuildIndex);

From my own experience, only loaded scenes will give you the correct result when using this method to get a scene. I couldn't find a way to get a Scene of unloaded SceneAsset at run-time (I would appreciate if someone shared this with me if such thing is achievable).

Then you can proceed to getting all of the GameObjects of that Scene.

GameObject[] sceneGameObjects = scene.GetAllGameObjects();
\$\endgroup\$
0
\$\begingroup\$

My problem got solved. I will put points toward Leggy7 as he gave me the idea to solve my the problem.

The Old procedure is his way of solving it....

List<GameObject> FindSceneObjects_Old(String sceneName)
{
    List<GameObject> objs = new List<GameObject>();
    foreach (GameObject obj in GameObject.FindObjectsOfType(typeof(GameObject)))
    {
        if (obj.scene.name == sceneName)
        {
            objs.Add(obj);
        }
    }

    return objs;
}

This is my way of solving it using LINQ

List<GameObject> FindSceneObjects(String sceneName)
{
    var objs = from GameObject go in GameObject.FindObjectsOfType(typeof(GameObject)) where go.scene.name == sceneName select go;

    return objs.ToList();
} 

Note: Both codes tested and functional

I am aware there is a shorthand for writing LINQ statements but since I have a Background in SQL I am used to this format.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Careful when keeping these result lists though, you might cause unintended memory "leaks". \$\endgroup\$
    – wondra
    Commented Jul 27, 2016 at 21:18
  • \$\begingroup\$ Thanks for the reminder; you are right; I forgot garbage collection cannot clean up as long as this list exists. when I unload a Scene. Good call \$\endgroup\$
    – Napivo
    Commented Jul 28, 2016 at 8:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .