Search Unity

Array of Components using Behaviour or Monobehaviour array/list

Discussion in 'Scripting' started by Maffew, Nov 26, 2014.

  1. Maffew

    Maffew

    Joined:
    Jan 22, 2014
    Posts:
    14
    Hi all,

    I'm trying to return a list of components attached to a Gameobject and have the ability to enable/disable them.
    Because I don't know what kind of components are attached to each Gameobject I'm returning an array of them all using:
    componentObject.GetComponents<Component>();
    The problem I'm having with this is that I can't do Component.enabled = false.
    Because of this I'm trying to list the Components as Behaviour or Monobehaviour objects but am having trouble filling my array.

    Code (CSharp):
    1. public class ActivateDeactivateComponent : EventScript
    2. {
    3.     public GameObject componentObject;
    4.  
    5.     public Component[] components;
    6.     public MonoBehaviour[] monoComponents;
    7.  
    8.     void Start()
    9.     {
    10.         if(componentObject!=null)
    11.         {
    12.             components = componentObject.GetComponents<Component>();
    13.             monoComponents = componentObject.GetComponents<MonoBehaviour>();
    14.         }
    15.     }
    my components array fills as expected but my monoComponents array has 0 elements. I have also tried:

    Code (CSharp):
    1. monoComponents = componentObject.GetComponents(typeof(MonoBehaviour));
    and

    Code (CSharp):
    1. monoComponents = componentObject.GetComponents<Component>() as MonoBehaviour;
    but nothing seems to fill the list.
    Any help with this would be fantastic.

    Thanks,

    Matt
     
  2. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    First to your code directly.
    • GetComponents<MonoBehaviour>() might not return deactivated objects so this might be a problem. You would have to use GetComponentsInChildren which has a boolean option to also include deactivated ones.
    • GetComponents<>() returns an array of elements so to cast it to an array of something else you would use "as MonoBehaviour[]". This method won't work in this case as casting only changes the "type" and doesn't repackage it into Monobehaviours.
    • You can manually loop through your array, cast each one to a monobehaviour check if it really is monobehaviour (!= null) and put it into a new array of type monobehaviour.
    • You could do the above using linq
      Code (csharp):
      1.  
      2. monoComponents = components.OfType<MonoBehaviour>().ToArray();
      3.  
    • The drawback of your method of using MonoBehaviours is that a BoxCollider for instance isn't a monobehaviour and so it won't be returned this way.

    My Solution
    There is a little known class in between component and monobehaviour called behaviour. This class provides the enabled field for all subclasses (including MonoBehaviours) as can be seen in the documentation.
    using
    Code (csharp):
    1.  
    2. components = GetComponents<Behaviour>()
    3.  
    You have the enabled field available directly without any further casting.
    Unfortunately colliders have their own enabled field inherited from the collider class so you would have to add a GetComponents<Collider>() and loop through them separately if this is desired.
     
    ArgVzla likes this.
  3. Maffew

    Maffew

    Joined:
    Jan 22, 2014
    Posts:
    14
    Hi,

    Thanks for your reply.
    I dabbled with Behaviour but had the same result. I couldn't get it to fill an array and it just appears empty in the inspector window.

    Code (CSharp):
    1. public Behaviour[] behaviourComponents
    2.  
    3. behaviourComponents = componentObject.GetComponents<Behaviour>();
    4.  
    returns no errors in the console but the array is still empty.
     
  4. billykater

    billykater

    Joined:
    Mar 12, 2011
    Posts:
    329
    Did you try using
    Code (csharp):
    1.  
    2. behaviourComponents = componentObjects.GetComponentsInChildren<Behaviour>(true);
    3.  
    This will include all inactive objects (and child objects).
     
  5. Maffew

    Maffew

    Joined:
    Jan 22, 2014
    Posts:
    14
    yeah, still has an array size of 0 :(
     
  6. Maffew

    Maffew

    Joined:
    Jan 22, 2014
    Posts:
    14
    Is this because only scripts, lights, cameras etc extend Behaviour, whereas Colliders, Mesh Renderers, Particle systems etc don't ?
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,706
    As @billykater mentioned, not all components are derived from Behavior. Some, like Rigidbody and Animator, have their own, independent enabled properties. The method below handles them. If I missed any components, just add them in using the same principle. :)
    Code (csharp):
    1. public void SetComponentEnabled(Component component, bool value) {
    2.    if (component == null) return;
    3.    if (component is Renderer) {
    4.      (component as Renderer).enabled = value;
    5.    } else if (component is Collider) {
    6.      (component as Collider).enabled = value;
    7.    } else if (component is Animation) {
    8.      (component as Animation).enabled = value;
    9.    } else if (component is Animator) {
    10.      (component as Animator).enabled = value;
    11.    } else if (component is AudioSource) {
    12.      (component as AudioSource).enabled = value;
    13.    } else if (component is MonoBehaviour) {
    14.      (component as MonoBehaviour).enabled = value;
    15.    } else {
    16.      Debug.Log("Don't know how to enable " + component.GetType().Name);
    17.    }
    18. }
    19.  
    20. // Disable all components:
    21. foreach (var component in componentObject.GetComponents<Component>()) {
    22.     SetComponentEnabled(component, false);
    23. }
     
    FileThirteen and Maffew like this.
  8. Ricks

    Ricks

    Joined:
    Jun 17, 2010
    Posts:
    650
    Can it be a different issue? You know, sometimes simple things make us go crazy...

    You have exposed a variable in the inspector: public GameObject componentObject;

    Now if you drag&dropped an object in there... did this object have any MonoBehaviours on it?