Why would I want to know this?
As a young self-taught programmer, I often found myself writing tons and tons of boolean statements to determine if some event or action has happened. I listen to those events through Coroutines and other methods to return values. If you find yourself doing this as well, STOP IT!
Welcome to Events…
Intro
Lately, I’ve been trying to improve on my C# programming skills and found myself lacking knowledge of the basic understand for Events. So, while looking through numerous tutorials on MSDN and other blogs, I found most tutorials to be over complicated and lush with convoluted code not pertinent to the core concept. I don’t want this to happen to you!
With that said I will try to explain the basics of Events and how they are used in a project…
Singleton?
If you don’t know what a Singleton is, you probably should. A Singleton is a script that cannot be an Instanced or ‘Duplicated’. It is, well… Single.
I recommend using Singletons for things that do not need to be copied multiple times during a game. Such as an Inventory System. Typically, the player only needs one Inventory, so we don’t want to call Instances of it, we only want one. And when we call it, we want to make sure it doesn’t get duplicated.
There are many ways to create Singletons, but this method is often used because it’s simple…
// This class sits on my camera and handles all the clicks I send with a Raycast
public class Clicker : MonoBehaviour
{
// Singleton
private static Clicker instance;
// Construct
private Clicker() {}
// Instance
public static Clicker Instance
{
get
{
if (instance == null)
instance = GameObject.FindObjectOfType(typeof(Clicker)) as Clicker;
return instance;
}
// Do something here, make sure this is public so we can access it through our Instance.
public void DoSomething() { }
...
Here, I have a class ‘Clicker’ that is attached to my Camera. This class handles all clicks that I send into 3D Space with a Raycast.
To access my ‘DoSomething’ method from another script, I simply call…
Clicker.Instance.DoSomething();
This eliminates the need to use a bunch of Static Method and Variable calls, plus gives us one instance only!
Delegate and Event?
A Delegate can be thought of as a reference pointer to an object/method. When it gets called, it notifies all methods that reference the delegate.
So, first things first…
Define a Delegate and the method that gets called when it fires…
public class Clicker : MonoBehaviour
{
// Event Handler
public delegate void OnClickEvent(GameObject g);
public event OnClickEvent OnClick;
The delgate called ‘OnClickEvent’ passes a ‘GameObject’ that we can use to define what game object it came from. Then, we defined an ‘event’ OnClick that gets called when the delegate is called.
Now, in the same script, we need to call the delegate and pass it our GameObject. I’ve done this through a Raycast…
public class Clicker : MonoBehaviour
{
// Event Handler
public delegate void OnClickEvent(GameObject g);
public event OnClickEvent OnClick;
// Handle our Ray and Hit
void Update ()
{
// Ray
Ray ray = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
// Raycast Hit
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
// If we click it
if (Input.GetMouseButtonUp(0))
{
// Notify of the event!
OnClick(hit.transform.gameObject);
}
}
}
}
As you can see, if the Ray has contact an Object in the scene and we Left-Mouse-Click, we call the event and pass the GameObject.
The last thing we must do is reference the delegate from our other scripts that are listening to the call. For this I’ve created a class called ‘GoldPile’.
public class GoldPile : MonoBehaviour
{
// Awake
void Awake ()
{
// Start the event listener
Clicker.Instance.OnClick += OnClick;
}
// The event that gets called
void OnClick(GameObject g)
{
// If g is THIS gameObject
if (g == gameObject)
{
Debug.Log("Hide and give us money!");
// Hide
gameObject.active = false;
}
}
}
In our Awake() method, we’ve defined our listening Event and assigned a local method that gets called ‘OnClick’. ‘OnClick’ does not need to be the same as our delegate method, but it can be.
Note: In our previous post we added a Singleton to our Clicker class. This allows us to use Clicker.Instance
As you can see, we’ve also created the OnClick() method that passes our GameObject we clicked on.
Note: You must use if (g == gameObject), otherwise it will hide other instances of that method in the scene as well… This is why we pass the GameObject for reference!
Now you are free to add this method to any other script in your game if needed. Don’t forget to define the method and delegate in your Awake().
You can download the complete project for your reference HERE. Enjoy! :)
Thanks for the tutorial, appreciate it!
Welcome :)
"I found most tutorials to be over complicated and lush with convoluted code not pertinent to the core concept."
Oh man, you and me both.
Trying to learn this through those is agonizing to say the least. And the worst part is that this problem is not limited to the particular topic of delegates, events and singletons, but pretty much every goddamn programming tool. ******* hell.
I am eternally grateful for this tutorial.
@Reactorcore
I'm glad you found this useful!
Like you, I can't stand how SDK Documentation is overblown and hard as hell to figure out. I hope to keep bringing new tutorials like this to the DB Community.
@"******* hell."
A-men brother
The overall professional opinion over singletons is bad. And from most standpoints, they're right :) An Inventory does not necessarily call for a singleton implementation. An Inventory should be a component of a parent Player or Character class. The only thing that could call for a singleton implementation is some sort of manager/controller that needs to be accessed from a lot of places. But that's a design flaw on its own.
Also, is there really a need for you to make sure you only have one instance of a class? :) How many times did you accidentally declare random variables when doing a conditional check?
Other than that, great article. I appreciate the points on events and delegates.
Thanks for the tutorial!!
Nice tutorial. Thanks a lot for the great explanation. I used that approach in my Game State Machine example @ Blog.i-evaluation.com"
Singleton is perfect way to persist object and delegates are most elegant solution for handling events I have seen so far.
The only question I would have is about destroying it. According to Unity spec Garbage Collector doesn't destroy object if there is an reference to it and so shouldn't we be subtracting handler while destroying an object?
Whoa, buddy. You're misleading people here with that understanding of a singleton. It absolutely IS an instance, but the creation of it limits it to build certain properties of itself only once, setting them statically on the first pass. It's commonly used for things like database connections, third-party services, sockets, etc.... The idea is that you create the connection itself on the first use of the object and on subsequent instantiations, it reuses that static property.
Thank you for tutorial. helped me a lot. By the way, do we need remove event listener on destroying or disabling object?
Something like:
void OnDisable
{
Clicker.Instance.OnClick -= OnClick;
}
Yes, best way is to use OnEnable/OnDisable:
void OnEnable
{
Clicker.Instance.OnClick += OnClick;
}
void OnDisable
{
Clicker.Instance.OnClick -= OnClick;
}
Hi, I really like how your tutorial and how it made me think how to do similiar stuff in my Perliner game. I especially like the way you explain singletons and delegates. I'm not sure however if the example for delegates is the best one.
If you have a 1000 gold piles in your level, each time a player clicks something, all 1000 gold piles will be notified. Why, if you already know which gold pile should be notified, that is the one stored in hit? you could just do hit.onClick() or something similar...
This way there is only one call and not a thousand.
Beside that, great thanks. I started thinking about refactoring my Perliner code and how to handle similar stuff. I would think I could have a ObjectClick on any clickable (or otherwise actionable) object, and use a Delegate pattern to attach any other component of the object to it.
For example let's have an object enemy with ObjectClick component attached. I could also have an Enemy component attached to it, that would do:
GetComponent<ObjectClick>().OnClick+=OnClick;
The camera's Clicker class would do similar stuff to yours, but when a hit is found, it would do instead:
hit.transform.gameObject.GetComponent<ObjectClick>.OnClick();
This way OnClick() of ObjectClick would call onClick of component Enemy.
What do you think? Or am I doing something strange?
This comment is currently awaiting admin approval, join now to view.
This comment is currently awaiting admin approval, join now to view.
This comment is currently awaiting admin approval, join now to view.
This comment is currently awaiting admin approval, join now to view.
This comment is currently awaiting admin approval, join now to view.
Hey! Good job on your article! Very informative and not enough people understand the advantage of using events to create clean code struture.
I would be glad if you take the time to take a look at EpixEvents, a library I developed while working on several video games. I think it's the best library to use all the power of events without the drawback it could occure.
You can get it here: Assetstore.unity3d.com
Or get more info here: Epixeventspro.com
Cheers buddy!
Great article! Really helped me out, thanks!
A good tutorial! Thanks a lot, these article can help me to improve my event manager Action Box ( Athagon.com )
Thanks for the tutorial! Helped me understand how I would go about applying OOP to Unity scripts.
One thing I want to note is that in the Unity3D Scripting manual, it states that if you attempt to define a contstructor for script component, it would interfere with its normal operations (Unity instantiates its objects from the editor and does not take place at the start of the gameplay) and can cause major problems with the project.
I think just by accessing that script from another script, you're guaranteed the one instance that's created at start up.
Thanks.
This comment is currently awaiting admin approval, join now to view.
There are a couple issues you're going to run into with this solution
The singleton doesn't clean up after itself - you need to make sure to clear the static variable on destroy of the singleton. If you reload the scene with this singleton in it, you're going to be attempting to access a destroyed version of it.
You'll run into init order problems. You're accessing the singleton instance in awake. Awake should only be used for accessing things to do with yourself, you can't rely on the awake of your singleton to be called before GoldPile awake.
I normally use static events when dealing with singletons. You just need to be careful any anyone attached to a static event is detached on destroy, or you can end up keeping GOs alive much longer than you expect them to be.
This comment is currently awaiting admin approval, join now to view.
I think there's an error in the singleton code example. I get the error "not all code paths return a value" on Clicker.Instance.get.
Is anyone else seeing this?
Hi there,
Nice little summary, for a cleaner solution the event should check whether or not there are listener attached to it like this:
if(OnClick != null)
{
OnClick(hit.transform.gameObject);
}
This comment is currently awaiting admin approval, join now to view.
This comment is currently awaiting admin approval, join now to view.
In Simplest Manner :
Delegate : A Delegate is a reference pointer to a method. It allows us to treat method as a variable and pass method as a variable for a callback. When it get called , it notifies all methods that reference the delegate.
Events : Events adds a layer of abstraction and protection on delegate, this protection prevents client of the delegate from resetting the delegate and invocation list.
there is very good tutorial on this Unitygeek.com