Search Unity

  1. Unity 2018.3 is now released.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  5. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  6. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

A different RequireComponent

Discussion in 'Scripting' started by Rodolfo-Rubens, Apr 29, 2017.

  1. Rodolfo-Rubens

    Rodolfo-Rubens

    Joined:
    Nov 17, 2012
    Posts:
    1,059
    Hey guys, I made a simple script that adds a useful functionality for component fields in inspector, instead of the "RequireComponent (typeof(CapsuleCollider))" attribute in the class I do this inside the class:

    Code (csharp):
    1. [RequiredComponent]
    2. public CapsuleCollider col;
    So, it will automatically get/add the required components to the col field when you add the monobehavior to an object, just like RequireComponent does but this also fills the field with the attribute.

    Here is the code:
    RequiredComponentDrawer.cs
    Goes in an Editor folder.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [CustomPropertyDrawer(typeof(RequiredComponentAttribute))]
    5. public class RequiredComponentDrawer : PropertyDrawer
    6. {
    7.     public override void OnGUI (Rect position, SerializedProperty property, GUIContent label)
    8.     {
    9.         EditorGUI.PropertyField(position, property);
    10.  
    11.         MonoBehaviour mono = property.serializedObject.targetObject as MonoBehaviour;
    12.         //if(fieldInfo.FieldType.IsSubclassOf(typeof(Component)) )
    13.         if (typeof(Component).IsAssignableFrom(fieldInfo.FieldType))
    14.         {
    15.             if(property.objectReferenceValue == null)
    16.             {
    17.                 Component comp = mono.GetComponent(fieldInfo.FieldType);
    18.              
    19.                 if(comp == null)
    20.                 {
    21.                     comp = mono.gameObject.AddComponent(fieldInfo.FieldType);
    22.                 }
    23.              
    24.                 property.objectReferenceValue = comp;
    25.                 //property.serializedObject.ApplyModifiedProperties(); // not sure if this is really needed
    26.             }
    27.         }
    28.         else
    29.         {
    30.             Debug.LogError("Field <b>" + fieldInfo.Name + "</b> of " + mono.GetType() + " is not a component!", mono);
    31.         }
    32.     }
    33. }
    RequiredComponentAttribute.cs
    Goes outside the editor folder.
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. public class RequiredComponentAttribute : PropertyAttribute
    4. {
    5. }
    6.  
    I hope this helps someone!

    Forgot to mention, this was based on this feedback: https://feedback.unity3d.com/suggestions/automatically-inject-requirecomponent-fields
     
    Last edited: Apr 29, 2017
  2. goldbug

    goldbug

    Joined:
    Oct 12, 2011
    Posts:
    631
    You are awesome Rodolfo! that is exactly what I wanted in my feedback.
     
    Rodolfo-Rubens likes this.
  3. minism

    minism

    Joined:
    Feb 11, 2016
    Posts:
    1
    For anyone else discovering this awesome thread and wishing that the above solution would work for non-public fields (I can't shake the uncomfortable feeling of making fields public that don't need to be), I have an alternative solution that requires inheriting from a base class.

    Base class code:

    Code (CSharp):
    1.  
    2. using System.Reflection;
    3. using UnityEngine;
    4.  
    5. public class BaseMonoBehavior : MonoBehaviour {
    6.  
    7.   protected void OnValidate() {
    8.     foreach (var field in GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) {
    9.       var attributes = field.GetCustomAttributes(typeof(RequiredComponentAttribute), true);
    10.       if (attributes.Length > 0) {
    11.         Debug.Log(field.Name);
    12.         System.Type fieldType = field.FieldType;
    13.         Component component = gameObject.GetComponent(fieldType);
    14.         if (component == null) {
    15.           component = gameObject.AddComponent(field.FieldType);
    16.         }
    17.         field.SetValue(this, component);
    18.       }
    19.     }
    20.   }
    21.  
    22. }
    23.  
    Property definition:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class RequiredComponentAttribute : PropertyAttribute { }
    5.  
    And finally, usage:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class Foo: BaseMonoBehavior {
    5.  
    6.   [RequiredComponent]
    7.   private SpriteRenderer renderer;
    8.  
    9.   // Use this for initialization
    10.   void Start() {
    11.     renderer.color = Color.black;
    12.   }
    13. }
    14.  
    The OnValidate hook seemed like a good place for this.
     
    goldbug and Rodolfo-Rubens like this.