What is the best strategy to extend Unity defined methods in subclasses?

I’m looking for a way to call a class’ parent methods such as Awake, Update etc. It seems that the privacy level of those Unity defined methods is private, so I cannot call base.Awake(); from the child class. In the following context, if MySubClass is attached to a game object, none of the parent’s methods gets called

class MyClass : MonoBehaviour {
    void Awake() {
        Debug.Log("Parent class awaken");
    }
    void Start() {
        Debug.Log("Parent class started");
    }
}

class MySubClass : MyClass {
    void Awake() {
        Debug.Log("Child class awaken");
    }
}

I know I can declare those methods as protected in the parent class and this will then work ok. I was wondering if there was a best way to do it. This is relevant to me for two reasons:

  • in the context of extending plugin scripts, I’m reluctant to change them in case this code gets an update.
  • If I want children classes to have the same method for let’s say Update, I want to write it in the parent class only and nothing in the children classes. But the parent’s Update method doesn’t get called.

Out of curiosity, what is the reason that the privacy level of those methods is so strong? I’ve found it not very helpful :slight_smile:

Lot of thanks for the help!!

I’m not sure what you mean by “the level of those methods is so strong”. You can give them whatever level you like. They are allowed to be private because generally one doesn’t want them being called from outside the class, and they are allowed to be protected so that one can derive as you are doing. I’m not sure I’ve ever needed them to be public (anyone got a use case for that?).

But anyway, personally I don’t think there’s a right answer to this. There are various idioms you can use and they all have their place, it depends on what kind of class structure you’ve got.

Most often I will just make Update() etc protected and have the overrides call the base version (if it’s not abstract) as required.

You say that the base’s Update() doesn’t get called - but it does, if you call it explicitly with base.Update(). You don’t want this to happen automatically because sometimes you do want the derived class’s Update() to replace that of the base class. And even when you want the base Update() called too, sometimes you want it called at the start of the Update() override and sometimes at the end or in the middle).

The more lightweight the base class, the more likely the above is to be sufficient (including the extreme example where the base class is just an interface).

But sometimes I do things the other way round - make the base class’s version private and have it call virtual protected helper functions which are specialised in the derived classes. This might work better when, for example, all the derived classes use a common sequence of processing steps. So, rather than duplicate that sequence in the derived classes, one implements the overall process in the base class then creates the appropriate helper functions in derived classes to create the different behaviours.

There are of course other ways to structure the kind of stuff I describe above (other Unity techniques as well as other OOP techniques). I just give those examples to show that there isn’t a single “best way” to do what’s being asked about.

I’m not sure there’s a right answer to the plug-ins question either. In an ideal world one wouldn’t change things, for the reason you state. If a plug-in isn’t letting you do what you need then it might be worth contacting the developers about it to start with. But there are times I’ve had to fiddle with their code, I think sometimes it’s the simplest thing to do - even if it might create a bit of work down the line when the plug in gets updated, it might be worth it for the benefit it gives your project immediately and overall. I’d always try to make my changes as minimal as possible, that’s for sure.

The problem is all those callback don’t exist at all in the MonoBehaviour class. Unity used some sort of reflection to detect if a class implements one of those callbacks and only calls it if necessary. This has problem that Unity don’t call some sort of base-class method which you override, but it detects the top most implementation and calls it directly.

However OOP still works exactly the same. You are in charge to define those methods in your baseclass. Obviously if you want to derive your class those methods need to be at least protected and virtual. Actually for Unity callbacks they don’t “need” to be virtual since Unity already calls the top most implementation itself. However if you use polymorphism, do it the right way ^^.

public class Base : MonoBehaviour
{
    protected virtual void Awake()
    {
        Debug.Log("Base:Awake");
    }
    protected virtual void Update()
    {
        Debug.Log("Base:Update");
    }
}

public class Derived : Base
{
    protected override void Update()
    {
        base.Update(); // call the base method if needed
        Debug.Log("Derived:Update");
    }
}

A lot people misuse polmorphism in several ways. For example a derived class should never be forced to call the base implementation of an overridden method. It can, but doesn’t have to. The point of overridable methods is to provide a new / alternative / extended method and calling the base class should never be necessary. See this SO question.

If you want to provide an interface that the user can use to implement his actions, but you also need to do certain things before / after the users code, you should use a seperate empty method that the user can override. Unfortunately since Unity always calls the topmost implementation there is no “safe way” to provide a MonoBehaviour base class for the user to extend. Even when you declare Update private, the user still can implement his own Update and his is called instead of yours.

For plugins it’s better to use a base class that doesn’t inherit from MonoBehaviour. You can provide the a similar interface:

public class PluginBase
{
    protected virtual void Update()
    {
    }
}

internal class PluginContainer : MonoBehaviour
{
    public PluginBase plugin;
    void Update()
    {
        // do additional stuff before the plugin update
        if (plugin != null)
            plugin.Update();
        // do additional stuff after the plugin update
    }
    public static PluginContainer CreatePlugin(this GameObject aParent, System.Type aType)
    {
        if (!typeof(PluginBase).IsAssignableFrom(aType))
            return null;
        var container = aParent.AddComponent<PluginContainer>();
        container.plugin = (PluginBase)System.Activator.CreateInstance(aType);
        return container;
    }
}

This way you abstracted your plugin from your code. The actual plugin class is a normal class, so you just have to create an instance and assign it to the plugin variable of the container class.