Search Unity

Construct An Array Of All Game Objects In Scene?

Discussion in 'Scripting' started by amcclay, May 18, 2009.

  1. amcclay

    amcclay

    Joined:
    Aug 8, 2006
    Posts:
    143
    Heyo.

    I'm starting to try to make a very basic "model viewer" app on the iPhone using Unity.

    I am basically going to just have a scene that is totally empty except for the models that I want to be able to view so, I assume I can simply construct an array of everything in the scene and use that to "cycle" through them based on a swipe across the screen or other input.

    My question is, is there a simple way to construct an array of every game object in a scene and then using the members of that array to turn those objects on/off (and perform other operations on them)?

    Any help is greatly appreciated.
    (ps I'm using C#)

    Thanks!
     
  2. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    Code (csharp):
    1. GameObject[] obs = (GameObject[]) Object.FindObjectsOfType(typeof(GameObject));
    Should fill obs with all (active) loaded GameObjects. This function can be used to obtain references to any loaded object that inherits from Object (i.e. the Unity 'Object', not System.Object).

    Edit: oops, seems I forgot two characters. That'll teach me to post untested code :p.
     
  3. amcclay

    amcclay

    Joined:
    Aug 8, 2006
    Posts:
    143
    Hey thanks for the response!

    I am getting an error when trying to add this line to the the Start function:

    GameObject[] obs = (GameObject) Object.FindObjectsOfType(typeof(GameObject));

    ERROR:

    Assets/Scripts/mainScript.cs(11,37): error CS0030: Cannot convert type `UnityEngine.Object[]' to `UnityEngine.GameObject'

    Any idea why?
     
  4. Der Dude

    Der Dude

    Joined:
    Aug 7, 2006
    Posts:
    213
    A GameObject is not an Array of GameObjects. Try casting to (GameObject[]), though this might not work.

    If it doesnt let you do that either, you'll have to use Object Arrays and then casting them before using them.
     
  5. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    You may want to keep things in an ArrayList rather than convert to a built-in array. The built-in array will be much faster for iteration, but the ArrayList is dynamic.

    Anyway, here are code snippets to get you started:

    Code (csharp):
    1.  
    2. ArrayList list = new ArrayList(FindObjectsOfType(typeof(GameObject)));
    3.  
    4. GameObject[] array = (GameObject[]) list.ToArray(typeof(GameObject));
    And god C# is verbose... :p
     
    Brogan89 likes this.
  6. lehk

    lehk

    Joined:
    Feb 12, 2009
    Posts:
    91
    Code (csharp):
    1. GameObject[] obs = (GameObject[]) UnityEngine.Object.FindObjectsOfType(typeof(GameObject));
     
  7. amcclay

    amcclay

    Joined:
    Aug 8, 2006
    Posts:
    143
    Now, what I want to do is get (and print) the name of an object in the array.

    Unfortunately, when I use the following:

    print(obj[0].name);

    I just get an output of "GameObject" (and not the name of an object)
     
  8. amcclay

    amcclay

    Joined:
    Aug 8, 2006
    Posts:
    143
    Ok, first off apologies for my stupid mistake in the previous post. I was seeing the output of "GameObject" .. well.. because that is what the first object in my scene was called! Duh!

    So, I've made my array of objects, and it seems there are a couple of different ways to print out that list. I'm wondering if one way is "better" than the other:

    This

    Code (csharp):
    1.  
    2.         for (int i = 0; i < obs.Length; i++)
    3.         {
    4.             print(obs[i].name);
    5.         }
    6.  
    Vs. This

    Code (csharp):
    1.  
    2.         foreach (GameObject myObj in obs)
    3.         {
    4.             print(myObj.name);
    5.         }  
    6.  
    Is either way better / more efficient than the other?

    Second, I really want to understand how to construct an array properly, and honestly the line that creates the array is confusing to me. Could somebody explain to me what exactly each part of this statement is doing?

    GameObject[] obs = (GameObject[]) Object.FindObjectsOfType(typeof(GameObject));


    Thanks!
     
  9. Scrat

    Scrat

    Joined:
    Apr 20, 2008
    Posts:
    316
    Ah! This is a huge debate to some C# developers.
    In short, for static arrays (like the one you have) it doesn't make any difference. But for Collections, it seems using foreach is a little slower.
    You can Google it to know exactly why.

    Basically you tell Unity to return you all the object of the type GameObject (Object.FindObjectsOfType(typeof(GameObject))) and then you ask C# to return the result as an array of GameObject (this is called Casting)
    you can use:
    Code (csharp):
    1. (GameObject[]) Object.FindObjectsOfType(typeof(GameObject));
    or
    Code (csharp):
    1. Object.FindObjectsOfType(typeof(GameObject)) as GameObject[];
    And then you ask C# to put the result of the command to an array of GameObject called obs.

    Hope it's a little clearer :)
     
  10. NCarter

    NCarter

    Joined:
    Sep 3, 2005
    Posts:
    686
    Regarding this line of code:

    Code (csharp):
    1. GameObject[] obs = (GameObject[]) Object.FindObjectsOfType(typeof(GameObject));
    I was confused for a moment because I recall not being able to do this in the past, but when I tried it to make sure, it compiled without errors. However, on closer inspection, the error occurs when you run the game, so it's no good like that. It seems that you're not allowed to cast an array of some base class type (such as Object) to an array of a type derived from it (such as GameObject).

    I can think of a couple of ways of tackling this with a built-in array. One is to just leave the return type as it is, then use a foreach loop to do the cast:

    Code (csharp):
    1. Object[] objects = FindObjectsOfType(typeof(GameObject));
    2.  
    3. foreach(GameObject g in objects)
    4. {
    5.     DoSomethingWithGameObject(g);
    6. }
    Or slightly less conveniently with a normal for loop:

    Code (csharp):
    1. Object[] objects = FindObjectsOfType(typeof(GameObject));
    2.  
    3. for(int i = 0; i < objects.Length; ++i)
    4. {
    5.     DoSomethingWithGameObject((GameObject)g);
    6. }
    The other is to use System.Array.ConvertAll in this rather horrifying way:

    Code (csharp):
    1. Object[] objects = FindObjectsOfType(typeof(GameObject));
    2. GameObject[] gameObjects = System.Array.ConvertAll<Object, GameObject>
    3.     (objects, delegate(Object o) {return (GameObject)o;});
    4.  
    5. gameObjects[0].name = "Example";
    The former is obviously easier to type, but the latter is useful if you want to do the conversion up front so that you can deal with an array of the right type everywhere else.

    Matthew's ArrayList suggestion is also good if you aren't dealing with functions that return built-in arrays.

    If anyone knows of a more succinct way to do this, please tell me!
     
  11. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    It compiles and runs fine on the Unity builds I use. It's perfectly legal to cast a base class to a derived type. In case of the array, as long as the array was initialized with the derived type, the cast should work.

    Edit: corrected misinformation ;)
     
  12. NCarter

    NCarter

    Joined:
    Sep 3, 2005
    Posts:
    686
    True, but the problems I'm seeing are to do with casting an array of base class to an array of derived class, which isn't quite the same thing.

    Interesting. I tested again and it does work for GameObject with FindObjectsOfType, but it doesn't work for SomeScript with GetComponentsInChildren:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Test : MonoBehaviour
    5. {
    6.     void Start()
    7.     {
    8.         // This works:
    9.  
    10.         GameObject[] gameObjects = (GameObject[])FindObjectsOfType(typeof(GameObject));
    11.         foreach(GameObject g in gameObjects)
    12.             Debug.Log(g.name);
    13.  
    14.         // This causes an exception at runtime:
    15.  
    16.         Test[] tests = (Test[])GetComponentsInChildren(typeof(Test));
    17.         foreach(Test t in tests)
    18.             Debug.Log(t.name);
    19.     }
    20. }
    21.  
    The second part causes this to happen:

    Code (csharp):
    1. InvalidCastException: Cannot cast from source type to destination type.
    2. Test.Start ()   (at Assets/Scripts/Test.cs:16)
    I don't really understand why one works but not the other. Any ideas?

    EDIT: Also, (Test[])FindObjectsOfType(typeof(Test)) works. I suppose it's something to do with GetComponentsInChildren.
     
  13. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    When I said "In case of the array, as long as the elements in the array are all GameObjects, the cast should work", I was wrong.

    An array cast to a derived type only works if the Array itself was initialized with that derived type. The following code will cast correctly:
    Code (csharp):
    1.  
    2. Object[] o = new GameObject[2];
    3. GameObject[] go = (GameObject[]) o;
    4.  
    But the following wont:
    Code (csharp):
    1.  
    2. Object[] o = new Object[2];
    3. o[0]= new GameObject();
    4. o[1]= new GameObject();
    5. GameObject[] go = (GameObject[]) o;
    6.  
    So even when all contents are GameObjects, the cast throws an exception.

    Apparently FindObjectsOfType returns an array initialized as an array of the requested type, whereas GetComponentsInChildren returns an array initialized as an array of type Component.

    For the latter case, you can create an array of the right type by casting each element individually to the desired type. Since that is rather annoying, it might be worth a feature request to change the code of GetComponentsInChildren ;).
     
  14. NCarter

    NCarter

    Joined:
    Sep 3, 2005
    Posts:
    686
    That's very revealing! Thanks for clarifying that.
     
  15. amcclay

    amcclay

    Joined:
    Aug 8, 2006
    Posts:
    143
    Hey guys. Thanks for the continuing help. Curious about another thing.

    My goal here is to identify each object in my scene that is a MODEL (i.e. has a mesh) - So, I would ideally not like things like Camera or an empty "GameObject" added to this array.

    Is there a way to construct an array of only the objects that have a mesh? Right now I am simply constructing the array as instructed above and then sorting through it to identify the objects with meshes like this:

    Code (csharp):
    1.  
    2.     void printNames(GameObject[] localObjs)
    3.     {
    4.    
    5.         foreach (GameObject myObj in localObjs)
    6.         {
    7.             print(myObj.name);
    8.                        
    9.             if (myObj.GetComponent(typeof(MeshFilter)))
    10.             {
    11.                 print("has a mesh");
    12.             }
    13.            
    14.         }
    15.     }
    16.  
    Is there a way to do this in the first place where I construct the array using this command:

    GameObject[] obs = (GameObject[])Object.FindObjectsOfType(typeof(GameObject));


    Thanks!
     
  16. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    FindObjectsOfType() can take in other types, such as MeshFilter...
     
  17. col000r

    col000r

    Joined:
    Mar 27, 2008
    Posts:
    699
    Any way I can get the inactive GameObjects as well?