How does Inheritance work in Unity?

As the title says, how does C# class inheritance work in Unity, specifically the constructors? I’ve practiced using derived classes in console project and am now trying to use it in unity.

With class constructors, the parent constructor is called and then the child constructor in called right after that. But since Unity scripts don’t have constructors, how does this work? I’ve been having Object reference errors because of this. Can someone clarify this for me? Thanks.

Are you using [System.Serializable] above your class declarations?

No, I’m not. Should I be?

Yes. Unity needs you to do this, else it doesn’t see the code. Example.

using UnityEngine;
using System.Collections;

[System.Serializable]

public class ParentClass
{
        // Some code for things

}
using UnityEngine;
using System.Collections;

[System.Serializable]
public class ChildClass : ParentClass
{
       //Some code to do business

}

Even if you want to add a new class inside the code you already have you have to do it. Example.

using UnityEngine;
using System.Collections;

public class ClassYouMade : MonoBehaviour {

void Update() {
//code...
}

}

[System.Serializable]
public class SomethingYouMightNeed
{

//code...

}

Classes that inherit MonoBehaviour have to be seen as magic. The constructor is called internally by Unity. The only thing we are getting is those magic functions like Awake, Start, Update, … .
There is certainly a constructor, but we are not supposed to override or use it, because it is an internal thing in Unity for classes inheriting MonoBehaviour. If you need an instance of such a class, you have to use AddComponent.

1 Like

So I have this script/class called ShipFire. As you can see, it declares some variables, does some stuff in start and update. I cut out the rest of ShipFire’s methods to make this post not so long. I then have another class called EnemyShipFire that inherits from ShipFire. EnemyShip fire just needs to change the targetShip property and call an InvokeRepeting() when the script start is called. However it seems that EnemyShipFire’s Start function is overiding ShipFire start function.

The underlaying question is how can have a derived class change some properties and methods of the parents, and leave the rest the same? And how should you handle the Start function of a derived class?

ShipFire

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[System.Serializable]
// This fires the ship and manages the bullets
public class ShipFire : MonoBehaviour {
    // Reference to the bullet prefab
    public GameObject bullet;

    // Refrence to the barrel GameObject transform
    // Used to place the starting bullet location
    public Transform barrelTransform;

    // Contains all of the bullets
    List<GameObject> bulletList{ get; set;}

    protected float expireDistance{get; set;}
    // The bullets will hitt and do damged to ships that have this tag
    protected string targetShip {get; set;}

    void Start () {
        Debug.Log ("ShipFire consturctor");
        bulletList = new List<GameObject>();
        expireDistance = 20f;
        targetShip = "EnemyShip";
    }
  
    // Update is called once per frame
    void Update () {
        CheckBulletExpire();
    }
    // I bunch more methods down here
}

EnemyShipFire

using UnityEngine;
using System.Collections;

[System.Serializable]
// This class inherits from ShipFire and, since ShipFire inherits from MonoBehaiorm so does this class
public class EnemyShipFire : ShipFire {

    // Use this for initialization
    void Start () {
        Debug.Log ("EnemyShipFire start");
        InvokeRepeating("Fire",.5f,.5f);
        targetShip  = "Ship";
    }

    void Update(){

    }
}

In this case, you are dealing with the usual inheritance. Make Start virtual in ShipFire and override it in EnemyShipFire. This is basic inheritance, with the exception that the first Start is kind of magic. I don’t see what else is not clear, so just ask.

You don’t need System.Serializable when you inherit MonoBehaviour.

1 Like

So does the below code match your picture of usual inheritance? And what do you mean by ‘magic’? Im not sure that I’m getting the analogy.
Parent

    protected void Start () {
        Debug.Log ("ShipFire consturctor");
        bulletList = new List<GameObject>();
        expireDistance = 20f;
        targetShip = "EnemyShip";
    }
   
    // Update is called once per frame
    protected void Update () {
        CheckBulletExpire();
    }

Child

    override void Start () {
        Debug.Log ("EnemyShipFire start");
        // Call the parent Start
        base.Start();
        
        // make child changes
        InvokeRepeating("Fire",.5f,.5f);
        targetShip  = "Ship";
    }

    override void Update(){
         base.Update();
    }

As you want to override Start and Update, they need to be virtual in the base class. It is pretty late here, so excuse me if I might not get this correct: As in the base class Start and Update are both protected, they can’t be private in the child class as you have it right now. There is a logical reason for that, I just don’t have the energy for it. However, there are plenty of resources online about that.
All you are asking here is standard inheritance in C#, so you can really use almost any online tutorial about it. The only magic in Unity is, that Unity can call it’s magic methods like Start, … even if they are private or protected.

The Magic Dantus is referring to is Reflection. Unity calls the methods in a MonoBehaviour’s lifecycle by checking a MonoBehaviour for each of the special Unity events via reflection and invoking them if they exist.

There has been much discussion about whether user scripts that inherit from MonoBehaviour should have used the typical C# inheritance method using the virtual and override keywords vs. the reflection technique. However, that was a design decision made eons ago and this is what we’ve got. If your MonoBehaviour class has one of the special functions in it, it will “magically” be called by the Unity framework.

Anyways, we can still use the typical model for inheritance in our scripts. It’s just the first level of invocation which is special.

class ShipFire : MonoBehaviour
{
    protected virtual void Start () {
        Debug.Log ("ShipFire consturctor");
        bulletList = new List<GameObject>();
        expireDistance = 20f;
        targetShip = "EnemyShip";
    }

    // Update is called once per frame
    protected virtual void Update () {
        CheckBulletExpire();
    }
}
class EnemyShipFire : ShipFire
{
    override void Start () {
        Debug.Log ("EnemyShipFire start");
        // Call the parent Start
        base.Start();
    
        // make child changes
        InvokeRepeating("Fire",.5f,.5f);
        targetShip  = "Ship";
    }

    override void Update(){
         base.Update();
    }
}

Thanks for the help.

I guess that one of the reasons was that some classes didnt need an Update method - using relfection they could avoid calling it if it wasnt present. Saving some execution time.

Even if it is faster, it is not a very smart idea because modern compilers catch a lot of errors. E.g. if you make a typo in OnCollissionEnter or start, everything seems right, but nothing is being executed. This issue happens to many people, I am helping just because of that on a regular basis.
There are other approaches to get the same performance e.g. by using interfaces. Those standard mechanisms would definitely be better than using reflection.

Yup. I’m picking it will come in Unity 6.0 or 7.0 or there abouts. There were a bunch of design decisions that in hindsight don’t make that much sense. Trouble is fixing them will break a lot of projects. Using a string based system is never a good idea. And that’s essentially what Unity’s magic method system is.

There has been some progress. The quick accesors got nerfed in 5.0, so there is no need to remember to cache the result of a property. SendMessage and all of the associated stringy goodness that resulted got replaced with the EventSystem stuff in 4.6. Unity Technologies is slowly progressing on making the engine meet more standards.

1 Like

I don’t get why they did that. They can still use reflection to determine if a method is overridden or not. It’s pretty simple - if the method is not overridden don’t call it. I think the first implementation was wrong and they are now too scared to change it. It’s a pity since now they have that magic script that updates all of your source code for you.

That’s not much better and still feels “magical”. Not to mention that empty virtual methods that don’t do anything just seem…weird.

I’d much rather have explicit interface implementations for stuff. IUpdatable mandates an Update method and the engine gathers every instance of it’s implementation and executes them.

Yes, but this gets very messy very quickly:

public class ExampleOriginalMonoBehavior : IAwake, IOnEnable, IStart, IOnApplicationPause, IFixedUpdate, IUpdate, ILateUpdate, IOnPreCull, IOnBecomeVisible, IOnBecomeInvisible, IOnWillRenderObject, IOnePreRender, IOnRenderObject, IOnPostRender, IOnRenderImage, IOnGUI, IOnDrawGizmos, IOnDestroy, IOnApplicationQuit, IOnDisable, MonoBehavior
{

}

And now we have another problem, forgetting to add the interface declaration. Forgetting to add IAwake, becomes just as tedious and frustrating as misspelling it as “Aweke()” in our current implementation.

1 Like

True. Could be partially rectified by spitting out warnings if you use one of those method names without implementing the interface. And yes, it would probably force you to have more MonoBehaviours that were more specialized; but that could be a good thing. :slight_smile:

1 Like

Not a big fan of the “we think you probably mean this, so we will spam warnings at you for the rest of your life”. For example, having an Animator and UI Button(which usually utilize each other) on the same GameObject will spit annoying warnings(5 of them) if you use the Animator for something else. Even if you have the Button not set to “Animation” transitions.

1 Like

Not sure I’m following your use case…