I have a class called BaseCharacter that inherits from Monobehavior and a Player class that inherits from BaseCharacter. I want both these classes to call the Update method. Unfortunately, only the Update method of BaseCharacter is being called. I am new to C#, so most likely this has something to do with inheritance. I am not sure if this has anything to do with virtual methods? Below is the code, just to make the situation clearer.
public class BaseCharacter : MonoBehaviour
{
// Update is called once per frame
private void Update()
{
Debug.Log("calling Update from Child of Monobehavior");
}
}
public class Player : BaseCharacter
{
// Update is called once per frame
private void Update()
{
Debug.Log("calling Update from Grand Child of Monobehavior");
}
}
In the console I only see the message from Player who is the grand child of Monobehavior and not from BaseCharacter who is the child of Monobehavior. I would like to see both messages.
public class BaseCharacter : MonoBehaviour
{
protected virtual void Update()
{
Debug.Log("calling Update from Child of Monobehavior");
}
}
public class Player : BaseCharacter
{
protected override void Update()
{
Debug.Log("calling Update from Grand Child of Monobehavior");
base.Update();
}
}
As a C# newbie you may be tempted to use OOP this way with inheritance. I can tell you I pretty much donât inherit from MonoBehaviour at all, and specifically not for game logic.
You may want to do some research why inheritance is often the worst choice in designing a gameâs code. Unity is already component based, so lean in heavily into that. Consider that whatever makes two BaseCharacter classes differ in behaviour could be implemented by having a Character component, and then either a Player or an Enemy component on the same object. This provides you with greater flexibility, and also avoids using the same codebase where itâs not needed or gets in the way.
Can you maybe describe with code what you mean. I keep hearing that Unity is component-based but I am not even sure what that means. I come from an Android development background and over there it is all interfaces and abstract classes.
Whatâs wrong with that type of inheritance? In my case, the Player inherits from BaseCharacter which itself inherits from MonoBehavior. Other moving objects also inherit from MonoBehavior because they all share the same basic responsibility of moving in one direction. Is there something wrong with this approach?
I mean the point of inheritance should generally be for substitution, which is what polymorphism enables. Namely, if you have an instance where youâre using a base type, the referenced instance should be able to be swapped with any of its derived types while still being able to use it as-is.
In short, IMO, the âcorrectâ use of inheritance is to employ polymorphism to enable substitution.
I wouldnât use it solely for the sake of reusing code. But I would use inheritance to reuse code in situations where youâre employing polymorphism already.
Though we also have interfaces in C#, which allows you to compose multiple relationships rather than only one, as we can only inherit from one type in C#.
Which is in effect what we do with components. We put multiple on a game object to compose our behaviour and relationships. And worth noting that GetComponent<T> does work with interfaces.
This is all to say that having an inheritance tree can box you in more than it helps.
I am curious how would you tackle my specific situation? For me the BaseCharacter simply moves in one direction and checks the health status (if 0, then the object is destroyed). The player that inherits from this then extends it with additional functionality like being able to side step and jump. The Enemy classes that inherit from BaseCharacter add different functionality as well. Some can shoot while others can explode.
Since I cannot replace my BaseCharacter with the Player or Enemy classes (who are originally its children), that violates one of the SOLID principles. But then does that mean I donât have any inheritance at all and simply define all common behavior in a separate script and get a reference to that and call it whereever I need it? I am already doing that with the different enemy behaviors and defining them using ScriptableObjects that are then provided to each Enemy Prefab. Maybe I should do the same with everything else?
Well the question to ask is: do you have a situation where something else cares about BaseCharacter and its properties (including those expressed by derived types)? Or is BaseCharacter a type that youâre only sub-typing to extend its existing functionality?
If the latter, then there might be no need for inheritance as it isnât contributing much other than boxing you into an inheritance tree. Or if they have shared properties - particularly simple ones - interfaces might be better.
And simple functions such as âmove in one directionâ can easily be distilled down into static utility functions that you reuse where needed.
Health, for example, is something that can easily be its own component. Itâs a separate responsibility, and can be better encapsulated into its own component. And because you potentially will have situations where you GetComponent<T> for this type, this is a situation where inheritance might make sense, if different entities may have health components that behave differently.
Worth noting that GetComponent<IMyInterface>() works fine. It used to not work in case you ever run into this but that was I donât know how many years ago (2017 or 2018 perhaps).
When I create something framework-like I follow the best practice that Unity has more recently heavily leaned into, specifically with their cloud service packages. Meaning every class has a public interface and only (or primarily) interface references are being used or exposed.
In case of MonoBehaviours this has the added benefit that to the outside world they canât just use the MonoBehaviour public methods and, say, Destroy(component) if component is merely an interface.
It came in several phases⌠GetComponent<I>() was the first one to start working (even as far back as somewhere in Unity5), but GetComponents<I>() and in *Children and in *Parent didnât⌠then more of it worked⌠Iâm honestly not sure where things stand right now⌠every time I reach for getting an interface, I just make a quick test and prove what the API will do for me.
And usually with anything more complicated than a simple GetComponent I wrap it up in something that adds semantic value to my program, such as GetAllBuffableWeapons(); for instance.
I assume thatâs what CodeSmile meant too, and I agree. The problem with subclassing below MonoBehaviour so is that no Unity developers EXPECT it, so you always have that surprise friction surface for making lots and lots of irritatingly difficult to track down bugs.
And these bugs can be vastly separated in time and location, triggered by some poor sod just trying to shim in a feature five years down the line, expecting that his OnEnable() will get called, and well, some other sod failed to call base.OnEnable(); but only in one weird variant of a subclass that hardly ever gets usedâŚ
Yes, thatâs what I meant. I do subclass MonoBehaviour but I donât build an inheritance tree on top of it.
And of course, if the framework only provides me with an already-subclassed MonoBehaviour, I subclass that. NetworkBehaviour for instance.
Yup, even with NetworkBehaviour the occasional âIt wonât workâ comes up where it turns out that person subclassed MonoBehaviour instead. I believe thereâs also the necessity to call base.OnNetworkDespawn() which is potentially problematic since despawn is rarely used while the commonly used OnNetworkSpawn does not require you to call base. On purpose I nevertheless added base.OnNetworkSpawn() as a reminder.
Oh and then ⌠when do we call base? First thing? Last thing? Iâve even seen code that only worked when base call was somewhere in between because it required the initial setup but then did additional overrides.
Thatâs why I prefer not to make Unityâs event methods virtual. Instead, I have them call protected abstract methods, similar to hooks in the template method pattern. Thereâs a hook before the code that needs execution and another one after.
The derived class must implement these hooks, eliminating any temporal coupling. While this approach might occasionally violate the Liskov Substitution Principle because the implementations might be empty, I believe this isnât problematic for non-public methods.
It is worth it in my opinion because it moves any protentional error to compile time. Anyone deriving from the base class will encounter compile-time errors if they donât implement the abstract methods, ensuring the code wonât compile until they are while also avoiding any questions of when base code should be called.
Right, thatâs one of the most useful features of components :). Though to be more specific the âit used to not workâ only applies to the generic version of GetComponent because in the past they put a generic constraint on âTâ so it has to be derived from Component. At first glance that seems to make sense but they forgot about interfaces at that time. However the âactualâ / underlying GetComponent(System.Type type) always did work with interface types as well. I think the earliest versions I actively tried was Unity3 (released around 2010).
Yes, but that would not stop a âbad actorâ since you can always cast the reference to MonoBehaviour (if it actually is a MonoBehaviour) and then destroy it or start a coroutine on it. So itâs just like âprivateâ which protects yourself from stupid mistakes but does not really represent a layer of security.
I often end up doing the same with IDisposable too. But there are other ways to handle this depending on context. This is generally just the quickest if I donât need a robust solution.
I know for a fact that a junior programmer once spent an entire day hunting down an issue that was due to something like that. It never occured to him to check whether âOnEnabledâ never ran, and granted, that wasnât really obvious. Canât recall if that was the exact method but it was one of those event methods with a non-obvious typo.
Yeah that wasnât the best example. Personally I call the methods AwakeInternal, OnEnableInternal though I know thatâs not entirely the most âcorrectâ naming convention, but it works for me as a solo dev.