- Home /
Using inheritance with scripts attached to different gameobjects : Base class Awake called multiple time, normal ?
Hi guys, I'm currently trying to manipulate and understand inheritance in unity.
In my scene lets say we have a parent game object called Example and a child game object called Example Child.
In the parent i put a script like :
public class Example : MonoBehaviour
{
private void Awake()
{
Debug.Log("Example : " + transform.name);
}
}
In the child :
public class ExampleSub : Example
{
private void Awake()
{
Debug.Log("ExampleSub : " + transform.name);
}
}
Results in the console will be :
Example : Example
ExampleSub : Example Child
Now lets say we do this :
In the parent i put the same script :
public class Example : MonoBehaviour
{
private void Awake()
{
Debug.Log("Example : " + transform.name);
}
}
In the Child :
public class ExampleSub : Example
{
// No awake function
}
Results in the console will be :
Example : Example
Example : Example Child
The way I understand this behavior is :
Example gameobject call attached Example Script which call Awake function
Example Child gameobject call attached ExampleSub Script which call Awake from Example Script base class.
The problem is : if I don't call an awake in the subclass, it will repeat all the code in the Awake from the base class. But I don't need all the stuff in the awake base class for my subclass. I will just need some variables from the base, but not to do all of its awake operations... The solution I found is to call an empty awake in the subclass. This way, the subclass doesn't execute the awake from the base class. But I don't know if it's a good practice... That seems a weird behavior. Does it exists any other option ?
I hope I'm clear in my explanations ^^
Answer by ADiSiN · May 13, 2020 at 12:42 AM
Hi!
Thank you for very good description of you issue. The solution for your desired behaviour can be virtual classes. Take a look at these lines of codes:
public class Example : MonoBehaviour
{
public virtual void Awake()
{
Debug.Log("Example : " + transform.name);
}
}
And the Child:
public class ExampleSub : Example
{
public override void Awake()
{
// base.Awake(); If you want to execute child code then simply use base.FunctionName(), if no just remove it
}
}
This will result in the Console single:
Example : parent
The thing with Virtual methods that you declare your base implementation of function, but when you inherit class you can override the function either to add something or to re-implement function completely. In order to execute base implementation of function use base.FunctionName();
IMPORTANT: For functions to be virtual they MUST be public. You cannot declare virtual private function.
Another option is abstract classes:
public abstract class Example : MonoBehaviour
{
public abstract void Awake();
}
And the Child:
public class ExampleSub : Example
{
public override void Awake()
{
Debug.Log("You MUST have override, otherwise will throw an error");
}
}
The abstract functions same as virtual MUST be public - they cannot be private. The difference between abstract and virtual:
You can declare virtual function in your usual class, but abstract function requires the class to be abstract as well;
Abstract functions don't have base implementation like virtual therefore when you inherit abstract class you MUST override functions - it will throw an error if you forget to override some function.
Just to clarify - what you described is expected behaviour, because you inherit functions that has base implementations and therefore they being called when your child class inherit them.
Hope that helps.
Answer by ludwu · May 13, 2020 at 01:15 PM
Hey @ADiSiN , thank you very much for your time and your explanations. Just to clarify, is it the same to do :
public class ExampleSub : Example
{
public override void Awake()
{
// Nothing
}
}
and to do (with a non virtual private base class) :
public class ExampleSub : Example
{
private void Awake()
{
// Nothing
}
}
If I want to do nothing in the Awake subclass, it seems the same to me and no needs to implement a virtual function in the base class ?
Hi!
In Awake() case - yes, but only because it's built-in $$anonymous$$onobehaviour function and let's say Unity does this thing behind for you, not the same way, but it understands which one stays above another and prioritizes it and that's ok if you want to do it, but keep in $$anonymous$$d that this will not work with custom functions.
Here is code that will help you understand it's better and here is link for console output (it won't let me attach picture, sorry for link): https://imgur.com/7YjuP7L
Parent code:
public class Parent : $$anonymous$$onoBehaviour
{
private void Awake()
{
Debug.Log("Parent says Awake!");
CustomFunction();
CustomVirtualFunction();
}
private void Start()
{
Debug.Log("Parent says Start!");
}
void CustomFunction()
{
Debug.Log("Parent says Custom Function!");
}
public virtual void CustomVirtualFunction()
{
Debug.Log("Parent says Virtual Custom Function!");
}
}
Child code:
public class Child : Parent
{
void Start()
{
Debug.Log("Child says Start!");
CustomFunction();
CustomVirtualFunction();
}
void CustomFunction()
{
Debug.Log("Child says Custom Function!");
}
public override void CustomVirtualFunction()
{
Debug.Log("Child says Virtual Custom Function!");
}
}
What we can see:
Awake called from Parent since we didn't call it in Child;
Start called from Child since we call it and Unity got it that we want it to be called here, not from inherited Parent;
Both Parent and Child called Custom Function, because we declared them in both functions and Unity don't see connection between them to check for pointer execution (early binding decided at compile time);
Both Parent and Child called Virtual Custom Function, because we called them in both functions, but both of the calls end up using Child implementation that's because virtual functions got us late binding when the implementation of the method gets decided at run time.
To expand a little bit more imagine you have this script:
public class Aunt : $$anonymous$$onoBehaviour
{
public Parent parent;
private void Update()
{
if (Input.GetKeyDown(KeyCode.C))
parent.CustomFunction();
if (Input.GetKeyDown(KeyCode.K))
parent.CustomVirtualFunction();
}
}
You cache in Parent parent reference to the Child that inherit from Parent.
Now, when you press C you will receive: Parent says Custom Function!
However, when you press K you will receive: Child says Virtual Custom Function!
So this become handy when you have multiple objects that should have different implementation of the same function but since the checking for each class name will be too heavy you can call the virtual Function from their inherited function and Unity will point to the overrided execution (if exists, of course).
Hope this was clear enough x)
I have not the same behavior than you.
That's the result I got in my log console : https://imgur.com/8U9PO13
To be clear, I just added the transform name and see how it acts in monobehavior : https://imgur.com/7fntWf4
As we can see, mono first treat the child script that even though it has no Awake function, go through its inherited parent and execute Awake a first time (that's why we have the child virtual custom func logged).
It then execute the Parent Awake
And then the Child Start
And finally the Parent Start
The problem is : if you don't do anything in child on awake or start, it will still execute the awake and start from the parent. It seems awkward because if you have multiple children of the parent with no awake functions, they will all execute the awake or start of the parent. Lets say there are some heavy operations in awake or start, it could be a problem.
Plus, if you have something like "transform.Find("someGameObject").getComponent()", children will result in a null reference because they have not the same hierarchy.
The only option I found is to break this behavior with an empty awake and start in chidren (override or not).
I'm also asking myself why it is the child script that is first executed by monobehavior over the parent ?
You have the same bahavior as $$anonymous$$e, but you put Parent in the scene as well, while I am not, that's why you have additional 4 lines of Debug, and that's expected, because by placing Parent in the scene it will call his own functions.
Yes, but you can look at this from another angle: if I wrote my functions in my parent class, why would I want to call them again to execute in my child class? You can create second parent and inherit from the first one where you empties Awake and then no further Child of Second Parent will need to do it, but Child of First Parent will need.
And I don't get what do you mean by Find function - If you place Child object with Inherited Parent class and call FindObjectOfType< Parent>().GetComponent< Collider>() or any kind of that - it will return you Collider of GameObject that contains Children class on itself.