Delegates, Events, Actions and Funcs - The Observer Pattern (Unity & C#)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

The next video in a series of programming pattern videos! I'm making these mostly to firm up my own knowledge and understandings of these patterns. They have certainly been covered before, but hopefully, they are still helpful to someone.

πŸ‘οΈŽ︎ 7 πŸ‘€οΈŽ︎ u/OneWheelStudio πŸ“…οΈŽ︎ Aug 06 2020 πŸ—«︎ replies

Great video, I'm always interested in seeing how others are implementing their games.

I use events in my project along with the EventHandler or EventHandler<TEventArgs> delegates. This allows me to always know what object fired the event in case I need to use it downstream. It's just slightly different than the code you presented.

For defining the event:

public static event EventHandler<AdventurerAssignedEventArgs> AdventurerAssigned;

public bool AssignAdventurer(Adventurer adv)
{
  // assign adv here
  OnAdventurerAssigned(new AdventurerAssignedEventArgs(_zone, slot);
}

protected virtual void OnAdventurerAssigned(AdventurerAssignedEventArgs args)
{
  EventHandler<AdventurerAssignedEventArgs> handler = AdventurerAssigned;
  handler?.Invoke(this, args);
}

With the AdventurerAssignedEventArgs class as:

public class AdventurerAssignedEventArgs : EventArgs
{
  public AdventurerAssignedEventArgs(ExplorationZoneType zonetype, int slot)
  {
    ZoneType = zonetype;
    Slot = slot;
  }
  public int Slot { get; private set; }
  public ExplorationZoneType ZoneType { get; private set; }
}

And of course registering is the same as what you presented.

πŸ‘οΈŽ︎ 5 πŸ‘€οΈŽ︎ u/WeirdBeardDev πŸ“…οΈŽ︎ Aug 07 2020 πŸ—«︎ replies

I fucking needed this. Saved for later. Can’t wait to get off vacation

πŸ‘οΈŽ︎ 2 πŸ‘€οΈŽ︎ u/feircedeitylank πŸ“…οΈŽ︎ Aug 07 2020 πŸ—«︎ replies

Is using event manager approach considered the same as observer pattern? I notice some like to use that style where other object start/stop listening using static method and usually string event name.

What are the pro con using the event manager approach compared to using events directly?

πŸ‘οΈŽ︎ 1 πŸ‘€οΈŽ︎ u/hafizmrozlan πŸ“…οΈŽ︎ Aug 07 2020 πŸ—«︎ replies
Captions
the observer pattern is one of my all-time favorites i use it all the time now it's not always the easiest to wrap your head around but it is one of the simpler programming patterns to implement with unity and c-sharp the observer pattern is all about communicating between objects sharing information in doing so in a way that decouples the object that is sharing the information from the objects that might need or make use of that information using the same example project as from the last two programming pattern videos let's imagine the npc kills a critter and the npc's score goes up and is then displayed on the screen now one way to do this would be to have the critter call a function or change a value on the ui element this requires a critter to have a reference to the ui element which means using some form of find object in the code or assigning it in the inspector and this works it's how many of us did it when we first started working in unity in c-sharp but what if there is an achievement system too that system wants to display a message for the first kill every five kills and then every 10 after that do we link the critter to the achievement system does the achievement system connect to the ui element what if you have an audio system that plays a sound effect each time the score goes up you can probably start to see the problem and it gets even worse if the ui element changes or the designer forgets to put the achievement system into the scene then errors will get thrown and the game will likely break the result of all this is a mess of spaghetti code that is highly coupled or interconnected if any one piece is missing or if you change how one element works that could break all the connected pieces this is a brittle project and will not be easy to finish and yes we've all been there and we've all given up on projects just like that so this is where the observer pattern comes in and changes how objects communicate instead of all the objects being connected or having references to each other the critter can broadcast a message that it was killed then any objects that might be interested can choose to listen for that message or not and then do something based on what they've heard the critter no longer cares or is aware of the ui element or the achievement system if those systems change or aren't in the scene nothing breaks if new systems want to be aware of when a critter is called all they have to do is listen for a critter to broadcast that message and this is huge this turns the ui and the achievement system into observers of the critter the observer pattern is so useful that c sharp has essentially baked it into the language that makes the implementation of the pattern quick but not always super clear or intuitive so before we get to the implementation we're going to talk about delegates events and actions and a little bit about funcs all these bits are related connected and useful if you want to skip the explanation of these bits you can check the time codes and jump to the implementation i've seen few things in c sharp that confuse folks more than delegates there's just something odd or mysterious about them and admittedly there's a lot going on in the syntax of a delegate which can hide behavior that isn't actually all that hard to understand so let's try to clear some of that up and to give credit where it's due check out the two-part video by sebastian lag on delegates and events when doing my research i couldn't find a better explanation than those two videos he also goes over a few more or at least different examples than i will in this video but back to the topic of this video delegates can be thought of as a variable that can be assigned a function as a value a delegate can hold a reference to a function and then the delegate can be invoked and that function will be called now that may seem strange why wouldn't you just call the function itself but you can imagine a scenario where you may want to change what happens when a particular key is pressed one way to do that would be to invoke a delegate each time that key is pressed and to reassign what that key does you simply have to change the function that is subscribed to that delegate this is easy and hugely flexible but for me the real benefit comes from the fact that delegates in c sharp are multicast capable meaning they can have multiple functions subscribed to the same delegate so invoking one delegate can call as many functions as needed add to this the fact that delegates can be made public and even static and that allows external classes to subscribe and unsubscribe from the delegate and right there is the basis for the observer pattern now to keep things grounded not too abstract let's think about what that means for our example project if our critter has a critter kill delegate that gets invoked whenever a critter dies then our score keeping ui element and our achievement system can both subscribe to that delegate whenever a critter dies it invokes the delegate which in turn calls a function on the ui element and a function in the achievement system each individual class is in full control of which delegates it listens to ui element and achievement system have become observers of the critter to use delegates we first must define the delegate itself this is done by using the keyword delegate defining a return type giving the delegate a name and finally choosing the input parameters if any we then need an instance of the delegate these can be defined locally inside a function or in this case they are defined with a class-wide scope it is this instance of the delegate that will be subscribed to and invoked not the definition on the line above the instance of the delegate is defined just like any other field choose whether it's public or private and the type of the delegate and give the instance a name we need to subscribe a function to the delegate this is done with an assignment statement notice that we have not included the parentheses after the name of the function after all we are assigning the function not calling the function and it's weird i know but it does sort of make sense the last step is to invoke the delegate this line also checks if there are any subscribers actually it's a null check and this is done by the question mark the reason we need to do this is if a delegate is invoked and there are no subscribers an error will get thrown likewise you could use an if statement to check if the delegate is null and then invoke the delegate but the shorthand syntax is pretty handy if we attach the script to a game object and enter play mode we can see the debug statement in the console and all is working as expected now to be clear this is a simple implementation not necessarily how it should be done but i want to walk through the delegates step by step and not jump straight into the shortest and most abstract syntax delegates can have multiple input parameters and can even have a return value or both it's important to note that any function that is subscribed to a delegate must have the same type of input parameters and the same return value type in order to not throw an error delegate input parameters are a great way to send information to other objects for example when a critter dies it might want to send a reference to itself so that systems know which critter died that's not needed in this example but can be useful in plenty of other cases in general return values are not used with delegates this comes from the fact that delegates are multicast capable and they can call multiple functions this would mean multiple return values however only the return value of the last function called will be returned which can cause all kinds of confusion as it's not easy or even always possible to control the order that functions are subscribed there may be use cases where a return value makes sense but in general the observer pattern is not one of those more than one function can be added to a delegate by using the plus equals operator with each function this operator adds a particular function to a delegate likewise the minus equals operator will remove a particular function from the delegate in general it's a good practice to do this in the on and able and the on disable functions this is particularly important when a delegate is public and functions from other classes are subscribing if a function from a class instance doesn't unsubscribe that class instance is destroyed an error will be thrown when the delegate is invoked also if the assignment operator or just equal sign is used then all other functions will be removed from the delegate so in general plus equals and minus equals are the best practice for subscribing and unsubscribing from a delegate now as mentioned above delegates can be made public and even static in general i found that public static delegates are the most useful in the observer pattern if delegates are made public and static they are accessed and the subscribed to just like any other public static property or field okay are you still with me let's talk about events events are just delegates the difference is when we are creating an event we are actually going to create an instance of a delegate but with the keyword event in front of the delegate instance this is a few very important things the generic public delegate the list of subscribed functions could be overwritten by any class and a delegate could be invoked by any class neither of these are good things at least in general using the keyword event prevents these two actions from happening all that can be done publicly to an event is to add or remove a subscriber which is much much safer and in general when implementing the observer pattern we will want to be using events for this very reason beyond that the implementation of an event is identical to the standard delegate okay so delegates are awesome what's the deal with actions and funks it turns out that both of these objects inherit from delegates which makes them delegates and in reality they are just shortcuts to create a delegate actions are delegates that can have input parameters but do not have a return value whereas funcs are delegates that can have input parameters and have a return value but funcs handle return values as an out value that is always the last input parameter so what does this have to do with us and the observer pattern not a ton but what it does do is reduce the number of lines needed to create an event each event can now be defined on a single line of code the use of an action has already defined the delegate for us notice too that in the example shown the second event will handle an integer input parameter this is done as a generic argument to the action this input parameter is then assigned or determined when the event is invoked using actions this way is just a shorthand for what we've already done just with fewer lines of code okay if you're still with me and your brain hasn't totally melted let's apply the observer pattern to the example project for our example all the action happens when the critters die or in other words the score is updated and potentially an achievement notification is displayed so to keep things simple and keep going with the theme of decoupling our code i've created a new script that will invoke an event when the object is attached to the critter is disabled this is nothing too fancy but this lack of fanciness is no small part of the appeal of the observer pattern then we have code on the ui element that is displaying the score here the code subscribes to the critter killed event on the on enable function and unsubscribes in the on disable function then when the event is invoked the update score method is called finally we have the code that displays the achievement message this is very similar in that we subscribe and unsubscribe to the event and call a function when the event is in vote and that's really it the observer pattern in c sharp is basically built in but it's super useful all the same this is one of those patterns that if you aren't using it you really should be if it doesn't make sense then keep working until it does because it will make your project so much easier to maintain easier to add new features and best of all far less error prone all of which is worth the effort to understand this observer pattern so i hope that was interesting and better yet useful for your project and until next time happy game designing you
Info
Channel: One Wheel Studio
Views: 17,475
Rating: undefined out of 5
Keywords: Unity3d, Indie Game, Programming Patterns, Observer Pattern, Delegates, Events, Actions
Id: UWMmib1RYFE
Channel Id: undefined
Length: 11min 48sec (708 seconds)
Published: Thu Aug 06 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.