Search Unity

Unity 5.3, SceneManagement and GameObjects within a scene

Discussion in 'Immediate Mode GUI (IMGUI)' started by skalev, Dec 9, 2015.

  1. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    With 5.3 now fully public, I noticed that GameObjects now have a new property that relates them to the Scene they are part of. This is important obviously.

    In some of our editor tools, we use Resources.FindObjectsOfTypeAll<> to get all scene objects, as this is the only supported way to get all objects in a scene including objects that are inactive.

    With this new addition, I was wondering if there is now a better way to traverse the scene hierarchy, and to enable us to get all objects under the scene's root. I'm assuming that using resources will now give me all objects within all open scenes, which in that case, I will have to then filter out by the scene I want.

    Is it time for Unity to allow us to easily traverse our scene in Editor? it seems like a pretty simple request and functionality, one that the Hierarchy window obviously already is capable of.
     
    NinjaISV, idurvesh and Eths like this.
  2. Eths

    Eths

    Joined:
    Mar 5, 2015
    Posts:
    184
    +1 for that.
     
  3. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    just noticed this in the changlogs for 5.3.1p1

    • SceneManagement: Add Scene.GetRootGameObjects() to return the root game objects of the scene.
    thank you!
     
  4. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    798
    Nice find. Seems undocumented at the moment. Do you think this will be faster/preferred to using FindObjects in general (not just in editor tools)?

    I have a couple of scripts that use FindObject to cache objects at the start of each scene. Now I'm thinking I could use GetRootGameObjects and loop through the array to cache them instead. Or am I way off?
     
  5. Crazydadz

    Crazydadz

    Joined:
    Mar 29, 2012
    Posts:
    50
    Maybe I'm misunderstanding something. Using GetRootGameObjects always throw me an ArgumentException: Scene is not loaded even if 2 lines before I'm loading the scene...

    Code (CSharp):
    1. SceneManager.LoadScene("GameController", LoadSceneMode.Additive);
    2. var controllerScene = SceneManager.GetSceneByName("GameController");
    3.  
    4. var game = controllerScene.GetRootGameObjects()[0].GetComponent<Game>();
     
  6. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    @flashframe - I haven't tested this new API yet, but I wouldn't think it will be any faster, since you'll be getting those objects, and then iterating over them to find what you need. So unless you can filter out objects based on the root they are connected to, you'll be doing the same thing.

    @Crazydadz - Like I said, I haven't tried it yet. I would check to see what the exception is telling you, or perhaps put in some breakpoints to see what you are receiving from the various functions.
     
    Last edited: Jan 12, 2016
  7. Crazydadz

    Crazydadz

    Joined:
    Mar 29, 2012
    Posts:
    50
    @skalev I'm in "testing mode" with this new API. I'm trying to get rid of my manager prefabs (like menus, etc) in my resource folder and put them in scene.

    Even after calling SceneManager.LoadScene, the scene that I'm getting from SceneManager.GetSceneByName is valid but still not loaded...I'll stick with my Resources.Load way for the moment hehe.
     
  8. nschrag

    nschrag

    Joined:
    Aug 8, 2012
    Posts:
    3
    This is a great addition to the API and will make it easy to show/hide entire scenes without loading/unloading them.

    @Crazydadz - Didn't test your code specifically, but it does work with the following. Perhaps there is a one frame delay as SceneManager does some bookkeeping?
    Code (CSharp):
    1. private IEnumerator Start () {  
    2.     yield return SceneManager.
    3.             LoadSceneAsync("SceneWithSeveralRoots", LoadSceneMode.Additive);
    4.     Scene s = SceneManager.GetSceneByName("SceneWithSeveralRoots");
    5.     GameObject[] g = s.GetRootGameObjects();
    6.     for(int i = 0; i < s.rootCount; i++) {
    7.         Debug.Log("Root: " + g[i].name);
    8.     }
    9. }

    @flashframe - GetRootGameObjects should be significantly faster. Assuming that the Scene object already has this array stored, this would simply be accessing the handful of objects in that one scene. FindObject on the other hand will iterate over all objects (not just root) in all scenes. FindObject has a significant GC Alloc as well. Here's a quick profiler test I did on my current project.
    upload_2016-1-19_11-1-54.png
     
  9. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
    @skalev when using `Resources.FindObjectsOfTypeAll` how are you differentiating between scene objects and random assets that have been loaded? `Resources.FindObjectsOfTypeAll` seems to return more than just the scenes in the hierarchy and I haven't found a reliable way to filter out anything that isn't in the hierarchy.
     
  10. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    @samizzo pre 5.3 and scene management, there wasn't really a good way of doing so. It never mattered to me since I only ever used this function in editor tools (it is very slow, i would highly discourage using it in game) and at that time, there weren't any loaded resources. Now, you can do this to filter objects:

    Code (CSharp):
    1.  
    2. if (go is GameObject)
    3.                 {
    4.                     if ((go as GameObject).scene != sceneToSearch)
    5.                         continue;
    6.                 }
    7.                 else if (go is Component)
    8.                 {
    9.                     if ((go as Component).gameObject.scene != sceneToSearch)
    10.                         continue;
    11.  
    12.                 }
    Where go is each object returned by the find function, and sceneToSearch is a scene object.
     
  11. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
    Thanks! I'm writing a tool that runs in the game which needs to find all root GameObjects so I need this functionality (because I want to run this tool even when the game is not running in the editor). I think I won't be able to support pre-5.3 then. I can use Scene.GetRootGameObjects in 5.3.1 and newer.
     
  12. javierfed

    javierfed

    Joined:
    Apr 10, 2015
    Posts:
    50
    I have implemented using additive scenes, and there is a mention somewhere (don't have a link) that says scenes need 2 frames to load. so whenever loading a scene into the heirarchy, make sure to delay by 2 frames before trying to use it.
     
  13. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    I have waited until the scene's 'isLoaded' flag is true and I have delayed by 2 frames, but the scene.rootCount is always 0 and scene.GetRootGameObjects() always returns an empty array. Am I missing something?

    I'm on Unity 5.3.4p6 btw.

    Code (CSharp):
    1. var scene = SceneManager.GetSceneByName(to);
    2. while (scene.isLoaded == false)
    3. {
    4.     yield return null;
    5.     scene = SceneManager.GetSceneByName(to);
    6. }
    7.  
    8. yield return null;
    9. yield return null;
    10.  
    11. var root = scene.GetRootGameObjects();
     
    Whatever560 likes this.
  14. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
    Could be a bug. Set up a minimal repro and report it.
     
    Gizmoi likes this.
  15. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Your code doesn't actually wait for anything. yield return null doesn't wait for a frame.
    Try replacing those lines with yield return new WaitForEndOfFrame()
     
  16. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
  17. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Wow, you are right... that's an obscure part I didn't know of. So I guess I have to go through my code base now and change a lot of calls to yield break;
     
  18. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
    But only if that ordering mattered, otherwise you'd probably be ok. `WaitForEndOfFrame` is really only useful if you want to read the framebuffer into a texture before its presented on the screen, I guess.
     
  19. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Well no. I used it to break out of coroutines ahead of time, this explains some bugs :)
     
    samizzo likes this.