Search Unity

How to change the material color of the object's children

Discussion in 'Scripting' started by febrarian2009, Aug 14, 2009.

  1. febrarian2009

    febrarian2009

    Joined:
    Jul 27, 2009
    Posts:
    37
    Hello, does anyone know already how to change the material color of the object's child?

    Say, I have a new Instantiated object and that object has its children, this would be the hierarchy:

    new_object ---------no material
    object_child1 ---has material
    object_child2 ---has material

    Once I instantiate the new_object, the script should find the object_child1's and object_child2's material and change its color, say to green.

    I currently have this code which doesn't work and throws a null reference exception:

    Code (csharp):
    1. Renderer[] childrenRenderer = new_object.GetComponentsInChildren(typeof(Renderer)) as Renderer[];
    2.             foreach(Renderer childRenderer in childrenRenderer)
    3.             {
    4.                 childRenderer.material.color = Color.green;
    5.             }
    Does anyone has a useful working code already there? Thanks.
     
  2. half_voxel

    half_voxel

    Joined:
    Oct 20, 2007
    Posts:
    978
    What line is throwing a null reference exception?
     
  3. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    For some unknown reason, GetComponentsInChildren constructs its return array as a Component[]. This means that Mono will prevent you from casting it to a Renderer[], even if it contains only Renderer objects. To fix your code, simply change childrenRenderer to a Component[] (the foreach will then work unaltered).

    Also, I recommend to cast using (Component[]) rather than 'as Component[]'. Casting with 'as' returns null when something goes wrong, whereas casting with '()' immediately causes an appropriate exception, making it much clearer what went wrong.
     
  4. duck

    duck

    Unity Technologies

    Joined:
    Oct 21, 2008
    Posts:
    358
    The reason is probably that Unity's Javascript does not support Generics. Without a generic version of this method, what else could it return its results as? there are almost always more than one kind of component, and 'Component' is the nearest shared common ancestor. Component[] is the most sensible choice.

    However, a neat way to solve the above problem is to do the casting within the foreach declaration:

    Code (csharp):
    1. Component[] renderers = new_object.GetComponentsInChildren(typeof(Renderer));
    2. foreach(Renderer childRenderer in renderers)
    3. {
    4.     childRenderer.material.color = Color.green;
    5. }
    hope this helps!

    - Ben
     
    DMWijesuriya likes this.
  5. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    The problem is not that it returns a Component[]. That makes sense. The problem is that it creates the array as a Component[] instead of a Renderer[] (it knows this type, since that's a parameter of the function). This prevents casting to the derived type directly. Similar functions like FindObjectsOfType do this correctly and their results can indeed be cast to the derived type.

    I made a feature request to fix GetComponentsInChildren. In that request, I attached the following script to demonstrate the problem and the solution:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ArrayCastingProblem : MonoBehaviour
    6. {
    7.     void Start ()
    8.     {
    9.         // desired behaviour, the result of GetComponentsInChildren can be cast directly
    10.         ArrayCastingProblem[] me = (ArrayCastingProblem[]) GetComponentsInChildrenDesired(typeof(ArrayCastingProblem));
    11.         Debug.Log(me[0].name);
    12.  
    13.         // current behaviour, casting the result of GetComponentsInChildren causes an exception
    14.         me = (ArrayCastingProblem[]) GetComponentsInChildrenCurrent(typeof(ArrayCastingProblem));
    15.         Debug.Log(me[0].name);
    16.     }
    17.    
    18.     // mockup of the current implementation of GetComponentsInChildren (obviously the real function does more than this one)
    19.     // note that casting the result to a derived type causes an exception
    20.     Component[] GetComponentsInChildrenCurrent(System.Type t)
    21.     {
    22.         Component[] returnArray = new Component[1];
    23.         returnArray[0] = GetComponent(t);
    24.         return returnArray;
    25.     }
    26.  
    27.     // mockup of the desired implementation of GetComponentsInChildren (obviously the real function does more than this one)
    28.     // note that casting the result to the derived type works as expected
    29.     Component[] GetComponentsInChildrenDesired(System.Type t)
    30.     {
    31.         Component[] returnArray = (Component[]) System.Array.CreateInstance(t, 1);
    32.         returnArray[0] = GetComponent(t);
    33.         return returnArray;
    34.     }
    35. }
    Note that the only difference between the two functions is that the one not causing an exception initializes the array as type 't' rather than just Component[]. No generics needed at all.
     
  6. duck

    duck

    Unity Technologies

    Joined:
    Oct 21, 2008
    Posts:
    358
    Gotcha... :) thanks for the detailed explanation.

    Hope they implement that fix (although could that break existing code, regarding webplayer content?)

    In the meantime, I think casting in the foreach declaration is probably the neatest solution.

    - Ben
     
  7. febrarian2009

    febrarian2009

    Joined:
    Jul 27, 2009
    Posts:
    37
    Thanks to all of you guys for clear explanations, I'm glad that it works now. :D THANKS A MILLION!!! :D
     
  8. DMWijesuriya

    DMWijesuriya

    Joined:
    Jun 21, 2018
    Posts:
    1
    Thank You So much