Skip to content

Helpful Tips

Justin Orringer edited this page Jun 22, 2022 · 1 revision

This page is to act as a knowledge-base of useful tips and tricks that may be unknown to other project contributors.

Multiple Output Values

If you have a function that you want to have multiple outputs from, you can output multiple values without needing a new type to encapsulate everything within a single return value or the use of something like tuples.

C# and Unity support the use of out parameters within method signatures

Useful MonoBehaviour Functions

Below I have listed several common and useful functions that can be overridden within MonoBehaviour classes along with a brief bit of information about each. Note that all of these methods should have protected scope.

Function Description
Start Called on the first frame that the object is enabled. This function will only be called once per object per scene load. This will execute before any call to Update().
Update Called every frame when the MonoBehaviour instance is enabled.
Awake Called when the MonoBehaviour instance is first loading in. This is called before Start() and executes regardless of if the behavior is enabled.
FixedUpdate An update method called at a fixed frame-rate, generally used for calculations like physics that rely on consistency time intervals between calls. This may occur multiple times per frame.
LateUpdate Called at the end of each frame similarly to Update(), but all calls to Update() will have already completed for all objects.
OnEnable Called any time that the MonoBeaviour or its owning object becomes enabled. If the object should be subscribed to any delegates or events they should be re-subscribed to here.
OnDisable Called any time that the MonoBeaviour or its owning object becomes disabled. If the object is subscribed to any delegates or events they should be unsubscribed here.
OnDestroy Called when the MonoBehaviour instance or its owning object are destroyed. This is an ideal location for cleanup code.

For a mush more in-depth description of the life-cycle of a MonoBehaviour instance please see Unity's Documentation on Event Functions

Smooth Update Behaviors

To achieve smooth movements or calculations within Update(), you can multiply your values by Time.deltaTime which is the time that has passed between frames. This will ensure that your calculations are done based on real-time elapsed instead of frames. Note that behaviors inside of FixedUpdate() do not require this type of smoothing due to the fixed time interval between calls.

ScriptableObjects

If you need a collection of data and behaviors that would benefit from existing as a standalone asset in the project that can be repeatedly referenced, you will likely want to use a ScriptableObject. Examples of their use cases are creating item assets, creating abilities, creating enemy types, creating settings presets, and creating dialogues among many other things. For more information see the Unity Documentation.

Editor Only Code

If there is any editor only code mixed in with runtime code, you can strip out the editor code during non-editor builds by using preprocess directives. The needed directive is #if UNITY_EDITOR

An example of this can be seen in BuildABot.Utility.QuitGame():

public static void QuitGame(int exitCode, string message = null)
{
#if UNITY_EDITOR
    EditorApplication.isPlaying = false;
    if (exitCode != 0)
    {
        if (message == null) Debug.LogWarningFormat("Play in editor exited with code {0}", exitCode);
        else Debug.LogWarningFormat("Play in editor exited with code {0}: {1}", exitCode, message);
    }
    else Debug.Log("Play in editor exited with code 0");
#else
    Application.Quit(exitCode);
#endif
}

Coroutines

Coroutines are functions that can be executed over the course of several frames. Any MonoBehaviour can call StartCoroutine which takes in a function that returns the type IEnumerator, which acts as a type of handle for the behavior. StartCoroutine will begin execution of the function, which will continue until finished normally or forced to stop by StopCoroutine (which takes in the IEnumerator) returned by the function earlier.

This is a fairly large topic, so it may be helpful to look through the Unity Documentation for Coroutines.

As a convenience, the BuildABot.Utility class includes a wrapper for a WaitForSeconds coroutine called DelayedFunction which will execute a specified function after a given number of seconds. For simple delayed behaviors, this should be preferred over setting up new coroutines from scratch each time.

Some specific types of delays used in Coroutines can be seen here.

Ternary Operator ?:

In cases where you need to choose between two potential values based on a condition, instead of writing a verbose if else statement such as:

if (someCondition) result = valueA;
else result = valueB;

You can instead write that expression in a single line using the ternary (?:) operator like so:

var result = someCondition ? valueA : valueB;

The general form of the ternary operator is as follows:

condition ? valueA : valueB

where the expression evaluates to valueA if condition is true, or valueB if condition is false.

While a bit less straightforward to read than if-else statements when you are not familiar with them, this allows for inlining of conditional behavior and can even be nested for multiple conditions.

Null Conditional Operators ?. and ?[]

When accessing a member field, property, or method of an object, we can use ?. to check if the accessed object is null. The form of this operator is:

myObject?.someMethod()

If myObject is valid, the expression will evaluate like normal. If myValue is null, then the expression evaluates to null.

This same type of syntax can also be applied to array access:

myArray?[index]

For more information see the Microsoft C# Documentation.

WARNING: While these operators (?. and ?[]) are very useful for C# types, be careful using this for Unity types such as MonoBehaviours or GameObjects due to some internal implementation details of Unity Objects.

Null Coalesing Operator ?? and ??=

In a similar vein to the ternary operator, C# supports a null-coalesing operator ?? in the form value ?? otherValue where the operator checks if value is null before selecting the value of the expression. If value is not null then the expression evaluates to value, otherwise it evaluates to otherValue.

This is the equivalent of (null == value) ? value : otherValue.

Similarly, an assignment version of this operator exists value ??= otherValue where value will only be assigned the value otherValue if value is null. This is equivalent to if (null == value) value = otherValue;

WARNING: While these operators (?? and ??=) are very useful for C# types, be careful using this for Unity types such as MonoBehaviours or GameObjects due to some internal implementation details of Unity Objects.