Unity Software Design – Inheritance and Composition


Third in a series of posts on Software Design for Unity. Read the introduction here.

Inheritance is a big deal in object oriented programming. The ability to extend parent classes into subclasses, and have those subclasses be recognised as the parent (via polymorphism), is extremely powerful. It’s one of those Big Ideas that, once it clicks in your head, drastically changes how you think about coding.

And if you’re not careful, it ends up making your life far more difficult than it should be. Because it’s also one of those hammers that makes everything look like a nail. Inheritance allows – and encourages – one to structure code in hierarchies. But the fact is, not everything is a nail hierarchy.

If you buy into the idea that everything should be solved using inheritance, you’re going to spend a lot of time and effort trying to make non-hierarchical structures look like hierarchical structures. Not only that, but the design of the codebase will usually end up worse than if you hadn’t – harder to understand, maintain, extend etc etc.

Fortunately, one of the absolute best things[1]in my opinion, as always about Unity is its widespread use of composition. While inheritance and composition aren’t mutually exclusive (in fact, they’re extremely powerful when used together correctly), they essentially represent two opposing ways of thinking about any specific problem. Composition is pretty awesome, and I’ll talk about it later. But first, let’s look at the core pitfall with inheritance. So, back to the idea that:

Not Everything is a Hierarchy

Imagine you’re creating a simple bullet.

public class Bullet : MonoBehaviour {
	public void Fire(){
		// Start moving
	}
	public void Update(){
		// Move along path
	}
	private void OnCollisionEnter(){
		// Check if we’ve hit an enemy
		// If so, damage it
		// Either way, destroy self
	}
}

Some time later, you want to make a different type of bullet – a bullet that explodes on impact. Simple – the only difference here is that it explodes, so you inherit from Bullet to get “for free” the basic bullet behaviour, and only have to write one new method.

public class ExplodingBullet : Bullet {
	private void OnDestroy(){
		// BOOM
	}
}

Lovely. Now, a little later, you want to make a homing bullet. Ok, so do the same again, but instead modify the Update method.

public class HomingBullet : Bullet {
	// Assume we’ve changed Update() in Bullet to be protected virtual
	protected override void Update(){
		// Rotate towards target
		// Move
	}
}

So far so good. You’ve reused code, and any other code that deals with Bullet will recognise both of these subclasses as a Bullet. This is the Power Of Inheritance at work. The obvious next step? Making an exploding homing bullet!

… and then it all goes a bit pear-shaped.

Your first attempt is to make HomingBullet inherit from ExplodingBullet instead of Bullet. But now all HomingBullets are explosive, which you don’t want. So switch it around – ExplodingBullet inherits from HomingBullet. Same problem – now all ExplodingBullets are homing.

You can, of course, put a boolean in to specify whether these attributes should actually be used in a particular instance. But in that case, why have separate ExplodingBullet and HomingBullet subclasses at all? You put the homing and exploding behaviours back into Bullet and have a set of bools to define the behaviour. This is ok, but then inheritance has proved to be totally useless after all. Now imagine you’ve got a bunch of other attributes a bullet could also have – suddenly your Bullet class is getting pretty messy!

The concepts of explodingness and homingness do not have a parent-child relationship with each other

Here’s the problem summed up – the concepts of explodingness and homingness[2]highly accurate, technical terms I just made up now do not have a parent-child relationship with each other. Bullets can be one, both or neither, so these attributes are not above, below or even alongside each other. They’re totally independent. So trying to cram them into a hierarchy just isn’t going to work naturally.

Here’s where composition comes in.

Composition And You(nity)

Look back up at that simple Bullet class code above. Like a lot of the code you’ll write in Unity, it inherits from MonoBehaviour. What you may not know is that MonoBehaviour itself inherits from another Unity class – Component[3]In actual fact, Component is a couple of levels above MonoBehaviour, but the point stands.. That name isn’t a coincidence. This is Unity’s composition system, and (stay with me) it’s one of those times where inheritance has been used really well – by inheriting from MonoBehaviour, you get access to a really powerful set of composition tools by default.

Composition is the idea that objects can be put together bit by bit, instead of inheriting wholesale from a hierarchy. One of the holy grails of object orientation has for a long time been multiple inheritance – the idea that classes can inherit from multiple different parents instead of just one. In practice, this has proved almost impossible to do “correctly”, and very few languages support it (C#, for example, does not[4]although some might argue that interfaces are an extremely limited form of multiple inheritance.). Those that do all have a different way of doing it.

One could easily argue that the whole venture is flawed, and that a compositional model solves the same problems without all the headaches. A frequently used maxim is that inheritance gives things “Is A” relationships, whereas composition gives things “Has A” relationships. The first is very strict; something that “is” something else must adhere to all its parent’s constraints. The second is not – if something “has” something else, it can also “have” lots of other things. This allows you to cherry-pick attributes to compose an object with the desired behaviour.

Under this model, homingness and explodingness, since they are independent attributes, are behaviours that can be coded into separate components. These components can be combined with the Bullet class to modify its basic behaviour. In Unity, this is done by simply adding any combination of the three MonoBehaviours to a GameObject in the editor!

public class Exploder : MonoBehaviour {
	private void OnDestroy(){
		// KABLAMMO
	}
}

public class Homer : MonoBehaviour {
	private void Update(){
		// Rotate towards target
	}
}

BulletComponents

This has other benefits, particularly when viewed in terms of encapsulation. Each component has a single responsibility, and need not know anything about the others. The Bullet component doesn’t need to know that it’s going to explode when it hits something, or that it’s going to home in on an enemy. It just needs to move forward and do damage if it hits an enemy. Likewise the Exploder and Homer components don’t need to know they’re attached to a Bullet, or communicate with it. And you can add any number of attributes in the future by creating new components, without having to rewrite existing components to account for them. Furthermore, these behaviours can be added to things other than Bullet, if so desired.

Of course in real life, components do often need to talk to each other. Again, Unity has you covered, with methods such as GetComponent<>(), and (somewhat differently) attributes such as [RequireComponent()]. Being built from the ground up around this type of architecture, composition is often the easiest and best way to do things in Unity.

Conclusion

Hopefully it should be fairly obvious that composition allows you to create a highly modular, encapsulated codebase which can be extended and reused in a really flexible way. Unity’s component model is great, and it’s worth trying to work with it rather than against it. But as I’ve said before, no one technique is the silver bullet that will make all your code awesome, and there are plenty of cases where composition isn’t the best tool to use. Likewise, inheritance is a hugely useful tool when used correctly. The only problem is the (seemingly widespread) tendency to see everything in terms of inheritance.

Inheritance is great for generalising, whereas composition is great for specialising

In my experience, a good general rule of thumb is that inheritance is great for generalising, whereas composition is great for specialising. If you’ve got something you need almost all of your code to do, it’s often a good idea to create a base class which implements frequently-used functionality, framework or glue code[5]It’s also worth noting that not all your code needs to, or should, inherit from MonoBehaviour – if it doesn’t make sense as an object “in” the game world, you’re often better off with vanilla classes. But that’s a whole other topic.. This is exactly what MonoBehaviour does, to hook you into the component system and so forth. But if you’re coding gameplay-level behaviour that may or may not be used by specific things, compositional thinking can be exactly what you need.

Footnotes   [ + ]

1. in my opinion, as always
2. highly accurate, technical terms I just made up now
3. In actual fact, Component is a couple of levels above MonoBehaviour, but the point stands.
4. although some might argue that interfaces are an extremely limited form of multiple inheritance.
5. It’s also worth noting that not all your code needs to, or should, inherit from MonoBehaviour – if it doesn’t make sense as an object “in” the game world, you’re often better off with vanilla classes. But that’s a whole other topic.


10 Comments

  1. Good post. Unity MonoBehaviour inheritance is not very new-comer friendly as many object don't care about Transform, dependency injection is not available and everything is thightly coupled requiring singleton usage - and as Robert Nystrom points out in his well-known game programming patterns book "friends don't let friends use singleton"... I recently found Zenject and uFrame whcih seems to cope with some of my issues but as a junior Unity user I don't see if they assist Unity editor or they fight against its internal. Do/did you use this kind of stuff?
    • I wouldn't say Unity isn't newcomer-friendly in general - if you're concerned about the lack of DI/IoC, you're probably not the kind of newcomer I'm talking about :p While Unity is pretty tightly coupled, I don't think the way the API is structured is problematic for beginners, but it does become so at an advanced level.

      I'm personally using StrangeIoC; quite close to Zenject (which I've not tried but would like to, as a comparison to Strange), but uFrame seems to be extremely comprehensive and very different to pretty much anything else out there (and again, I've not used it).

      But yes, in my experience Strange does kinda fight against Unity instead of working with it (which is the Unity API's fault, not Strange's!). In future I'm planning to 'back off' with how I'm using IoC with Unity - essentially separate my code into 'scripts' which will mostly ignore Strange, and 'framework' which will use Strange. That way, scripting that actually needs to use the Unity API doesn't get overcomplicated by trying to mash it together with Strange.
  2. goals in all competitions this seasonMertens has been linked with a move to England this summer with Manchester United and Manchester City leading the race.
  3. Amman Arab academic world is a Jordanian educational institute of cutting edge education, located upon Jordan Street, Mubis-Amman. Amman Arab college circles (AAU) was founded in 1997 below the far ahead Education Council No. (1476) upon 24/11/1997 as a non-profit private academic circles specialized in graduate studies below the declare Amman Arab university circles for Graduate Studies, and thereby became the first university in Jordan that offers graduate programs leading to Master's Degrees, and Doctoral Degrees. upon 30/9/1998 the highly developed Education Council gave comply for the academic circles to start full of zip past the given No. (1625). Academic operation started in the start of the Second Semester of the academic year 1999/2000. One of Jordanian Private Universites
  4. My children and I are thrilled that we happened upon the site, this is absolutely the kinds of things My children and I are constantly scanning around for last week. The detailed information on the web forum is information that helps - most experienced and will provide back up for my friends from work and I quite often. It shows that all of the members on here acquired a lot of incredible of benefitial topics concerning pointers I am always researching and the other hyper links and bases of knowledge like wise show it. I am not usually on the net during the week but when my friends get an opportunity I am more often than not poking around for this kind of knowledge and stuff similarly having to do with it. I have two of my friends that have also assumed a liking about this due to what I have learned of it and they're probably to visit the website since it's such an one of a kind learning place. I'm also fascinated in Surviving Pandemics and cope with the new changes in modern web design as well as looking for alternate hunting resources companies to feed my hunger for making advances in my own technology. If you needed major services like: trademark lawyer for infringement
  5. I do agree with all the ideas you've offered to your post. They are really convincing and will definitely work. Nonetheless, the posts are too short for newbies. May you please extend them a bit from subsequent time? Thanks for the post.
  6. I'm excited to portion my thoughts on the contemporary website! The design is tasteful and informed, instantly capturing my attention. Navigating utterly the pages is a task, thanks to the user-friendly interface. The comfort is revealing and engaging, providing valuable insights and resources. https://www.toneighborhood.com/forum-1/general-discussions-1/math-assignment-helper I be au courant the limelight to group specifically and the seamless integration of features. The website in reality delivers a jumbo consumer experience. Whether it's the visually appealing visuals or the well-organized layout, the whole feels grammatically salutations out. I'm impressed by way of the travail press into creating this dais, and I'm looking consign to exploring more of what it has to offer. Guard up the cricket duck slog away!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>