Unity Architecture - Composition or Inheritance?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up Jason here from unity3d college today we're going to talk about inheritance and composition what they are or when you should use them or at least when I use them and kind of what to watch out for before we dig in though I want to start with an example of a character that I think is set up badly or a script for a character that at least that I think I set up badly and should be broken apart either with inheritance and core composition and then we'll dive into how that would happen and what it would look like so to begin we've got this character here he's just a little capsule with a bad character script on him he's got us filled for a first name a move speed some starting health and a checkbox to see if he's a player already feels kind of dirty so let's open that script up and see what it looks like so in here my aptly named bad character script we've got some fields here for name I've got a move speed we've got a health the player check these are all the things that we saw in the editor remember they show up because the serialized field attribute is there and they're set us protected because in the other example we are using inheritance for this bad character it doesn't really matter imagine it's just the same as being private now in a wake we set our health to the starting health and then we can take some damage if we take damage we die dying this just destroying it and then in our update we have this check if it is a player do movement from input otherwise do some AI movement and the AI movements not filled in but the you know input for a character which is just based off of an axis like a joystick or something so this would move a character around and if we hooked up something for the AI we could toggle it to be a player or not a player and have it move around it seems reasonable right and I guess if this were the entirety of the code for the character it probably wouldn't be so bad it would be a little bit messy because we're doing a couple different things in here we're reading input dealing with health killing this thing handling its life cycle but it wouldn't be terrible because we're only at 50 something lines it's still small enough that anybody could comprehend it but in a realistic situation this is gonna grow right we're not gonna have just starting health we may have other attributes on here we may have a mana thing or a number of cards or whatever it is for your game and it's gonna keep adding and adding and adding we keep getting more and more things and what I see a lot of is people do this and they just start building up these big giant monster classes that no this is a thousand lines two thousand three thousand Lions just full of a bunch of logic that really should be split up and it's super hard to follow really hard to refactor hard to split things out and you start ending up with a lot of this kind of crap this check to see like hey is this a player is this box checked if this box is checked to go to be a totally different flow of stuff no and it like said gets to be pretty problematic as soon as you build it into a bigger project so let's jump over to some of the alternatives the first one I wanted to talk about and look at is just using inheritance so in this inheritance folder here let's slide that down by the way if you hold ctrl and mouse wheel up and down it adjusts that view I like to go to the list view when I'm looking at most things there aren't images it's also this little slider down here anyway let's open up the player character and take a look so the player character class is actually not doing much Oh cuz we want to look at the character first I opened up the wrong one so we want to open up the character character is doing a decent amount because this is our base class this class has the first name it's got a move speed it's got starting health and health it has all the things that our character would need whether it's a player or an NPC so if it's an NPC it needs health of its player needs health they both move they both have names it also has the shared functionality of taking damage so our players and our NPCs take damage if they take too much they die okay make sense so far now let's go back to that player character that I skipped ahead to earlier and take a look for this guy so here you'll see that in in the player character we're actually using the update method again in the character we weren't using update if we were we would have to call into that base update and we'd have to do an override right now we're not in fact let me just show you how to do that real quick so if we were needing to do something down here we'd do a protected protected virtual void update and this would like do something in the in the base update and then in the player character we would call we'd change this to be protected override void update and here you'd want to call base update that's just gonna make it call into the update on the base class and remember the base class is just to find here by putting a colon after the class name so our player character is inheriting from character right now it's actually calling in to the base update method doesn't do anything but it's calling it and then we're calling this do movement from input and here we're just getting the movement access from the axis again and then moving the thing cool that that's perfect for player you know we want the player to move around based on input well let's look at the NPC the NPC would be different right so on the oh let's change this back to protected override since we're calling into the base and blue base update now in here we may want to do our movement totally different so instead of reading the input our NPC has a method called do a eye movement and then this would figure out how the character would move around and this might seem like a perfectly fine way to split things up right you've got a base set of functionality then you've got a bigger set of functionality a lot of the time this works it works great if you're doing really small things too I use this all the time with relatively small classes that that aren't doing a whole lot of things in this case though we're still gonna end up with the situation where our character and our NPC are doing quite a bit unless we start splitting things out but we're also gonna run into some weird edge cases right so say we've got another type of NPC that's not just a regular NPC but maybe it's a merchant right and then the merchant you know it's an NPC it should spawn and maybe walk around you can interact with it but we don't want it doing whatever this AI movement is or maybe in here we've got like a Jew combat stuff and then this is you know responsible for making him go get aggro and attack things if it's a merchant we don't want to call this and just like check to see hey if I'm a merchant like what you could end up doing and what I see down a lot is things like let's just clutter it up so we'd have another class here public class merchants and it would inherit from an NPC and then on here it would have like a public list of string get items whatever return no we're just not gonna get back an actual list actually let's just make it a string return back a list of items right now it's just totally fake so what would happen here though I see people do this they'll create this merchants but then when they spawn their merchants it's still running this do combat stuff and what usually ends up happening is something like hey if this is merchants return like don't do this if this is a merchant still like I said it works but it's getting sloppy it's not following solid principles at all and things are gonna get problematic now again that doesn't mean don't use inheritance so there are a lot a lot of cases where inheritance is perfect one thing that I've found personally is if I'm inheriting something from something that's not an abstract class like this there's a good chance that there's somewhat of a design problem and we should probably refactor things by the way an abstract class just means that you can't implement or instantiate an instance of that class so our character class could never be added on to a component or created with like a new character call or something in in this case to model behavior so we couldn't just add a character we'd only add something that has character as a base class but like said a lot of time I'll use this for small things where there's an abstract class that kind of defines functionality but the way that I wanted to react is totally different between classes a lot like using an interface but with a little bit of shared functionality now let's take a real quick look in game and see what this guy would look like so here under this inheritance section I've got an NPC first-name move speed starting health exactly the same really because remember all of these fields are on the NPC or on the character in fact let's uh let's add another field here like sterilized field I'm gonna make a string string a class name like player class name and maybe this is like what's a good class a bar okay play that and lots of lots of games right so if we do that I just want to show what happens in the inspector you will see this new attribute show up this one it's always going to be at the end it's going to be after the base class unless you go in and build your custom inspector so the base class attributes will show up and then the inherited class will show up and then so a so if you keep going down and inheriting more and more and adding fields you're gonna keep seeing them add it on to the bottom again not the best solution the best solution in my opinion for these kind of situations at least is to use more of a composition model so let's take a look I'm just gonna start in unity and take a look at these and you can see here on the player instead of one component we've now got three components we've got a movement component we've got a health component and we have a player input controller if you look at the NPC almost exactly the same right character movement a health and an AI movement controller so let's take a look at how this is all put together let's go into the composition folder and I'm gonna start with well let's start with character movement so the character movement component is super lightweight it defines the movement speed of the character and it does the moving and it doesn't figure out how much to move because that's not its responsibility it's not a charge of figuring out movement how it figures that movement could be done any way we want we can do it over the network could be like network packets coming in moving our character around it could be a controller it could be an AI and we could switch it at runtime just toggle it we just need to call move with something else now let's look at another one let's look at the player input controller so player input controller you see now has a reference to a character movement in fact in this case if I were setting this one up and making these actual components I would add a require component attribute and tasin type of and give it the character movement what this is going to do is until the inspector or the editor to force a character movement onto this game object and it also make it so you can't remove the player input controller or you can't remove the character movement component I mean if the player input controller is there because it'll know that it requires it so in here and start which actually price should be awake in a way if we cache the character movement and then in update we're not actually calling it so we call character movement not move and pass in our movement amount again this may seem more complex but imagine the possibilities here now we can swap out this movement we don't have to just use a player you know we don't have to use the player input controller or the character movement we can swap them and can make them interchangeable and hook them up let me jump one more time over to the health component so the health component again starting health and a health amount in a wake we just reset the health basically take damage and then have it die now one thing I would change in in a real project instead of the health component killing the game object and just destroying it what I'd much more likely do is add in some sort of an event or call back when this character is going to die and then have that propagate out and maybe then the cooling system will remove this thing from the pool and any particle systems would clean up and that would look something like this it had like a public event action whoops action let's add using that system but it's control period to bring up that little menu in visual studio and this action would be like on died yeah something like that and then down here in die we go if on died is naughty well the No so it means if something has registered for that event then I call on died and then the things registering for the event would handle all the cleanup instead of the health destroying it except didn't be like the pooling system would register for this on dyed message particle systems or anything else on the character that really cares and that way we're moving more and more of this functionality into the right places kind of out of the wrong places like this and then the last one on here I think there's just an AI movement controller echo I wrote some code so why not pull it up so here again you'll see we have a requirement for a nav mesh agent and character movement and put the attributes there but they should be there and then we cache the nav mesh agent and we don't cache the character movement in here so here we go character movement equals getcomponent character movement there we go fix the code live so then in update it's like figuring out a new point and then moving in that direction not very good code for moving things around like said just because we're recalculating the path and just moving it manually I'd clean this up but I think you kind of get the idea we're still calling into character movement move just from a totally different source and it doesn't care and then everything is just composed on these objects so if I want to change this NPC to work like a player I can simply remove this component the AI Movement controller and drop in a player input controller and this is really useful when you're going in and like debugging a game testing things out you know you maybe you want to take control over some NPC gonna swap it swap it right here you could also hook it up in code so that you could swap these components at runtime or even make them not game object components and just have them be regular old classes that you swap and instantiate at runtime anyway I hope this kind of clarifies the difference again inheritance still really valuable it's just not it's not a go-to for everything it's not like the end-all solution you should use a mix of inheritance and composition in your projects I always look for position versed know what parts can I pull apart and slap together to make into something useful and that's kind of what unity does right we have these objects made up of a bunch of different components mesh renderer capsule Collider this capsule we add a rigidbody and a Collider all kind of stuff they're all components the system is built for it use it take advantage of it it'll make your life easier to make your code cleaner and happy times anyway thanks for watching the video if you have questions comments or just suggestions please just drop them below don't forget like subscribe and all the fun stuff also um if you're watching this and it's live like it just came out I recently opened up my unity mastery course I'll put a link in the description below but you can go check it out it's only open until next week then class starts and will be going on for six weeks just kind of diving into unity in depth building out a bunch of games and having a lot of fun alright thanks again for watching
Info
Channel: Jason Weimann
Views: 48,780
Rating: undefined out of 5
Keywords: Unity, Unity3D, Unity SOLID, Unity Architecture, Unity Component, Unity Inheritience, Unity Coding Patterns, Unity Clean Code, Unity3D SOLID, Unity Design Patterns, Unity Code Architecture, Game Architecture, Game Patterns
Id: 8TIkManpEu4
Channel Id: undefined
Length: 16min 23sec (983 seconds)
Published: Tue Feb 20 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.