Hey there, I'm Matt and welcome to Game Dev Guide. This week we're taking a look at building a custom event system for your game. We'll look at how to build one and why doing so might be useful. But first, we're going to have to take a small diversion and talk about singletons and dependencies. If you're not already aware, a singleton is a design pattern that restricts us to having one singular instance of an object. If another object in our code needs some data from that object, we call upon the singleton instance to refer to it. Singletons are incredibly useful for getting access to classes that you know you're only going to have one instance of. This is most notable in Unity when you call code like camera.main or eventsystem.current. The design of the engine expects only one of these classes to be active at any given time in your project. So we can get a reference to it with a simple call like this. With that in mind, let's talk about dependencies. Let me explain a typical scenario and see if you fall somewhere into one of these two categories. In Unity, you're probably relying on one of two things to be able to access data from another class or object. You've either got a bunch of public properties in mono behaviors that you can assign in the inspector to get a reference to something, or you're using a plethora of singletons frequently in your code to get the data you need for your various components and objects in your game. Odds are that most of us would usually fit somewhere in that spectrum. Regardless of where you are positioned, you're sitting in a big bowl of dependency-flavored spaghetti code, and the moment you tweak, remove or refactor one of those scripts. You're probably having a bad time. This is especially the case on large projects where bloats are a real problem. Code can spiral out of control quickly, and dependencies can stack faster than the professional cop stacking champion. Yes, that's a real thing. Look it up. It should be pretty clear from this that anything we can do to mitigate dependencies in our code base is a good thing. So this is why we turn to a custom event system. A custom event system is a form of observer pattern. This is where an object, also known as the subject, maintains a list of its dependents known as observers. Then if a state has changed, the subject notifies all of the observers of said change usually by calling their methods. So for our event system, we're going to create methods that our scripts can subscribe and listen to. Then, in our game, whenever something happens that our observers are listening for, we'll tell the event system to dispatch that event. If you've ever used a Unity action or the event trigger components in the inspector, these are using a very similar methodology, just that our event system will be a single object for all of our code to point and talk to, rather than being spread across multiple components. I've got a scene here with a little cube, and I've got a wall and a door that I want to open when our cube enters the trigger area. We're going to do all of this using our event system, meaning that none of these objects will depend on one another existing for any of this to work. Let's start by making a new game object and adding a new script called Game Events. With the script open, we'll start by making a static Singleton reference to the script. Yes, I'm aware of the irony. Now we'll define our first event, which will be one that comes from a trigger point beside the door. So we'll create a public event action called on-doorwaytriggerenter. We'll then create a public method called doorway triggerenter, and we'll check to make sure that the action isn't null before invoking it. Next, we'll need to get our objects to subscribe to the event. First, let's create a method on our door that listens to the event and opens when it's dispatched. From the start method, we'll call the event system and we'll access the on-doorway trigger interaction like this. As you can see, because we define the action as an event, we can't just equate a single method to it. It will only allow methods to be subscribed and removed, so we use the plus equals operator instead, which basically just means add me to your list of subscribed events. Then, we'll simply tween our door upwards when the event is triggered. So now that we have the event listening, we need to trigger the event itself. From on our door trigger, we'll add an on-triggerenter method, then we'll simply call the event system until it to dispatch the event. We can see that the event system is triggered and the door opens. Let's repeat the whole process by adding another event into our event system that will tween our door to close again if the trigger is exited. And as you can see, we've managed this with only the event system as our single dependency. If I remove or delete any of these elements, everything will still carry on running smoothly. Similarly, if we duplicate our door and trigger a few times to make a series of rooms, we can see that we don't need to do anything extra to get this to work. It's handled by our event system automatically. But Matt, this causes all the doors to open, you might be saying. What if I want to link a door to a specific trigger? Well, that's a great question, friend. Let me show you. The beauty of our custom event system is the ability to pass parameters into actions. So let's add an integer into our actions and force this to be passed by the methods. We need to go through and update our subscribe and listen methods in our scripts to match this change. Unfortunately, we can't escape all dependencies. We'll also need to set up an index ID property on our trigger and door. And then we'll check that if the ID that's passed by the method matches the one on the door, open Sesame. And there we have it. We've now got events that are still agnostic, but they're using parameters passed into them to handle their behavior locally. One final thing, it's really important to unsubscribe the event from the event system when the object is no longer needed or when it's destroyed. Otherwise you'll be running into a large number of null pointer errors. And as you can see here, if I delete this door while the game is running and then walk into its trigger, it's not pretty. So let's go into our code and add a method to our door that unsubscribe from our event system when it's destroyed. Clean code is important, especially with systems like this, so just be vigilant and try to remember to stay on top of it. On a side note, you might be wondering if it's possible to give events a return type. Using the funk class, you can have an action give either a return type or a return type and parameters. For instance, if you want to return a list of doors that are currently in the game, it would probably look something like this. I would recommend limiting funk events like this as much as possible, and you probably want to have a single script subscribing to the event to avoid any issues, but for the sake of the topic, that's how you approach it. And that's about all there is to it when it comes to building a custom event system. Simply create an event system class and have it hold events relevant to your game logic. Then whenever you need scripts to talk to one another, subscribe and dispatch the event system. It's a much cleaner and much more flexible solution to passing data between objects in your game. Ultimately, there's nothing wrong with using multiple singletons in your code base. However, building an event system like this should help you cut down on unnecessary singleton instances and reduce heavy dependencies within your code. If you've enjoyed this video and found it useful, be sure to hit the like button and let me know what you think in the comments below. And of course, if you're interested in more game dev tips, tricks and tutorials, be sure to subscribe. I'll see you next time. Thanks for watching.