Search Unity

Convenience Attributes

Discussion in 'Scripting' started by TylerMorton, May 29, 2017.

  1. TylerMorton

    TylerMorton

    Joined:
    May 24, 2015
    Posts:
    2
    Hi everyone,

    I'm working on coming up with some new Attribute types that could increase the readability of code in my scripts.
    • GetComponentOnAwake & GetComponentOnStart attributes
    These two attributes can be paired with the [RequireComponent] attribute and used to automatically initialize the references to components in a script without having to call the GetComponent() function. In some cases, this could completely eliminate the need to write Awake or Start functions in some scripts just to initialize the required components.

    For example:
    Code (csharp):
    1. [RequireComponent(typeof(Health))]
    2. public class Player : MonoBehaviour
    3. {
    4.     [GetComponentOnAwake]
    5.     private Health health;
    6. }
    This would completely eliminate the need to write out the Awake function
    Code (csharp):
    1. private void Awake()
    2. {
    3.     health = this.GetComponent<Health>();
    4. }
    I know it's not a lot of work to implement the Awake function to do this, but it would eliminate some cases where you just completely forget. It would also increase readability when people on your team are looking at your scripts and wondering when and where things are initialized.

    My question is, what part of Unity can I hook into to create the functionality that detects these attributes and calls the GetComponent() function at the correct times?

    Thanks in advance,

    Tyler
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    So to do such a thing on Awake or Start would require a base class that reflected the fields with those attributes and did the work. Then all your scripts that used it would have to inherit from it.

    This then means all of your scripts have this extra overhead to initialize. Probably not the best idea.

    Instead... what I do is create a 'DefaultFromSelfAttribute', then a PropertyDrawer that hooks into this and if the field is null it attempts to get the component from itself.

    I also have a 'RequireFromSelfAttribute', who is like DefaultFromSelfAttribute, but if the component is not null, and is not a component on the same GameObject, it gets set to null.

    With that now the editor auto populates the fields (making these editor time configured).

    Down side is that if you add the component at runtime (and therefore don't use the editor), they don't auto populate. As a result I personally don't use the RequireFromSelfAttribute as often, since I end up having to initialize it in Awake anyways.
     
  3. UziMonkey

    UziMonkey

    Joined:
    Nov 7, 2012
    Posts:
    206
    I had done the same thing at one point, but decided against it. Instead I just make all component references like this [SerializeAttribute] privates and drag the component in the inspector. It literally takes 3 seconds to set up, is more flexible and 0 lines of code. Honestly a better way would be simply an editor snippet to save yourself some typing in Awake or Start if you don't want to drag references in the inspector. This is not really a problem that I think needs solving with a heavy hammer like attributes and reflection.

    However, if you really want to do this, you're going to have to subclass MonoBehaviour and implement Start and Awake functions there. You'll then have to use the reflection API to iterate over the fields of the class, then iterate over the attributes on those fields to look for your custom attribute. However, this may amount to a rather large cost as the reflection API can be quite slow. Remember that you'll have to set these fields through the reflection API as well. Then just remember that your behaviours will need to inherit from your class and not MonoBehaviour, and if they need an Awake or Start they need to override it and call base.Awake() or base.Start() to get auto-assigned component references.
     
  4. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    What I do mostly too.