Performance cost of implementing Update() in every script via inheritance?

Greetings,

I had it in mind to create a base MonoBehavior script class, which provides virtual base implementations of all the primary update functions: Update, FixedUpdate, and LastUpdate. Each game script would then inherit from this base class (instead of MonoBehavior), and simply override any of the functions that it needs.

A simple implementation of the idea looks like this:

public class BehaviorBase : MonoBehaviour
{
    [SerializeField]
    private bool _updateWhenPaused = false;

    public bool UpdateWhenPaused
    {
        get { return _updateWhenPaused; }
        set { _updateWhenPaused = value; }
    }

    // The GameStateManager is a singleton which tracks the paused/unpaused
    // state of the game
    protected GameStateManager GameState { get { return GameStateManager.Instance; } }

    private void Awake()
    {       
        OnAwake();
    }

    private void Start()
    {
        OnStart();
    }

    private void Update()
    {
        IfNotPaused(OnUpdate);
    }

    private void FixedUpdate()
    {
        IfNotPaused(OnFixedUpdate);
    }

    private void LateUpdate()
    {
        IfNotPaused(OnLateUpdate);
    }

    // Proxy function; will only call the provided action, if the game state
    // is 'not paused', or if the script is configured to Update when paused.
    private void IfNotPaused(Action action)
    {
        var paused = GameState.IsPaused;
        if(!paused || UpdateWhenPaused)
            action();
    }

    // Override these, to implement your initialization and update logic
    protected virtual void OnStart()  { }
    protected virtual void OnAwake()  { }
    protected virtual void OnUpdate()  { }
    protected virtual void OnFixedUpdate()  { }
    protected virtual void OnLateUpdate() { }
}

And the child class could look like this:

public class MyChildScript : BehaviorBase
{
    protected override void OnUpdate()
    {
        // Update logic here.  
    }
}

As you can see, this can be used to implement a convenient “Pause” system - the virtual OnUpdate*() functions are only called if the game is unpaused, or if the script has explicitly toggled its “UpdateWhenPaused” property.

The concern I have is; surely there is a reason Unity didn’t implement this type of virtual function hierarchy in the first place? Is there going to be a noticable performance hit by implementing all these functions ‘by default’ in the base class?

This is not recommended:

IfNotPaused(OnUpdate);

Here you will create a new delegate instance each time you call the IfNotPause method. This will create garbage. Your IfNotPaused method should simply return a bool or you replace it with a property and do this:

if (IfNotPaused)
    OnUpdate();

However keep in mind that if a MonoBehaviour implements a certain method it will be called by Unity which creates some overhead. A better implementation would be to either:

  • Don’t implement any of the callbacks in the base class but only provide an “IsPaused” property so the actual implementation can do a simple early exit if they wish : it(IsPaused) return;
  • Implement helper classes one for each callback type and only attach those which are needed. You could use reflection in Awake to check if one of your callback has been overridden and subscribe the callback in the appropriate helper class.

One helper class could look like this:

public class UpdateReceiver : MonoBehaviour
{
    public event Action OnUpdate;
    void Update()
    {
        if (OnUpdate != null)
            OnUpdate();
    }
}

So your base class could attach the appropriate Receiver component whenever it’s needed and subscribe to the OnUpdate event dynamically.

However that approach has a lot of memory overhead as for each callback you would need a seperate Receiver class. The first approach is the most flexible with least overhead. So let the final child class decide if it want to handle a specific event and if it want to ignore it when the game is paused or not. Having all those OnStart, OnAwake, … methods just makes life harder for everyone that is used to use Unity.