The Power of Scriptable Objects as Middle-Men

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
scriptable objects are data containers which live outside the scene and are also persistent meaning that their values do not change when the scene reloads unless you programmed that functionality so they're a great way of storing data you may need such as the health enemy attack etc however there is another use for them and if you're interested in learning more about scriptable objects i'd recommend my previous video before watching this one which explains scriptable objects what they are and how to use them and their benefits but there's also another use case for scriptable objects and that's called event-based messaging and it's where the scriptable object kind of acts as a middleman between two different scripts or components so we can decouple the code which basically means to separate or to remove dependency onto components and when you remove dependency it makes your life easier makes it easier to test more flexible and easier to debug but it is a great way to architect your game to be flexible and easy to change for both the coders and designers and what i'm going to show you is based on a unite austin 2017 talk which is actually pretty well known it's called game architecture with scriptable objects and it's given by ryan hipple a principal engineer at shell games so i definitely recommend checking out this video because they go more in depth into scriptable objects and how to architect your entire project to use scriptable objects and on the topic of game design this video is sponsored by mental checkpoint if you haven't heard of mental checkpoint they're an amazing youtuber who actually made the game move or die which is an awesome game on steam if you haven't played it already and their channel is full of super useful and secret tips on game designs that i have not heard in many of the other videos that i watch and this is largely due to their experience in the field the editing and pacing of the videos are also amazing so if you're interested in learning about game design i definitely recommend you check out mental checkpoint alright so i'm going to show you how to use scriptable objects for event based messaging now what does that entail so first let's see how we would usually manage our player health so let's say we have a player controller which you can ignore all of this but the important thing is let's say we have a health here of a hundred and then on awake we set the health to the max health and then we have a function here called decrease health which decreases the health and you'll see that we have a reference to the slider here which the slider is just a ui slider on the scene and we set the value of the slider whenever we decrease the health so you'll see that right here we need a reference to the slider and of course we need to import the unity engine ui namespace to use this slider so what happens here is that when the player enters this red box it loses health and when the player enters this green box the level reloads so if we enter this red box you'll see that the help decreases by 10 which is just a simple script here health decrease trigger that we attach to this box and we have this on trigger enter function if the other tag is called player then we get a reference to the player controller and we decrease the health seems simple enough right and make sure to add a rigid body disable using gravity and also marking the object as static if it's not moving as well as enabling its trigger on the box collider so this is simple enough we walk into the green box and the level reloads and we have our health back however there are multiple issues with this first of all now the player has to have a reference to the slider so now the player is dependent on the slider so what happens if we have a level where we don't want the slider but we still want to decrease the health well in that case you'd have to change the script around so that you don't accidentally have any null reference values or you might be accessing this slider when you don't actually have one in the scene with game architecture you want to try to reduce dependencies between things that don't need them does the player really need to depend on a slider no the player should only be in charge of what it needs to do and in the coding world this is called the single responsibility principle it states that every module class or function in a program should have responsibility over a single part of that program's functionality and it should encapsulate that part this is to prevent dependencies between different things that might not need them because if you have dependencies it makes it harder to test because now if i wanted to put my player in a different scene to test just the movement now i'd need to import the slider over or else i wouldn't be able to test this accurately and this is just a simple example what happens if the player starts depending on a bunch of other stuff on the audio what if the player depends on the enemies themselves then to test this we need to import all of those variables into a new scene just to test if the player mechanics are working and that is not scalable so this bad another issue is that what if we want to keep the health over different scenes what if we don't want it to reset well since we're reloading the scene somehow you'd need to pass the value over to the new scene and i'm actually going to explain this in the following examples all right so let's see the example number two so what a lot of people do is that they have a health manager now instead of the player having a direct reference to the ui you have a manager as the middleman speaking between the two parties which helps with the single responsibility principle since this health manager is only managing health and now the player doesn't need to worry about the slider so what's common practice is to make a singleton of managers and a single 10 just means a single instance of this on a scene similar to a static class so you can only have one health manager in the scene but that means that it's just really easy to access for example if we go to this singleton class you'll see that we can access the singleton just by typing in the class name and dot instance and it'll return an instance of health manager all we're doing is putting the decrease health function here and in the awake function we're just setting the health back to the maximum health so this is refreshed when the new scene loads and now for the health trigger instead of accessing the player directly you can do health manager.instance.decreasehealth so now the trigger does not depend on the player it just depends on a manager that we can assume that will be in our scene at all times alright so if we click play here you'll see that the health decreases we reload the level and it refreshes the slider and the health so there's some issues with this the main one being the singleton class is generally frowned upon in the gamedev community and in general because of multiple reasons with singletons you have a global instance meaning that anyone can access it it also means that when someone accesses the singleton you create a dependency to this singleton and we're back at the issue with dependencies where now if something depends on the singleton and the singleton is not available for some reason many reasons can happen then you have a null reference and tracking that down and fixing it is just quite a hassle with singletons since this is a static instance it makes it hard to test because it's hard to track exactly what's referencing what so in general singletons have the issue where if you're not careful you can end up having a bunch of dependencies so you're singletons with your scripts and it can make it harder to test now if you're making a small project this isn't much of a problem however as you scale your project upwards and create a bigger project with different teams that's where the issue begins and another issue is that the health resets when we reload the scene and we don't want it to reset we want to keep the health the same throughout the different scenes like most games do now first let's tackle the issue of having the data persist along different scenes so usually if you follow this line of thinking and not use scriptable objects there's some ways to do that first you can use player prefs to save your values and then load it again when the scene reloads however i tried doing this and it was just kind of a mess especially when you start the game over again because the previous player prefs value is saved and you have to reset it and using player prefs isn't totally scalable if you have a ton of values another method is for your singleton your health manager is to use don't destroy on load so in this case i've changed the singleton to be singleton persistent which if you go to the main difference is that in the awake function we have the stone destroy unload which when loading a new scene does not destroy the game object it keeps it and with the singleton persistent value this makes sure that there's only one instance of the health manager on the scene even when the scene is reloaded so this is one way to make your values persist over multiple scenes by using a singleton and do not destroy unload and then the main difference here is that instead of accessing the slider directly we create a ui manager instance which is another singleton and we call that to change the slider value with the ui manager all it does is has this function called change slider value and that's what we call here from the health manager prefs so for example we press play and we reset the level you'll see that now we have the scene here which is the previous game objects and the don't destroy unload which is the health manager so since this was not destroyed the reason why we made a ui manager is so that the ui manager would have a reference to the slider and so that the health manager can access the slider since the ui manager is a singleton if you put the slider directly on the health manager once the levels were loaded it would lose access to the slider since the slider would be destroyed and a new slider is created when the level is reloaded so now you see this is getting a little bit complicated we're making managers we're making singletons you see how this can get messy really quickly additionally the health manager is assuming that the ui manager exists and that the ui manager has a reference to the slider so if the ui manager does not exist there's a null reference or if the ui manager doesn't have a reference to the slider that's another null reference and so following this line of thinking there's actually another way to have the value persist over multiple scenes apart from player preps and it's using additive scenes usually in unity you have one scene active the main scene and what you can do is you can actually have multiple scenes running at the same time and those are called additive scenes because they're added to the main scene and similar to the do not destroy that lives in its own bubble you can actually click in the hierarchy here to add a new scene you can delete the lights that the scene has and you can actually add a game object in that new scene and you can add your health manager in that scene what you can do is you can load in a new active scene this is the active scene where all your game objects are and where your light settings are which is important the directional lights and so you can keep this health manager scene running and instead just reload this main active scene now i'm not going to go over the implementation for that but just know that that's a possibility but the issue still remains where we're using singletons and the health manager depends on the ui manager and vice versa so what is the solution we can fix all of our problems really easily using scriptable objects as a way to communicate between scripts similar to sending events so what we can do is have a scriptable object with the health of the player and then whenever our player enters the red box which decreases our health that trigger will talk to the scriptable object which will then tell the scriptable object to decrease the health and the scriptable object will then send out an event which the ui manager will subscribe to and the ui manager will listen to that event when the health has decreased and when it has it will change the value of the slider so let's go over the implementations pretty simple we have this health manager scriptable object here it extends scriptable object so all we have here is an integer for our health which is our data and then we have max health which is the maximum amount of health our player can have so in the on enable of this script which is called also when this scriptable object is loaded in this case when we press play and the level is loaded this is called we set the health equal to max health so kind of like when the game starts we just set it to the max health and then what we're doing here is that we have an event a unity event called health change event and here we're just instantiating that event if it equals no and so what we're going to do here is when we decrease the health of our player we decrease the health as usual but now we invoke or we just call this event and we pass in the health so to make that easier to understand we have our trigger here so when the player enters our on trigger enter we call the health manager which in this case is just our scriptable object so we just have a reference to our scriptable object here and we call decrease health and so this will call this function and then any script that has subscribed to this event will get notified of this new health value and unity events are super powerful i definitely recommend using event based programming that's what the input system uses actually and side note the input system actually uses scriptable objects for most of its stuff such as the assets the input actions which i thought was pretty interesting besides the point here we invoke this event meaning we call it and then the ui manager what it does is that in the enable function we use our reference to the same scriptable object health manager scriptable object we do dot health change event and we add a listener in this case we add the change slider value function that we made here which just changes the value of the slider to the new amount and on disable we unsubscribe from this event so we remove the listener so this script is listening to the scriptable object and the trigger is telling the scriptable object what value is changing so the scriptable object now acts as the middle man and not only is it scene independent so we can avoid any problems with reloading scenes and the health will get passed on from scene to scene but we also now are not using any single tins and now these two scripts only depend on this one scriptable object and so we've dramatically decreased the complexity of having a bunch of scripts everywhere reference each other all we need to do is drag our scriptable object which you can right click create scriptable object health manager you can call it whatever you want and you can just drag in your health manager scriptable object into your field and when you press play and you reload the scene you'll see that the value still stays the same because it's still reading it from this scriptable object you see that now the health is at 90. you see that when we keep going here the health decreases and when we reload the scene the health is the same since it is reading this value from the scriptable object and when we unclick play and click play again you'll see that the value is reset and that is because of this on enable function that is called when the scene is played health equals max health so we've decoupled our code we've made our lives easier and we've also made the lives of the designers of our games easier and so a few other things i wanted to mention are the differences between using a scriptable object and a monobehaviour so with the monobehaviour it's c independent this is nuts independent however you could do something similar with a monobehaviour if you wanted to however since the monobehaviour is in the scene you have to instantiate a game object and that game object has to have a transform component just to act as a middleman whereas the scriptable object does not need that extra memory of the transform component and it does not exist in the scene which is nice and there's also something called dependency injection which dependency injection is when you inject a script with the dependency that it needs so instead of it needing to go and fetch that dependency itself it just waits to receive it and then it can use it for whatever it wants now this is kind of similar to what we're doing with scriptable objects but you'll need a whole framework for dependency injection and it's good because it decouples code and removes hard-coded references decoupling just means separating so it is kind of similar to scriptable objects in a way but scriptable objects are more designer friendly because you can easily drag and drop stuff in the inspector without much code with dependency injection the designers will have to look into the code to see what's going wrong to see how to change values with scriptable objects they can easily add in their own attributes it's visually friendly and it's good for building tools and holding data overall so i hope you enjoyed the video if you did make sure to like subscribe and sign up for notifications by clicking the bell icon and thank you once again to the sponsor of this video mental checkpoint i'd also really like to thank all of my patrons for the support their support makes these kind of videos possible and so i really appreciate it if you're interested the link is in the description i offer source code early access to videos sneak peeks exclusive discord chats and more and with that i'd like to thank my new patrons in the supporter tier we have thank you so much in the enthusiastic tier we have flamed keith andre mirko ever the journeyman nicholas jesse you 3000 banu popa sebastian stephen bernard paul knew thank you so very much for your support and in the dedicated tier we have brett thank you so much for all of your support it is really really appreciated if you're interested again the link is in the description and if you haven't joined our discord yet the link is in the description you can chat post memes or ask for help so thank you again for watching and i'll see you next time [Music] [Music] you
Info
Channel: samyam
Views: 108,021
Rating: undefined out of 5
Keywords: scriptable objects, scriptable objects unity, unity scriptable objects, scriptable, how to scriptable objects, how to use scriptable objects, ScriptableObjects, scriptable object, game architecture, How To Use Scriptable Objects in Unity, scriptable objects tutorial, ScriptableObject Tutorial, unity austin 2017, scriptableobject architecture, unity scriptableobject architecture, game architecture with scriptableobjects, scriptable object architecture, ryan hipple, middleman
Id: qUYpQ8ySkLU
Channel Id: undefined
Length: 17min 40sec (1060 seconds)
Published: Thu Feb 10 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.