When working a lot in Unity it's easy to get crashes and errors because you forget to set the correct data in components, or having forget to add a component type altogether.


This is common and there is no silver bullet to solve it bu there are however steps you can take while writing your code to decrease the chance of making mistakes. Many of these tricks are built-in in Unity's editor from start and very simple to get started with.


Adding tool tips

Naming variables is hard. They have to convey a lot of meaning while still being short. In programming, comments are there to help you when you need more information about a variable. Unfortunately they wont be visible in the inspector.


This is where the

[ToolTip("")] 
attribute comes in handy. Add this to any public variables in behaviors and they will be displayed when you mouse-over the field in the inspector. Write down what the variables does and any special considerations needed when setting it and help out your future self.


[Tooltip("This is an example of a tooltip")]
public string TestVariable;


There is no need to stick to short tool tips. Do you have a field with an advanced usage? Put a detailed description in the tool tip.

[Tooltip(@"TestVariable

This is an example of a tooltip that is very
long and uses explicit line breaks to get a long desciption in.
It might look bad in C# but Unity will display it just fine.")]
public string TestVariable;

Note: We're using a verbatim string here, signified by the @ prefix. It allows string to multi line and also takes your line breaks into consideration.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim

It can be a good idea to write tool tips for all public fields when adding them. If done at once the time spent will be minimal while the potential help to you and other people in your project, down the line, is enormous.


Here is a an example from real project of mine using tool tips for all fields.

[CreateAssetMenu(fileName = "new Instant Ability", menuName = "MageQuest/Abilities/Instant/Insant Ability")]
public class InstantAbility : Ability
{
  [Tooltip("Checks for cooldown only when the ability is cast")]
  public bool DontCheckCooldownOnSetup = false;
  [Tooltip("Checks for mana only when the ability is cast")]
  public bool DontCheckManaOnSetup = false;
  [Tooltip("Usually abilites are cast when the button is released")]
  public bool CastOnButtonDown = false;
}

https://docs.unity3d.com/ScriptReference/TooltipAttribute.html


Better layouts with Header and Space

A MonoBehaviour with lots of public fields will have them all lined up over each other and the inspector will look cluttered.


Consider this mocked Spell class. The fields are just there with no real though in where they're put or how they're grouped together. Not an uncommon situation for a real project.

public class TestSpell : MonoBehaviour
{
   public float ManaCost;
   public float CastTime;
   public float CastCooldown;
   public float DamageMin;
   public float DamageMax;
   public Element DamageType;
   public float Range;
   public string Name;
   public string DescriptionShort;
   public string DescriptionLong;
}


This is what it looks like in the inspector. Look at this mess. Try to remember what everything is and make sure you are editing the correct line otherwise things won't work as intended.


[Header("")]
and
[Space]
are two attributes made to help this issue.
[Header]
is displayed as a bold label above the field.
[Space]
adds some space between two fields.


Here is a reworked version of the mocked Spell class with its field sorted according to use and headers inserted where appropriate.

public class TestSpell : MonoBehaviour
{
   [Header("Descriptions")]
   public string Name;
   public string DescriptionShort;
   public string DescriptionLong;

   [Header("Casting")]
   public float ManaCost;
   public float CastTime;
   public float CastCooldown;
   public float Range;

   [Header("Damage")]
   public float DamageMin;
   public float DamageMax;
   public Element DamageType;
}


And here is the new look of its inspector. Not perfect but definitely an improvement.

https://docs.unity3d.com/ScriptReference/HeaderAttribute.html

https://docs.unity3d.com/ScriptReference/SpaceAttribute.html


Hide in inspector and Non serialized fields

One of the advantages when working in Unity is that all public fields will be accessible via the inspector automatically. One of the disadvantages is that ALL public fields will be accessible via the inspector. Sometimes fields are made public because the code requires it, but the field should not be editable in the inspector and might in fact break if you edit the value.


Unity's solution to this is the

[HideInInspector]
attribute. The name seems self explanatory. Code-wise the variable works the same as before and it will still be serialized like any other public field but it will not show up in the inspector.

// This public field wont be in the inspector but will be serialized
[HideInInspector]
public int Amount;


https://docs.unity3d.com/ScriptReference/HideInInspector.html


Unity will also serialize any public field to disk for prefabs, scriptable obejcts and objects in a scene. When this is not desired

[System.NonSerialized]
can be added to fields. Now the field will not be saved to disk it will not show up in the inspector.

// This public field wont be in the inspector and wont be serialized
[System.NonSerialized]
public int Amount;

Note: [System.NonSerialized] is not a Unity specific attribute but is part of the standard .NET/dotnetcore/mono framework.

https://docs.microsoft.com/en-us/dotnet/api/system.nonserializedattribute?view=netcore-3.1


Say that you have a private field and wants to protect it in your code (due to proper C# encapsulation discipline) but still want to edit it in the inspector?

[SerializeField]
got you covered. Add it to any private/protected field and it will be serialized and displayed in the inspector.

// This is a private field but will be in the inspector and will be serialized
[SerializeField]
private int Amount;


https://docs.unity3d.com/ScriptReference/SerializeField.html


Avoid errors and null checking with [RequireComponent(Type)]


Pretend that you have a very simple MonoBehaviour that will interact with a Rigidbody component.

public class RequireComponentExample : MonoBehaviour
{
   public void Push()
   {
       GetComponent<Rigidbody>().AddForce(transform.forward);
   }
}

If this GameObject has no Rigidbody component attached this piece of code will throw a

NullReferenceException
because
GetComponent<T>()
will return null. The most common solution is to null check the valuefirst. It's a valid solution, and often, the best one.

public class RequireComponentExample : MonoBehaviour
{
   public void Push()
   {
       var rigidbody = GetComponent<Rigidbody>();
       if (rigidbody != null) {
           rigidbody.AddForce(transform.forward);
       }
   }
}

Unity does however have a RequireComponent attribute to ensure(1) that a GameObject have the required components you specify. The habit of using this attribute can save you from lots of null checking and/or crashes in your game and also helps you remember the script dependencies in your project.

[RequireComponent(typeof(Rigidbody))]
public class RequireComponentExample : MonoBehaviour
{
    public void Push()
    {
        GetComponent<Rigidbody>().AddForce(transform.forward);
    }
}

https://docs.unity3d.com/ScriptReference/RequireComponent.html


(1) Note: "Ensures" is a strong word to use. If you create a new game object and adds a script like this, Unity will not allow it without first having the required component types either already attached or will attach one automatically. If you add [RequireComponent] attributes to already existing scripts Unity will do nothing to make sure already created game objects and prefabs complies with this requirement until you're trying to edit them. Null checks might still be necessary.


Avoid circular dependencies when using
[RequireComponent]
. Here is a simple example where A requires B but B also requires A. This leads a circular dependency that can never be resolved. A requires B that requires A that requires B ad infinitum.
[RequireComponent(typeof(BehaviourB))]
public class BehaviourA: MonoBehaviour { }
[RequireComponent(typeof(BehaviourA))]
public class BehaviourB : MonoBehaviour { }


Conclusion

These are a few tips on how you can use Unity and C# attributes to make you Unity projects a bit less error prone and hopefully increase the stability of your projects. In later articles I hope I can dig deeper into how you can create custom attributes to bend Unity's editor to your will. Stay tuned!


Part II goes over how you can create your own PropertyAttributes and PropertyDrawers

/unity-editor-more-ticks-to-protect-you-from-your-self