For all Unity developers and developers-to-be, both beginners and professionals!

Post tutorial Report content RSS feed Delegates, Events and Singletons with Unity3D – C#

Here I demonstrate how to create delegates, events and singletons to work in conjunction. This tutorial is written for Unity3D, However, similar code can be used for any C# or .NET application.

Posted by on - Intermediate Client Side Coding

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! :)

Comments
TwisterK
TwisterK

Thanks for the tutorial, appreciate it!

Reply Good karma Bad karma+3 votes
vicestudios Author
vicestudios

Welcome :)

Reply Good karma+1 vote
Reactorcore
Reactorcore

"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.

Reply Good karma Bad karma+2 votes
vicestudios Author
vicestudios

@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

Reply Good karma+2 votes
AlexM.
AlexM.

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.

Reply Good karma Bad karma0 votes
Neobits
Neobits

Thanks for the tutorial!!

Reply Good karma Bad karma+1 vote
promosquare
promosquare

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?

Reply Good karma Bad karma+1 vote
codysnider
codysnider

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.

Reply Good karma Bad karma+1 vote
Enzx
Enzx

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;
}

Reply Good karma Bad karma+1 vote
Symyon
Symyon

Yes, best way is to use OnEnable/OnDisable:

void OnEnable
{
Clicker.Instance.OnClick += OnClick;
}

void OnDisable
{
Clicker.Instance.OnClick -= OnClick;
}

Reply Good karma Bad karma+1 vote
Chemec
Chemec

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?

Reply Good karma Bad karma+2 votes
Guest
Guest

This comment is currently awaiting admin approval, join now to view.

Guest
Guest

This comment is currently awaiting admin approval, join now to view.

Guest
Guest

This comment is currently awaiting admin approval, join now to view.

Guest
Guest

This comment is currently awaiting admin approval, join now to view.

Guest
Guest

This comment is currently awaiting admin approval, join now to view.

epixcode
epixcode

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!

Reply Good karma Bad karma+1 vote
lionroot
lionroot

Great article! Really helped me out, thanks!

Reply Good karma Bad karma+1 vote
germancr
germancr

A good tutorial! Thanks a lot, these article can help me to improve my event manager Action Box ( Athagon.com )

Reply Good karma Bad karma+1 vote
minq20
minq20

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.

Reply Good karma Bad karma+1 vote
Guest
Guest

This comment is currently awaiting admin approval, join now to view.

TimeJockey
TimeJockey

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.

Reply Good karma Bad karma+1 vote
Guest
Guest

This comment is currently awaiting admin approval, join now to view.

orchard800
orchard800

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?

Reply Good karma Bad karma+1 vote
JohannesDeml
JohannesDeml

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);
}

Reply Good karma Bad karma+1 vote
Guest
Guest

This comment is currently awaiting admin approval, join now to view.

Guest
Guest

This comment is currently awaiting admin approval, join now to view.

UnityGeek
UnityGeek

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

Reply Good karma Bad karma+1 vote
Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.

Established
Privacy
Public
Subscription
Open to all members
Contact
Send Message
Homepage
Unity3d.com
Membership
Join group
Group watch
Start tracking
Tutorial
Browse
Tutorials
Share
Related Games
Coco Blast
Coco Blast Arcade
Related Engines
Unity
Unity Commercial
Related Groups
Unity Devs
Unity Devs Hobbies & Interests
Vice Studios
Vice Studios Developer