Observer Pattern Game Programming Patterns in Unity C


Jason Weimann


game programming patterns,design patterns c#,observer pattern,game programming,design pattern video tutorial,game development,unity3d c# tutorial,design patterns interview questions,design patterns,unity,unity3d,unity 2018,unity 2018.2,unity 2018 tutorial,unity3d scripting tutorial c#,unity3d game tutorial c#,unity3d architecture,game code,game code architecture,databinding unity3d,unity3d observer,unity3d ui,brackeys,games,tutorial,c#,make a game

[Music] hey what's up Jason you're from unity3d College today we're going to talk about the observer pattern now the observer pattern is one where we have subjects that have a list of dependent properties or objects that are observing what happens with this subject and then the subject automatically notifies those observers of changes a really common use for this is UI stuff so we'll have a UI element and we want that UI element to automatically update whenever the data behind it changes and we see this a lot in games and in non game programs but today we're going to focus on a very game specific example of how you can use the observer pattern to separate out your code and make your project just a lot better in general now if you're following along with the game programming patterns book the observer pattern example that he gives which is I think a really great one is the in achievement system so we have things that happen throughout our game and then we want to bolt on an achievement system we want to be able to say hey when the player reaches this point or gets this many kills unlock some achievement and it could just be a local achievement system it could be something like the steam achievement system where we're sending up achievements and they're getting some extra Steam rewards and then things are popping up on their screen either way it's a pretty simple system to build in but we want to make sure that it's really segregated out we want to make sure that it's not intertwined with our game code I mean we don't want to have whenever a player Dyess we call into the achievement system and say hey achievement system a player died no we don't we don't want to have those direct connections or if they unlock the thing we don't we don't want that code getting mixed up into our game logic code so we use the observer pattern to really separate that out and make it nice and simple now let's dive into an example so we've got a scene set up right here I've got a bowl and then we've got three little objects and an achievement system before I well let's let's just run through let's play it and then I'll we'll look at the code see how it works and then I'll show you how to set it up in a much cleaner so here we go we've got the game running and if I run up and hit this spear with my bowl you'll see we unlocked the ball and I believe this will unlock a cube or a box up and then this one unlocks a capsule and if I hit them again nothing happens the second time so let's take a look at the code let's see how this is all working and then again we'll dive into a better example right after that so let's see we'll go into the project folder and then the script is right here we're going to start with this observer script so the basic setup for an observer and subject or the observable pattern is that we have some sort of an observer that reacts when it's notified so it can be notified essentially we have this class and we say that whatever is an observer must implement the on notify method because this is abstract we can't just create a new observer we'd have something like our achievement system that's an observer and you'll see that in just a moment the other part of this is a subject or the thing that's being observed and you see here that it has a list of observers we have a register observer method that adds the observer to that list and then we have a notify method in fact why is that so small let's make it nice and big so we have that list we're registering we're adding the observer when we pass in an observer and then we have a notifying method and this notify method loops through all the observers calls on notify passes in the value that it gets in the notification type all relatively simple there's not not a whole lot to this system in general and it's going to get even simpler once we go a little bit further so how do we implement these two things so let's take a look at the observer for the observer we have our achievement system so our achievement system the base class is observer and then we implement the start and here we're just clearing out the player perhaps you'll see why in a minute it's just when we start I want to reset those unlocks and then we're looping through all of the points of interest that are in the scene and registering this as an observer now I put a comment here to note that you probably wouldn't do this necessarily in the achievement system if you were going this route you might want to go in when you spawn the PIO I say hey also whenever what everything happens go go notify the achievement system but for this just to keep it simple as my not perfect example we're looping through all of them and registering this so that means that whenever the point of interest calls its notify our observer will be registered and on notify will get called then when on notify it does get called we'll see how that works in just a moment we checked the notification type make sure it's this achievement unlocked one we get a key which is just a string we're taking achievement plus the value of whatever this is and turning it to a string and then we're setting a player pref to one if it's not set so that's right here so if if it's already set we just don't do anything if it isn't set yet we set it to 1 and then we log in there that it's unlocked and then it's somewhere after that is when we would do our steam achievements or whatever system we're using you know a call out to their API to say that hey the player has unlocked it and here we've got this notification type there's only one entry right now but you can imagine there could be multiple entry types here now before we go on to the next type I do want to show the subject here so that is our point of interest so our point of interest is using the subject based class remember these are both abstract classes subject and observer we never employ or we never created a new one of those we just have base or subclasses that use these so our point of interest has a non trigger enter because this is a mono behavior remember subject is a mono behaviors base class and then ontriggerenter we just notify that hey whatever this P oh I name is that achievement was unlocked so that's how that's working we go in here here let's uh make this a little bit more interesting let's actually just debug and step through it so if anybody doesn't know what I'm doing here I've hit play in there we're gonna go hit attach to unity and Visual Studio you can do this in monodevelop as well so now we should be attached in a second it usually takes just a second depending on the size of the project to go through that and I'm gonna put a breakpoint right here and I think that's it so then I'll run over here and then what should happen is we kicked over to the code there we go we entered the trigger and now notify is being called the P oh I name is this the a ball and we have the notification type so now if I hit f11 and step in let's just get rid of all of this we don't need all this stuff here clear all this out so it's a little easier to see so we go into that subjects notify remember we're going into the base classes notify method and then if we look here we have one observer just put the mouse over you can see it and it's that achievement system so when I step over over over and into so I'm going to hit f11 and it should call into the observers or the achievement systems notify there we go and then you see that this is how the codes getting called so then we're setting the thing and marking that as unlock so this is kind of the whole stack of where it's going in the subject says hey something has changed observer go deal with it so this system works it's not ideal though in a c-sharp or unity sense because it does have some serious drawbacks the first one being that we have to use this observer and subject base class or we have to re-implement all of this stuff in here now we could get around that with maybe an interface and then reimplemented it but I still don't like the idea of putting all of this boilerplate code in multiple places and I don't really like the idea of needing a base class for all of these observable things and the observers themselves if I don't necessarily need it now there may be a good framework that you use that does this for you and use a base class for it and that could totally make sense I just wouldn't recommend building one from scratch like this yourself so instead what I like to do is go with version 2 here so let's go into my scenes folder and we have v2 and you see it's named events and that's because c-sharp kind of builds this observer pattern into the language itself with the eventing system in fact there are even observable collections of things so you can have a collection that you get notified whenever it changes we're not going to go quite that far we're just going to go into the events and building this out with events but there's a lot in there that you can do that just with the language that just isn't available in a lot of older languages or at least wasn't previously ok so let's take a look this we have our v2 version and you see everything looks pretty much the same it's green and we've got the bowl and the three guys again but our achievement system is now achievement system with events so it's a different script here and then our objects our points of interest are also point of interest with events so how does this work differently let's take a look first I'm just going to open up the PIO i with events and the number one first most important thing i want to point out is that the base class is now monobehaviour we're no longer using the subject base class because we don't really need it instead what we're using is an event and here if you look at line six we have a public static event and it's an action of type point of interest with events so that's essentially this class type right here and it's named on point of interest entered so you might be wondering what the hell is this if you've never used events never use static events or actions it might not make any sense at all but what this is essentially is an event that we can put on to this type this point of interest with events and register for without having a specific object instance so if we didn't have the static keyword here we'd have to find this point of interest we'd have to figure out each point of interest there attached so I couldn't delete oh yeah so we'd have to find them all kind of like we do in the achievement system with this find objects of type and go through and register for them all what I like to do instead in this case and not not for all observable stuff but for especially things like achievement systems is make these static so that we don't need a reference to it and then you'll see why in just a moment in our new point of interest system we'll be able to register for all points of interest without having to do any any finding of the points of interest we don't have to know what the points of interest are just have to know that there's an event here that gets called and how does it get called well let's take a look if we look at ontriggerenter you see that here we just check to see if point on point of interest entered is not equal to no then we call it and it's a static method on this class so again it's going to call that one and we're gonna pass in our point of interest that's why we have this here for this action now this action could of course be something else as the parameter type it doesn't have to be the class it could be something like a string and a four-hour call to it instead of calling passing in this because this is a point of interest not a string we could pass in something like this dot P oh I name and just pass the string name for our event instead of the full object that's just not how I have it set up for this one just because I wanted to pass the full object but it does work fine too don't think that you have to pass the full class in one of these events so how is this used how are we registering it in our achievement system well let's select it and I'm gonna hit shift f12 to find all references let's just drag it up right here and you see that right here on this one we're registering in our achievement system with events so let's go to that take a look here in our start again we delete all player preps just so that these are all cleared out and reset and then we call point of interest with events remember this is the class name or the class type that's why it's got that blue right there in the nice capital P and then here's our event and we say plus equals and then the method name and now the method name needs to have the same parameters as the action here so right now we have point of interest with events and that matches this if I change this to a string or something and maybe did like this dot toi name and go back you'll see that we're going to get an error well what's it there now we have an error saying that this doesn't match now I could change this around right right now I'm passing in the point of interest let's just change it let's make it a string and this could be P oh I and I'm gonna hit f2 and rename it to P oh I name and then we could get rid of that and get rid of that and now we have an event in our observable system that just passes in the P oh I name and the thing I like about this the most oh well I say the second most outside of not having to have that base class is that I don't have to pass around some sort of an enum for a message type or a string or anything like that because these events are all now registered based on what they are so I have an event for when a point of interest is enter I could also have an event for when I've killed an enemy and then in my achievement system here I could just register for or something like player dot on killed enemy plus equals and then here have another method that just does an achievement for killing an enemy I can even make this more generic and have a very simple one that they're all calling into the same method if I wanted to probably wouldn't though because I probably want to do slightly different things based on the different types of achievements that are unlocked now if I kill an enemy I may want to increment some counter and keep track and then unlocking the achievement at a certain point but I think the the biggest benefit that I'm getting here is that this code has zero coupling with my player or with my point of interest my point of interest doesn't give a damn if I have an achievement system it the achievement system could be pulled out could be swapped it could just be totally removed it wouldn't matter it wouldn't break the game it wouldn't impact it and I can change the way the achievement system works without impacting the game at all as well and if I have enough of these hooks in my game so if like a point of interest entered a thing died somethings health changed or whatever I can register for just about any of those and if I need to add in a new thing for a new achievement I can just put in a an event in there and then call that event whenever it's needed and again not couple in my achievement code now if you're worried about performance it could be an issue if you're calling some event too often so if you're like every frame you're firing off this event that hey this thing changed this thing changed this thing change hasn't changed it can add up it can be a problem so you definitely want to profile it but in a situation like this it's not gonna be an issue unless you did something bad in your code you're probably not doing these actions that unlock achievements that often and again if it's not in some hot path of code where it's just every frame it's really not going to be an issue and you're gonna get the big benefit of just really separated code in the ability to both on other things so I've got the achievement system in here carrying about on point of interest entered what if I want to play a sound I can just have a class that listens for on P oh I entered and then plays sounds whenever a POA is in Turkey to play a sound based on with P oh is it just be a generic sound it could be based on any other thing we could show a UI element we can do whatever we want whenever this P o AI is entered we don't just have to register one event this plus equals just means that hey whenever this does it whenever this fires do this thing in addition to whatever other things that you want to do now that's also important because in our achievement system it's not going to matter much but in a in another sit context where we have a thing that is listening and cares about stuff but then eventually stops caring about things maybe we've loaded a new level or something else has changed so that we don't want to get called when that event gets fired or maybe even it's a thing that got destroyed like our achievement system is there and then we destroy it halfway through the game be weird but it's just an example of a way that you could end up with an issue and that issue is that our event could be registered to something that's not really around anymore and it's gonna make this thing linger it's gonna make it not go away it's gonna cause problems so what we want to do instead is when we're destroying something that's registered for an event like this so do on destroy we want to say minus equals here so I copy that paste it here and I change this to a minus and this is going to unregister this callback so now when point of interest is entered it'll no longer call in to this object in fact if we do it without let's just let's come at that out let's save let's go in here and just show the issue just to make sure that it's really clear what the problem could be if you not if you're not careful and you don't deregister your events so we hit play and then we delete the achievement system and we run over here and you see that we're still getting calls to this so our unlocked ball is still getting fired that method is still around it's still happening even though that thing has been destroyed because it's not actually able to completely go away so make sure you unregister your events you could also end up with no references all kinds of errors if you don't unregister them but don't let that distract you or keep you from using the servable pattern it's an amazing pattern it'll definitely simplify your code and again not just with achievements but UI gameplay all kinds of stuff it's really really helpful really useful and probably the one that I use the most out of all patterns just in game development in general anyway I hope this is helpful for you if you have questions about observable pattern or observer patterns or if you want to know more about events or maybe you'd be interested in the observable collection stuff and just kind of learning a little bit more about that just let me know and I'll possibly dive in a little bit deeper on this stuff and then I also plan to continue just going through these game programming patterns so make sure that you like and that you're subscribed and getting all the updates and thanks for watching keep coding you