In this lecture, we'll look at the event manager for the feed the teddies game. This event manager is slightly more flexible and robust, but also a little more complex than the event managers we've seen so far in the course. As you can see, we have a number of scripts in our events folder and our scripts folder for our project. The GameOverEvent, GameStartedEvent, HealthChangedEvent, PointsAddedEvent, TimerChangedEvent, and TimerFinishedEvent are the standard events that we've seen before. We're inheriting in a variety of different ways from the unique event family of unity events. But the EventName and EventManager and IntEventInvoker are new, so let's go and take a look at those. We'll start with the event name enumeration. This is an enumeration of the five different event names that we're going to use within the EventManager to connect invokers and listeners. We haven't included the TimerFinishedEvent here because we're not linking invokers and listeners together through the EventManager for the TimerFinishedEvent. These are the other five events that we use in our game. Our IntEventInvoker class extends MonoBehavior so that we have a dictionary of unity events that a class that inherits from IntEventInvoker can invoke. The big idea is we have a number of classes within our game that invoke one or more events that are a UnityEvent with one int parameter. We create this class as a parent class so that all those other classes can just share this behavior, and because of polymorphism, we can just have references to int event invokers within our EventManager, and whatever class is a child class of this class can be included in the structures in the EventManager that we'll look at soon. The reason we have a dictionary is because a particular child class might invoke more than one of these events. We want to be able to easily look up by EventName, the UnityEvent object that that child class will use to invoke the event. As we discovered when we learned about dictionaries, we can key by any datatype, and here we're keying by that EventName enumeration that I showed you already, and the value that we'll have in here is a UnityEvent with one int perimeter object. Of course, I create a new dictionary here when I'm declaring the field for this class. This class also exposes an AddListener method. Whoever is calling this method provides the EventName, the name of the event that they're trying to add a listener for, and they provide the listener, they want to use to listen for the event. Of course, UnityAction with one int parameter is the appropriate form of UnityAction to use to listen for a UnityEvent with one int parameter. We need to check here to make sure that whatever EventName got passed in as a perimeter is actually contained in the dictionary of events that this particular object might invoke, and if it is, we do our standard access using square bracket notation, the event object that we're trying to add listener to. Then we just call the AddListener method passing in the listener perimeter that we got up here as an argument to the AddListener method. That's it for the IntEventInvoker class. Finally, in our EventManager, we have a dictionary that's also keyed by EventName for the invokers of our event. But the value here is a list of IntEventInvoker objects. We need this to be a list because we may have more than one invoker live in our game at a particular time for a given event, so we can't just have an EventName and a single IntEventInvoker. We might have multiple invokers for a particular EventName active at any given time. That's why we need to list, as we've seen before, just not in the context of a dictionary, and this will let us look up the list of invokers for a particular EventName when we need to. Similarly, we have a dictionary of listeners also keyed by EventName, but this time it's a list of UnityAction int delegates. Again, we may have multiple listeners for a particular EventName at any given time. We need a list of those listeners, not just a single one. The nice thing about using a dictionary here is we only need two fields, even though we're going to connect invokers and listeners for five different events. That's really helpful to us, instead of having an invoker's field and a listener's field for each of those five events, which would be 10 fields instead of these two fields. We'd also need to have 10 methods for adding invokers and listeners to hook them together, and as we'll see as we work our way through the EventManager, we actually only need one AddInvoker method and one AddListener method. We do have an initialized method which we haven't needed before in the event manager. The way this works is it creates empty lists for all the dictionary entries. This makes sure that we have a list for every event name in the invokers and listeners dictionaries and that those lists are empty. The reason we need to make sure they're empty is because if we play the game and different objects have registered as invokers and listeners, and then we leave gameplay and come back to the main menu and then go play the game again. We need to start with fresh dictionaries. We can't start with dictionaries with stuff already in them. This may look weird to you in our foreach loop. You can explore this more if you'd like. But the big idea is we're going to iterate over each of the values in our event name enumeration, putting each of those values into name. In the body of this foreach loop, we first check to see if the invokers list does not contain the key of the enumeration name that we're currently looking at. Because if we try to create a new list for a key that already exists, we've already seen we get an exception thrown because there's a duplicate key already in the dictionary. First we check to make sure that our invokers dictionary doesn't contain the key, that's the current event name. If it doesn't, then we're safe to add a new entry to the dictionary. We add keyed by name, and a brand new empty list of int event invoker objects. We can also add an empty list of unity action int delegates to our listeners dictionary. Because if the invokers dictionary doesn't contain that key, the listeners dictionary doesn't contain that key either. If, on the other hand, we already do have an entry in the dictionary with the name we're currently processing, we just clear the list for the invokers and listeners. After we've completed this for each loop, we know that the invokers dictionary and the listeners dictionary has an entry for each of those five names and the enumeration in the list for each of those entries, the value for each of those entries is an empty list of the appropriate datatype. Now, down to some more familiar stuff, adding an invoker, although we do include the event name as well as the invoker we're trying to add. Notice that we pass in an int event invoker so as long as our invokers inherit from int event invoker, this will all work fine. We do a foreach loop across all the listeners in the listeners dictionary for this event name using that square bracket notation to access that particular list as a value. Each listener, we process one at a time. We just add that listener to the invoker using the add listener method that's implemented in the int event invoker class. Remember we pass in both an event name in the listener delegate. After we've added those listeners, although listeners that have been hanging around waiting to be added to an invoker, we also add the invoker to the dictionary for the provided event name. Add listeners does something similar? We pass in the event name into the delegate we want to add as a listener. Then we go through all the invokers in the dictionary of invokers for this particular event name. Remember this will retrieve a list of int event invoker objects for this particular event name. Then we grab each of those invokers one at a time through the foreach loop and add the given listener to that invoker. Then finally, when we're done hooking up all those existing invokers to this new listener. We add the listener to the dictionary in the appropriate list. The last thing we do in the event manager, is we have a new method called remove invoker, where we pass in the event name and the invoker and we remove the invoker from the dictionary. This is an efficiency thing. Over time, our invokers will get destroyed as we play the game. We don't need to keep them hanging around in the dictionary of invokers after they've been removed from the game. This is a good way to clean up when we're destroying an invoker, we remove the invoker from the dictionary so that we don't have dead invokers hanging around in our dictionary. Those are the core ideas behind the event system in the feed the teddies game. To recap, in this lecture, we explored the implementation of the event manager in the feed the teddies game. We saw, how we can use a dictionary and an event manager, though of course, we'll use dictionaries and lots of other places in our game development as well. In this event manager is really one step away from a commercial quality event manager. The one step would be to add support for different invoker and listener data types. Instead of just an int event invoker we might have a float event invoker and other kinds of invokers and listeners. That's exactly what I'm doing in my balloons extreme game.