I recently came across another hairy problem in Unity (version 5.2, as of this post), specifically using the new EventSystems. I eventually figured out that the problem could be solved by bubbling events, but I wasn’t sure how to do that. This post will recap the original problem and how I solved it, in hopes that it will save you hours of head scratching and frustration if you happen to be in a similar scenario.

The problem

First off, Unity’s EventSystems will bubble events automatically. This means that when you click an object that has a collider or a raycast-enabled UI component attached, Unity will begin traversing up the hierarchy in search of a handler, beginning with that original object. The propagation stops with the first object that can handle the event. Note that the object that eventually handles the event need not have a collider or UI component itself – it simply needs a component script that implements one of the many EventSystems interfaces such as IPointerDownHandler or IPointerClickHandler (full list here). It’s very straightforward and is common practice in event-handling systems. To read more about Unity’s EventSystems, click here.

There are, however, things about EventSystems that aren’t so apparent. For instance, you need to attach a Raycaster to your main camera for any of it to work. Also, if you implement IBeginDragHandler or IEndDragHandler without also implementing IDragHandler, Unity will never invoke your OnBeginDrag or OnEndDrag methods. If you think about it for a minute, it does make sense – though unfortunately it doesn’t seem to be documented anywhere. One of the stranger issues, which is what I ran into, occurs if you have an object with a script that implements IPointerClickHandler, while its parent object has a script that implements IPointerDownHandler:

using UnityEngine;
using UnityEngine.EventSystems;

public class Child : MonoBehaviour, IPointerClickHandler
{
    #region IPointerClickHandler implementation

    public void OnPointerClick(PointerEventData eventData)
    {
        print("Child clicked");
    }

    #endregion
}
using UnityEngine;
using UnityEngine.EventSystems;

public class Parent : MonoBehaviour, IPointerDownHandler
{
    #region IPointerDownHandler implementation

    public void OnPointerDown(PointerEventData eventData)
    {
        print("Parent pressed");
    }

    #endregion
}

At first glance, it would seem that clicking on either the child or parent would log their respective messages, since they each handle different events. This was the effect I was originally after. In actuality, however, you’ll see "Parent pressed" no matter which one you click. The child’s OnPointerClick method is never called.

The solution

Like many things in life, defining the problem is a big stride toward the solution. Though I can’t say for certain that I know what Unity is trying to do here, I’m assuming that it’s attempting to enforce that OnPointerDown and OnPointerClick be handled by the same handler (and probably OnPointerUp too, but I haven’t tested it), whoever that ends up being. Since a mouse button must first be pressed down and then released on the same object to register a click, the search for a handler probably begins by looking for someone who can handle OnPointerDown – in our case, that handler is the parent object.

From there, the solution is rather straightforward, given you know to implement it: we need the child object to be the first one to handle OnPointerDown so that it can also be the first to handle OnPointerClick – as was our original goal – while still allowing the event to continue bubbling afterward. After spending some time searching for help on the Internet and in the Unity docs, I finally found a method called ExecuteHierarchy on a helper class called ExecuteEvents:

public static GameObject ExecuteHierarchy<T>(GameObject root, BaseEventData eventData, EventFunction<T> callbackFunction)

This will essentially restart the event bubbling process, beginning with whichever game object and whichever event you like. Using this in our child behavior yields the following script:

using UnityEngine;
using UnityEngine.EventSystems;

public class Child : MonoBehaviour, IPointerDownHandler, IPointerClickHandler
{
    #region IPointerDownHandler implementation

    public void OnPointerDown(PointerEventData eventData)
    {
        // Handle event here AND in ancestors
        ExecuteEvents.ExecuteHierarchy(transform.parent.gameObject, eventData, ExecuteEvents.pointerDownHandler);
    }

    #endregion

    #region IPointerClickHandler implementation

    public void OnPointerClick(PointerEventData eventData)
    {
        print("Child clicked");
    }

    #endregion
}

Now, clicking each game object yields "Child clicked" and "Parent pressed" appropriately. I hope you learned something new. I definitely did, and I can see ExecuteEvents.ExecuteHierarchy being a very useful tool in a variety of situations, as it gives one full control over which events are consumed completely, and which are multiply-handled.


eventsystems unity event bubbling
PREV NEXT