When appropriate to use inheritance versus components

Hey guys, this is a general game programming question, but I’ll try to illustrate with a specific example just so it’s easier to understand.

Suppose you have a survival game. There are items that player may interact with. For example, we have these two classes:
Foodstuffs (apple, banana, grapes, and so on)
Vehicles (car, truck, and so on)

The common link that Foodstuffs and Vehicles share is that they can both be interacted with.

So, as I understand there are two main approaches: I add an “interactable” component to both the class Food and the class Vehicle.

Alternatively, I derive Foodstuff and Vehicles from a parent class: Interactable Object class. And in that parent class lives an OnInteracted function. Perhaps some other shared functions may exist, but for example I think a single commonality is enough.

As far as I can tell, the difference between inheritance and component structure here is in ergonomics? With components I need to remember to add the component to anybody who needs it, which does have some room for annoying human error.

With inheritance I guess the worry is that I could possibly spin a tangled web, but I think so long as the chain is only 1 or 2 layers deep, what is the problem? Should some child class need to override a function that allows for all the flexibility I need, right? And if over the course of development, I realize that a banana is actually in a class of its own, then I can just change the parent class, no problem. Right?

So, my major question is - is it just a matter of ergonomics? Are there performance or other technical considerations I am unaware of?

In the past I’ve used something like a state pattern to handle input. I have a Base State class that has some common functions like OnConstructed and HandleSpacebarInput. Depending on the Child State class that is currently active, then I can easily map the same input functions many different ways. This also chunks my code in a very intuitive and easy to debug way.

I see the same approach being viable to handling interactable objects. If I collide with a game object, we query its class. If it is interactable object class, we can call its OnInteracted function, and the child instance is going to add additional stuff on top of the parent’s function or override it entirely if necessary.

Another approach might be that we just send an interface message to the collision object, and should it implement the interface it can do whatever it wants. But if my Apples, Bananas, Pears and Kiwis are only differentiated by their data values (i.e banana is 10 calories, apple is 12, and each has its own sprite image), then it seems like making each of these things a separate game object that has to get a litany of components and each subscribe to interfaces is a lot more work?

I think the main objection to an inheritance approach is that any given object could have various behaviors, not all of which can easily be associated with a base class.

To extend your example, imagine you want to add a behavior that determines if an object will explode if something shoots it. And let’s say you also add a basic exploding barrel to your game. But you decide that vehicles should also explode when you shoot them. You’ve already decided that Vehicle inherits from Interactable Object, but now it should also inherit from Exploding Object. But food shouldn’t explode, so… What should a vehicle’s base class be now? Interactable? or Exploding?

Another nice thing about the component approach is you can easily decide later on to add some behavior to something. Maybe early in development you didn’t think vehicles would explode, but later you decide that’s a good idea. Slap an Explosive component on it, and you’re good to go. No re-engineering all your inheritance.

That doesn’t mean not to use any inheritance. I use that too. But probably only when it satisfies the “is a” constraint.

3 Likes

Also, follow up question - very general here, but in planning out some systems like this, I think the best way to start is have a two-column list, where, Column 1 has class similarities, Column 2 has class uniquities?

Do you do something like that, or totally different?

A quick example:

So from a list like that, you could start figuring out either what classes you need or what components you will need, based on shared commonalities?

Ahhh, that totally makes sense.

So in this case, I think both could be used. We might have the ergonomic boost of deriving most things from interactable object class, but should they also need to explode, that sort of behavior comes from a component - thus if bananas also need to explode, we can do so.

But for something so common as calling an “on interacted” function, that still makes sense to you to let that come from a parent class?

edit:

The “is a” constraint - is this talking about the Liskov’s Substitution Principle?

Idea being that if we changed InteractableObject class with Banana, it should still work? Because the banana should only be adding upon the parent class?

I guess I don’t understand that, because overriding parent class functions seems to be a useful thing to do that hasn’t presented me any problems so far. But I think I’m not understanding the basic idea.

Two criteria you can use to follow dgoyette’s advice are:

1. Might using inheritance cause code duplication down the road? Taking the InteractableObject → Vehicle, ExplodingObject → Barrel example, if you wanted to explode vehicles, you might have to duplicate ExplodingObject’s code in Vehicle. So in this case, “exploding” should be an attribute (component).

2. Will inheritance add unused code to parent class? Say you add explosion code to InteractableObject so Vehicles can explode. Then the interactable Banana inherits this code that doesn’t apply to it.

2 Likes

@TonyLi @dgoyette

ah okay, that makes sense. Sorry I’m kind of scatter brained about all of this - just trying to get a vague picture of how some things might work so I can build a few test. This helps a lot.

That’s probably the formal version of what I mean, but I don’t think so deeply about it. In practice, I nearly always use inheritance with abstract base classes, and concrete sub-classes. A simple example in my project is an “Energy Beam” controller. I have various types of lasers which do different things when they hit something. But all Energy Beams share some common functionality, like turning on/off, aiming, raycasting for hits, etc. All that functionality goes into a base class. Then I have sub-classes for all the specific kinds of beams that do different things compared to each other (burn things, make things grow/shrink, etc). So, a Burning Energy Beam “is a” Energy Beam. If I had a method that accepted an Energy Beam, I could pass it any of the subclasses and the method would work fine.

So, I tend to use inheritance for behavior that really determines the core identity/nature of an object.

But for things that could blow up if you hit them? To me that feels like nearly anything could do that, so I go with components. For something like that, I have a component I call a “Force Tracker”, which handles all of the ways that forces could potentially “harm” the object. And if the force exceeds a threshold, a UnityEvent is called. The only thing I do for each object is tell it what method to call when that threshold is exceeded. Pretty easy.

You’ll probably find, though, that this is a topic that developers love to give opinions on, but it’s almost always anecdotal advice based on their own personal preferences. So, probably the best thing is not to rigidly constrain yourself to one approach or another. If you’ve painted yourself into a corner with either inheritance or components, don’t be afraid to pivot.

2 Likes

This is the best advice. Refactor as necessary. (But find a balance between refactoring and moving forward with new tasks.) Try to minimize dependencies between different functional parts of code so you can refactor one part without impacting other parts. We could mention C# interfaces, but that’s just another detail.

2 Likes

Yeah that is my main worry, besides just ergonomics. So far the major pain point in my learn-to-code adventure was in making code with too much tight coupling. In a “simple” project I think I’ve mostly reduced that but I am exploring a few things to do next, and now that i understand the problem a little better I should be able to avoid it (hopefully :slight_smile: ).

My experience with interfaces so far is unreal blueprint implementation - I believe its the same idea though. Basically, an interface allows you to send a function or event call to a soft-reference of an object, and if that object implements the interface, then they will do whatever they want with the event/function.
This reduces tight-coupling is the big idea, though my use of them has been pretty basic so far. With minimal moving pieces in my project it’s not a big deal to use a couple hard references here and there for convenience’s sake.

Yeah I see a lot of debate about this approach versus that. In the end I always do just what I can understand and manage but good to get some sense of how others approach things.

I think it’s worth mentioning that the component structure is kinda layered over inheritance. Remember, Unity is component based, but all unity Components are part of a greater inheritance tree, with the root class in most cases being UnityEngine.Object.

To that end I’ve nearly always preferred a component structure over a purely inheritance one. Often, also, alongside some composition with Interfaces. When I do use inheritance, it’s always very shallow; never more than the one base class and its immediate children.

So with your initial example with vehicles and food being both ‘interactable’, personally I’d make that an interface (IInteractable) as it just boils down to implementing a method or two. Then, for interactable stuff that all express similar behaviour, they could have a base class that implements this interface, with some immediate children. GetComponent works on Interfaces after all.

These days I’m using SerializeReference a lot too to serialize polymorphic data without the need for scriptable objects.

2 Likes

The following is probably far more than you care about the topic. :wink:

Inheritance is powerful and elegant… until it suddenly isn’t.

As you’ve been learning programming mostly related to Unity I would guess that you’ve only been using C#, and when you learned about inheritance there was a fundamental rule that a class can only extend one other class. The thing is that it’s totally not a fundamental rule in computer science at all. “Multiple Inheritance” is a thing, it makes perfect sense, and other languages have it. Single Inheritance is a deliberate choice in C# (and some other languages) because it turns out that multiple inheritance… is tricky.

Imagine that I’ve got a class, FuelTruck, which is both a vehicle and an explosive. In an ideal world it seems like a good idea to have it derive from both Vehicle and Explosive. In C++, as one example, that’s a thing which you can absolutely do. And while things stay simple everyone is happy. The catch is that this can very easily introduce ambiguity to your code, and I’m sure you’re aware by now of how much computers loathe ambiguity. Add that to the fact that things almost never stay simple and you can easily find yourself in a Deadly Diamond of Death situation, as one example. (Yes, it was really called that, by “Uncle Bob”.)

So given that the seemingly ideal solution turns out not to be ideal at all, other approaches are taken.

Breaking objects into components and using “composition” is one of those. I can have GameObjects with both Vehicle and Explosive components. There is no ambiguity because both Vehicle and Explosive can have their own implementations of ApplyDamage(), and anything which calls them must call either or both explicitly. Everything is groovy, no worries. It is slightly more verbose for programmers, and extra objects do mean extra memory / instruction overheads, but they’re usually negligible.*

Another approach is the use of “Interfaces”. These behave and can be used almost identically to classes, with one exception: they can declare methods (give them names) but they can not define them (have code in them). In this case I can have an object which, for instance, “derives” from Vehicle and “implements” IExplosive. It inherits everything from Vehicle as per usual, but all it gets from IExplosive is a bunch of methods signatures which it must implement. So if I have five disparate classes which implement IExplosive I need to manually provide an equivalent implementation in each of them, i.e. repeat some code.

You may notice that each of these is essentially just committing to a subset of the pros and cons that Multiple Inheritance would bring. I suspect that’s exactly why language designers often restrict us to Single Inheritance - it forces us to deliberately choose for ourselves, rather than get tripped up by it later on**. Single inheritance alone is enough to get most jobs done, and we’ve got a bunch of approaches to choose between for the remaining cases - components, interfaces, mix-ins, etc.

Coming back to your question. Whether or not it’s “all just ergonomics” is probably a semantic debate. At the small scale with simple stuff I’d say that yes, it essentially is. As projects grow it eventually becomes an architectural level design decision, partly rooted in all that computer science stuff in the paragraphs above.

  • There’s actually another massive benefit to this approach, which is that composition of new object types can be lifted out of code and into data. For example, I don’t have to write any code to make my explosive vehicle also be solid in Unity. I just go Component → Add → Collider. No code or recompilation needed. I can replace the Wheels with Thrusters in data and turn it into an explosive hover truck, etc.

** Whether or not that is effective and productive is a separate debate that shall never end. :wink:

3 Likes

Thanks a lot for sharing - I am working mostly in a vacuum so sometimes I wonder if I am doing things in a totally nonsense way. Really helpful to hear how others are doing things. What you’ve described here is similar to what I’ve done but a bit better it sounds like - using the interface. In my simple case I dont think it matters but if things grew more I think I’d want to do something more like you’ve described.

That’s greek to me. I’ll have to google that at some point.

@angrypenguin

magisterial post!

In refactoring my project and starting to think a bit about the next, I’ve taking some time to read more about programming. It is really hard to find plain language, practical explanations though. You’ve helped clear up quite a bit here.

Currently most of my hands-on programming has been with unreal blueprints. But it’s helped me grasp the basics so that I’ve been able to understand enough general programming that I was able to make a (not finished) chess game with Java (why java? I just thought making chess would be good general programming exercise and the best example that I found was in java, so I just did that). And a lot of the people I get programming advice/examples from are using unity c# so to me it’s all just “programming” and I remain ignorant so far of major differences between languages.

I can see how idea of multiple inheritance may be useful for like, refactoring. Maybe you already have the explosion and vehicle classes fully fleshed out, and you really that some new class could just plug into both? But I can imagine how that might get messy fast if you are still in a development phase creating new things and making many changes rapidly.

I also got a better idea about the benefit of modularity using the compositional method - that makes sense. I think in my work so far, it has taken all my energy to learn so much kind of quickly so any tiny thing that makes my code more verbose makes me feel like it’s overly complicated. But now that things are becoming clearer, I can see how implementing that sort of pattern from the start might pay off in the long run - especially if I work on a project that is more complex code-wise.

I think the major thing for a solo developer like me is that I need some general patterns to follow so that it’s not necessary to remember what every little script is doing - so long as I know the general pattern, then making changes throughout development should never be a brain twister. So far I think successfully put to use a couple patterns (like state pattern for input) and that made life so much easier rather than just having a litany of conditionals.

Anyway, thanks everybody this has been both a great sanity check and also, I learned/understand a few important things better now. In coming weeks on my time off I’ll try to test out some more of these ideas as I poke at exploring next project possibilities.

So, as an example of composition, suppose we have many vehicle types in a game. Say car, truck, bicycle, boat. Let’s suppose it’s arcade representation - no physics simulation at all.

Some things they all have in common is input to control them. WASD or joystick makes them go forward, back, left, right.

Each vehicle must have its own visual representation, its own movement related variables (top speed, acceleration, deceleration, turn speed, etc.)

In this case, you might derive each vehicle from a base class - but that base class can be as high level as your character class, basically, right? It’s just something in the game that the player can control through input.

Our car, truck, and certain types of boats might be able to explode. So for that we might use a ExplodableObject component that can be attached to them.

That is my initial thought how a system like that might work. But as exercise, what if we did it with pure composition?

How might that look?

We have our base character class where we handle input. Would you just attach a vehicle component to it at runtime when necessary? And then the vehicle component might ask the question, “based on what sort of vehicle I am, do I also require the ExplodableObject component?”

That seems a little confusing to me because we need to override or replace what is happening when the same input that normally moves character is handled. I guess this is not a good case to use composition approach, or am I imagining it the wrong way?

Maybe you don’t even have a base character class? You just have a “things in the game with a transform” class, and you build your character up from various components?

Foot travel Movement component
Vehicle travel movement component
etc etc ?

No reason that shouldn’t work. But is anything gained? Anything lost?
We know that the foot travel movement component is only going to be used by the player. Is there any benefit to moving some code into a component if it can only ever be used in one case?

Here is a better visualization of this example:

So, we would like to have all of the data for each movement type in a .csv file. At some point, somewhere, we can swap which .csv file we are reading to get the data needed for whatever the current movement type is.

Most of these movement types are all going to be the same - i.e. they share the same input related functions (press W to go forward). The only thing changing is the data variables (car goes faster, walking goes slower.)

What is different is what animations play, what sfx plays, etc.

And in some cases, we might have unique conditionals. E.G, kayak grants you stealth bonus or something.

Now, we don’t want a gigantic class for all vehicles, with a switch statement that is saying, “if I’m ____ type of vehicle, do all these things.” Right? I mean that can work, but it’s not so easy to search and debug I think.

It could be a GameObject with components such as:

  • Rigidbody
  • UserInputAdapter
  • Motor
  • SteeringController
    In turn it would have some child GameObjects which have “Wheel” or “Thruster” or whatever components which the Motor and SteeringController components use to do stuff.

And when I say “could be”, I mean that’s exactly how the different vehicles in my game are all made.

Close. You don’t need a class for that. Transform already exists.

I have four different vehicle types, each fundamentally different, and until fairly late in development there was no concept of a “Vehicle” in the game at all. Anything which cared about a vehicle just got a reference to either its GameObject or its Rigidbody, depending on the context.

I did eventually add a “Vehicle” class, but it basically does some administrative stuff, such as storing the localisation ID for the vehicle’s name, and data for a couple of the high-level rules of the game which are specific to vehicles.

This sounds as if your character class is actually doing a little too much. A general rule of thumb is that component classes should be named after their function or role. I don’t know what function a “Character” performs, because there are actually many things a “character” does and I’m not sure which one you’re talking about. Can your vehicle engage in dialog? Does it have an inventory? If it’s a thing which controls movement or input, I’d name it that.

Ideally, in an environment such as Unity, I should be able to know what a GameObject does just by scrolling down the Inspector and looking at the component names.

I would avoid having components ask such questions at runtime, precisely because it could turn things into a confusing mess. If this is based on the type of vehicle it is then that is known at design time and should be applied then and there.

In short: anything which can be handled ahead of time should be handled ahead of time.

Adding and removing components at runtime is a nice and powerful approach, though. I’d just keep it for when behaviours actually change, rather than using it to defer setup of stuff you know in advance. If I know my Vehicle musthave a Rogodbody then that should be added at design time. However, if I have a spell which makes a vehicle highly magnetic, then implementing that by adding a Magnetic component at runtime makes a lot of sense - it is indeed adding new behaviour.

To assist at design time, in Unity you can add the [RequireComponent(…)] attribute to a MonoBehaviour class to tell it that one component requires another to be present, and it’ll then automatically enforce that in the Editor for you. So if my Vehicle class requires a Rigidbody component, I can add [RequireComponent(typeof(Rigidbody)] just before the class definition, and then when I add a Vehicle component to a GameObject in the Editor it’ll also add a Rigidbody for me, and it won’t let me remove it while the Vehicle is present.

Gotta go, but quick question: why a CSV rather than just using the Inspector?

1 Like

Thanks a lot for response. A lot to read over carefully there.

As to why use a csv - mainly just so that I can work in excel and use functions and stuff to balance things out. Just better ergonomics basically.

If I understand correctly, basically your setup is like this:

You have a class for each type of vehicle. And then, each class is assigned whatever components that it needs.

So you might have Mars Rover and it has Engine component, Wheels Component, etc.

And then you might have Hovercraft and it has thrusters, no wheels, etc.

Suppose the only difference between two vehicles in a game is the static mesh and data variables (i.e movement speed, acceleration, etc.)

In that case, do you still think its worthwhile to separate them into distinct gameobjects? Like, would you even need a Wheels component if the only difference between boat and car is how fast they move and a few conditionals like, boat can go on water but not land?

Would you see a problem if all vehicles were only one class? Suppose I wanted some general rule for all vehicles. Like, “can travel on surface type _____.” All vehicles must know which surfaces they are allowed to travel on might be a global rule for some games. In that case, with composition, you’d end up repeating the same code in multiple components? Or would that condition just be part of certain components? Like Wheels component knows that it can travel on road but not water. And boat component knows only water, not roads?

How are you handling input?

Like, is there some class that reads input, and then delegates it out to whoever or whatever is currently meant to be controlled?

In my current project, I have a “controller” that reads all input (this is suggested use of the class in unreal), and then the controller passes input along to a state class. Each state class then delegates that input to functions/events that typically live on the character class. But I only have one type of character (character in my project just means humanoid avatar we will control. But in something like examples we’ve discussed here, you are right character has little context.)

If I were building a project from ground up in unity that is more like the examples we have been discussing (you can walk as a human, ride a bike, drive a car, fly a plane, etc etc), then the compositional approach is something like this:

input gets handled… somewhere. I guess isn’t super important, just needs to be something that persistent across levels.

gameobjects that might handle input would get an input handler component. We would need a way to identify input priority in that case? Like, in game suppose you are on foot, then you enter a vehicle. Both the human character gameobject and the car gameobject have input handler components, right? So, we have to track which is currently active?

And then, consider boat versus car. They might have 99% same components. If there is special behavior that only will ever pertain to boats, that script can just live on the boat class? Or just for consistency’s sake, you make it into a component as well?

So, a “vehicle” then, before you have a vehicle class, is only known in game as a generic gameobject? And then this specific instance of gameobject has certain assigned components? And that specific configuration is the concept of a prefab?

Supposing you want to identify a specific gameobject - how do you handle that? Or typically you dont need to? You only send interface calls, and should some component in that gameobject subscribe to the interface, then it will act accordingly?

e.g. if I shoot a rifle and it strikes vehicle, and imagine i want vehicle to explode when shot five times, with this compositional approach you dont need to have any concept of vehicle, you just send interface message “hasBeenShot” and then some component on the vehicle DamageComponent might tally up the damage and report that out, and then the Explode Component might be listening and fire off its own events based on data the damage component is putting out?

And the difference between vehicleA which explodes in 3 shots and vehicleB which explodes in 5 shots is just housed in the prefab? Like, the component is instance editable on a per prefab basis?

No, there’s no class for specific vehicle types. It’s as you described later:

They’re all just GameObjects with different components and/or children.

Of course that might not be practical in all cases. For instance, if you need to identify things by their vehicle type then knowing the type of a vehicle somewhere is quite handy.

I’d put it in a component. Partly for consistency, and partly for reusability.

Boats float where most Vehicles don’t, so I agree that it could make sense to implement that in the Boat class if I have one. But Boats aren’t the only thing that float, and if I’m introducing “floating” as a mechanic into my game (or as a consideration into whatever software I’m making) then there’s a good chance it’ll be used again. So implementing it as a Bouyancy component which I can slap on any GameObject sounds good to me. And when someone wants to make a floating crate later on there’s a good chance that I don’t have to do anything.

Most of the time this arises naturally out of whatever system the interaction is coming from. For instance, if a vehicle enters a trigger zone then an OnTriggerEnter(…) call is made to both which includes a reference to each.

Or if I know I want the player to collect 10 specific items, I almost certainly have a list of those items somewhere. So when the player collects something I check to see if it’s in that list.

There are also cases where you need to know things such as “Is the vehicle which entered the zone the player’s vehicle?” There are a few ways you could address that. One is to have a “Game Manager” which knows what GameObject the player is represented by, and you could ask it. Another is to have a component which tracks that, i.e. it is added when the player takes control and removed when they lose control, and you check if it’s there. Another is to have a field in a component that says if it’s player controlled, AI controlled, etc. The best way forward will depend on the game.

Yeah, and it’s beautiful. :slight_smile:

Where you say “interface message” it’s either a function call or an event being raised. I don’t use SendMessage or similar.

Yep. This is the basis of how Unity works.

1 Like

Awesome. I think I understand enough now that I can build some test to try out some of these ideas. I’ll try out changing player control between a human and a vehicle, and both will be generic class, and with all of their behavior coming from components. That should confirm the basic idea. I guess then I might add some very different vehicles, like the boat, and also see at what point it becomes easier to define a new class or not.

@angrypenguin

I hope you don’t mind a follow up question - and of course anybody’s input is appreciated.

But about a vehicle example using a composition approach, the question arises, how low level do I get in defining components?

An engine component might be used to define an output of horsepower. This could be used by a car, motorboat, or rocket ship. Just outputs a float to define horsepower, and for further simulation you could have engine weight, type of fuel needed, etc. But let’s only consider horsepower output for example.

By itself, the only thing engine component does is turn on and off and make noise. It outputs the horsepower variable but if no component is listening for that then no bueno.

Now then if we have a wheel component, that might listen for horsepower? And then maybe horsepower can get filtered through a curve to determine torque on the wheels. Just example to demonstrate composition, never mind actual physics sim.

So, the chain of communication might go like this:

W Input is pressed. Whoever is handling input sends message to player controlled gameobject (in this case our wheeled vehicle).

First, engine ask is it on? If not, turns itself on. It begins updating its horsepower variable.

Wheels component can also start reading the horsepower variable every frame. Then they can do whatever they want with it.

Alternatively, maybe you are not doing any physics sim - just faking things like acceleration, braking, mass affecting turn speed, and so on. In a case like that, maybe wheels are just a data only class, not an entire component. They would only provide some info like, “can traverse offroad? max friction? likelihood to go flat? etc”

If that is the case, then since there is no special behavior tied to wheels specifically, then the engine components horsepower variable would just be considered by a more general “wheeled vehicle” component.

The boat, supposing it is still a generic gameobject and not it’s own class, would have engine component, but it might have its own “boat vehicle” component which might intepret the engine horsepower it’s own way. Or maybe the only difference is in adjusting the data parameters (top speed, turning speed, etc), and restricting surface it can travel on. in that case, do we need a difference between wheeled vehicle component and boat vehicle component?

I suppose not, it could just be vehicle component. No sense duplicating a thing only to change the name. But in interest of futureproofing, might you go ahead and make the distinction for any reason?

Is this example sensible?

And in the case that you need to save persistent data associated with any prefab instance, then in that case you must have some class which holds onto the data?