Category
Programming/Engineering

Originally Published (Daklab.com)
Nov 24, 2013

Republished
Jul 3, 2015

Read
4 min

Tweet
Share
Comment

An Efficient Event Delegation Approach in Unity

Article Purpose

The purpose of this article is to introduce the Delegation Design Pattern, but specifically in the context of Unity and game-wide/application-wide communication.

This is a republished article from my old site. The original publish date was Nov 24, 2013.

Delegation Design Pattern

Delegation is a programming design pattern where “…a helper object, known as a delegate, is given the responsibility to execute a task for the delegator“. This definition excerpt comes from the Delegation Pattern Wikipedia page if you'd like to dig deeper.

What the above definition doesn't tell you is that there can be many delegators. This fact is what makes this pattern useful. At the core, you can have a single object (or set of code) that acts on the behalf of many objects – more bang for your buck. Say you have 20 buttons inside a container object and you want to set up click/tap event listeners for each button. Using event delegation, you can instead set up an event listener on the container as opposed to each button instance. This can lead to better code organization as well as code efficiency if used correctly. In the context of events, delegation is super helpful for these exact reasons.

Event Delegation

I'm used to event delegation in AS3 and JavaScript where this pattern is most commonly achieved through the use of event bubbling. I however prefer implementing event delegation by piggybacking Signals (AS3 Signals / JS Signals). Signals piggybacks the powerful observer pattern (#doublepiggyback) and it gives me maximum flexibility while encouraging loose coupling.

I've been digging into how the Unity community achieves event delegation, and I haven't had much luck… until recently. I posted this question to the 2D Toolkit forums and shortly after doing so a light bulb went off and I posted my solution. Shortly after posting my solution unikronsoftware (forum admin) responded with an example of how to achieve this via an event bubbling style approach. Had I not come across my own solution just prior, I would most likely be planning out how best to implement his suggestion. Though his solution is still a good one, I feel my solution is magnitudes lighter, cleaner, and easier to implement.

The Solution – Core Listener


//multiple hit check example
private void onEventDelegationClick(tk2dUIItem pUIItem)
{
    Vector2 pos2d = pUIItem.Touch.position;
    Ray ray = Camera.main.ScreenPointToRay(new Vector3(pos2d.x, pos2d.y, -1));
    RaycastHit[] hits = Physics.RaycastAll(ray);

    for(int i = 0; i < hits.Length; i++)
    {
        Debug.Log(hits[i].collider.gameObject.name);
    }
}

//single hit check example
private void onEventDelegationClick(tk2dUIItem pUIItem)
{
    Vector2 pos2d = pUIItem.Touch.position;
    Ray ray = Camera.main.ScreenPointToRay(new Vector3(pos2d.x, pos2d.y, -1)); //-z is z-depth of my UI
    RaycastHit hit;
    Physics.Raycast(ray, out hit);

    //UI level button example ("Button1", "Button2", etc as GameObject name)
    string hitObjectName = hit.collider.gameObject.name;
    int levelNum = int.Parse(hitObjectName.Replace("Button", ""));
    Signals.LoadLevelRequest(levelNum);
}

...and the code breakdown:

  1. Get a 2D touch point
  2. Create a Ray using this 2D touch point
  3. Cast the ray from the camera into the 3D scene in an effort to see what it hits
  4. The cast returns hit(s) in the form of a RaycastHit object or collection of RaycastHit objects
  5. Parse the hit(s) however you desire (for the single hit example above I preview a way to load levels)

The Solution – Core Delegator

Where event delegation comes into play is on the last line (Signals.LoadLevelRequest(levelNum);) in the example above. I make a call through my Event Manager which I call Signals.cs. In essence Signals is just a script which is attached to an empty game object in my scene. What makes this script special is that it has an Event API Chunk for each custom event type in my game/application. As a result of the way each chunk is set up, it becomes trivial to have game-wide or application-wide communication. In order to achieve this communication I utilize a C# delegate, a built in event object, and a public static method. Combined they comprise a single Event API Chunk. Here is an example:



//load level request
public delegate void LoadLevelRequestHandler(int pLevelToLoad);
public static event LoadLevelRequestHandler onLoadLevelRequest;
public static void LoadLevelRequest(int pLevelToLoad){ if(onLoadLevelRequest != null) onLoadLevelRequest(pLevelToLoad); }

With these Event API Chunks in place they can be combined with methods like the onEventDelegationClick() examples to achieve efficient, flexible, and light application-wide communication. For the example given I could have numerous listeners (say a GUIManager, a DataManager, and a LoadManager) all execute their own functionality based off this single event extremely easily. Though this particular example is dependent on user interaction, you can obviously use this same approach for custom events not initiated by user input. This is extremely powerful and has been the best approach I've come to know in Unity. I hope this is helpful, but if you know a better approach or have an idea for improvement, I'm all ears.