Best way to organize OnClick events for Unity buttons?

Unity has a component called Button as part of its UI system which you can use to subscribe on-click events to it through the inspector which is incredibly useful.

However, when projects get larger, I run into trouble in many situations:

  • events subscribed this way in the inspector are not rearrange-able which makes buttons that have lots of events difficult to manage
  • changing the contents of the scripts used for events can cause the button to not recognize a function that was used for an event which means you have to re-reference it
  • if anything happens to the GameObject or prefab that stores the Button component such as it getting corrupt then all your events that were serialized onto the button would be wiped and you would need to re-reference all of them
  • the above points make debugging very very difficult

What are some ways I can work around the problems I’ve listed above?

You can make a dedicated component that acts as an interface between Unity.UI.Button’s and anything else, which you can reinforce with RequireComponent. Said component just has a regular System.Action delegate that other objects can sub/unsub to, and you only have to hook the Button’s OnClick event to said intermediary component once.

Requires more but ends up being more decoupled.

What I sometimes having going on is my buttons are only hooked up to one component, and anything else that might care about the button subscribes to callbacks in that component. But I also have the aforementioned general purpose component in place most of the time.

You definitely can rearrange the order of events in OnClick(); just drag them up and down.

That said, I have never been a fan of button events for these and other reasons. Distributed, data-driven control is a debugging nightmare from which you awake into a worse nightmare when you start trying to overlay a FTUE or tutorial on everything.

My personal preference is to run a class-based state machine to encapsulate the current game state, and use a relay script attached to each button to send a string message to the currently active state. So the meaningful ‘OnClick’ events are always of the form Relay (this gameobject).RelayMessage(string), which is a completely stable pattern. inconsequentials such as sound effects or animations can be triggered via additional events in the OnClick.

It’s not as fast-cars-and-gleaming-teeth as an event free-for-all, but it has tremendous advantages in terms of readability and debugging. Everything that’s allowed to happen in any given game state is inspectable in code, and creating FTUE or tutorial states or locking out buttons during animated transitions is trivial.

I generally dislike dragging lots of random bits and bobs into place in a scene / prefab because it is completely “out of sight” from the actual code, and yet profoundly affects the entire app. About 90% of our day-to-day bugs are simply a misconfigured scene or button that one of us has to laboriously track down.

Therefore I wrote a package called Datasacks to let you sprinkle generic responders on all your UI items, and then control all the responding directly in code. It is entirely bound by the name of the GameObject, requiring you to keep that unique, such as ButtonPlay and ButtonBack and ButtonOptions.

But the cool thing is you could hand the UI off to an artist to completely redo, and all they need to do is drop a set of standard scripts into place, and without connecting anything up, it all just works as long as they kept the names correct and stable.

Here’s how it works:


Datasacks is presently hosted at these locations:

https://bitbucket.org/kurtdekker/datasacks