- Home /
Why is Start() from my base class ignored in my sub-class?
I fully understand that I'm probably making an error, but I'm not sure how to rectify it.
I'm only not posting code because it's easily described; make two scripts, make one be a standard script and the second one a sub-class of the first script. If you then attach the sub-class to a game object, Start() from the base class doesn't fire. The Start() only fires if you attach only the base class to the object, verifying that the Start() function itself does work.
How can I fix this?
Answer by ClandestineMan · Jul 22, 2011 at 08:33 PM
Start in your base class is not public so when you extend the class start is hidden. You will have to implement a new Start method and call base.Start(). That is what we do on our project.
I am also assuming that your coding in C#.
EXAMPLE:
public class TourGuide : _Model { new void Start() { base.Start(); transform.Rotate(Vector3.down,95.5f); } }
public class _Model : MonoBehaviour { void Start() { // original code. } }
Thanks. Understood. I assumed that splitting my classes into separate script files within Unity would somehow eli$$anonymous$$ate the need to do this, but I guess not.
Sorry, I un-checked your answer for the moment. I just wanted to make sure that I check the most useful answer for whoever stumbles into this thread next, and want to see if jharger's alternative is more useful for me. :)
Why does everyone $$anonymous$$ch shadowing / hiding / reimplementing a method. That's not the usual way when you create a subclass. If you want a method to be able to get overridden in a subclass you should make it "virtual". In the subclass you have to use the override keyword and that's how the inheritance work the way that is intended. The method doesn't need to be public but it can't be private. That's why we have protected
. Protected members are visible only for the class it self and derived classes but not from outside.
The method you're overriding gets replaced by the new one so if you want to have the overridden method called as well you have to use base.methodname() in your new method.
This has been discussed several times here and it's fundamental oop stuff.
This answer doesn't work anymore, at least in Unity 5.3.5. You'll get a warning and an error if you do this. See the komodor answer below (make it public and virtual). PS. Hiding methods (using new) in C# is also a bad idea until you desperately need it in a particular situation.
This does work, however not as it's stated here. In this example the methods are "private". So you can't use base.Start()
since Start in the base class is private. It has to be at least "protected".
Start, Update, Awake, ... shouldn't be public as they shouldn't be called manually from outside. Best practise for those if you want to use inheritance is to make them protected virtual.
Answer by komodor · May 15, 2014 at 03:10 PM
declare the Start method public virtual in base class and public override in all classes derived from it, methods without access specified are considered private
For those looking for code:
In the base class:
public virtual void Start()
{
// base class code runs
}
In the class that inherits from it:
public override void Start()
{
base.Start(); // runs the code from the base
// add your additional code here
}
As Bunny has pointed out in comments on other answers, these functions should be protected, not public.
Answer by jharger · Jul 22, 2011 at 08:36 PM
I believe that, basically, Unity only calls the lowest implementation of any message function (which Start() is), so the sub-class will always have its version called and not the parent class. Compare this to inheritance in a lot of OOP languages in general, and this is also the case (the overriding version is the one called). Note, that this is not overriding the Start() method, rather it is shadowing the name.
I'm not sure how to do this in Javascript, but with C# you can declare the base class's Start() like this:
protected new void Start() { /* ... */ }
... and then call it from the sub-class by using base.Start()
. I personally don't like this solution, because it's not really clear what's going on there, so you can do something more like this:
// Base class
void Start() {
DoInitialization();
}
void DoInitialization() {
// Base class's code goes here
}
// Subclass
void Start() {
base.DoInitialization();
// Other init code
}
One question you should ask yourself, though, is sub-classing what you really want to do? If you're just looking to force some shared functionality, why not just use add the base class as a component to the game object you're working with? You can even force it with a RequireComponent(). That way you can still get the functionality of what was the base class without the funkyness of using inheritence in MonoBehaviours.
What I have right now is a simplistic game to test a concept, and right now all enemies have a base class called "Enemy". This includes various shared attributes such as health, points, death instantiation, things all enemies have in common. Then for each individual enemy, I have classes that run their specific AI. Sub-classing does seem to be the best option to do this (to me). Because in each sub-class, I do often need to refer to the base class for certain things.
Just to clarify, do you mean I can add the base class and sub-class scripts separately as components rather than just adding the sub-class, and retain the functionality of both with no ill side effects? For instance, both their Start(), Update(), etc. would be independent and would run naturally? Or would this cause me to not be able to easily reference the base class from the sub-class anymore?
Yeah, basically. What I do is have a sort of character logic class that takes care of the common functionality that's likely to be in both the player and AI objects. So I might have a "Warrior" class, for example. Then I create a separate "EnemyAI" class when would then use RequireComponent() and GetComponent() to get a handle to the class, which I can then query to get hit points, select a weapon, etc.
As far as ill side effects... there may be a tiny bit of overhead to adding yet another component, but not much... I mean most objects are going to have several components anyway... what's one more?
The problem is when you hide a method ins$$anonymous$$d of overriding it you can get in trouble.
When you have a derived class instance but you save the reference in a variable of the base type it won't call the new/overridden method. If it's overridden properly that's no problem.
public class Animal
{
public string GetName()
{
return "Animal";
}
public virtual int GetLegCount()
{
return 0;
}
}
public class Cow : Animal
{
public new string GetName()
{
return "Cow";
}
public override int GetLegCount()
{
return 4;
}
}
public class Chicken : Animal
{
public new string GetName()
{
return "Chicken";
}
public override int GetLegCount()
{
return 2;
}
}
Execute the following code with the classes above :
List<Animal> myAnimals = new List<Animal>();
myAnimals.Add(new Cow());
myAnimals.Add(new Chicken());
foreach(Animal animal in myAnimals)
{
Debug.Log("Name:" + animal.GetName() + " Legs:" + animal.GetLegCount());
}
The result will be:
"Name:Animal Legs:4"
"Name:Animal Legs:2"
Answer by softrare · Oct 26, 2013 at 09:36 PM
I don't know if something in Unity changed in this regard since this was asked / discussed but I just did exactly what the OP wanted and it worked.
If you declare a base class abstract (inherited from MonoBehaviour) and put a child class on a gameobject (in the scene) and implement Awake() or Start() in the base, the child class doesn't need to do anything (except for inheriting of course). When the scene starts these callbacks get called on the base class automatically. I am using Unity v3.5.7.
yea, this same thing happened to me. I just did not declare anything in the child class, it automaically called the base class's monobehaviour methods. I had to do none of the workarounds mentioned here.
If the child class doesn't implement Start it will call the one in the base class. If you do implement Start in the child, this is where the issue occurs as it only calls the sub-classed Start method.