For future reference: adding listeners with parameters to the Button’s onClick event can be done using UnityEventTools.AddXPersistentListener in the editor.
In builds a lambda expression or a local function can be used to conveniently convert a parametered delegate into a parameterless one, so that it can be passed to Button.onClick.AddListener.
To make debugging easier, it can be beneficial to define an abstraction for adding and removing listeners which always uses the prior method in the editor (even in play mode) and switches to the latter in builds. This is because runtime callbacks added through the UnityEvent.AddListener method aren’t visualized in the Inspector.
Wrapper for a callback that is usable both in the editor and in builds:
#if UNITY_EDITOR
using UnityEditor.Events;
#endif
using UnityEngine;
using UnityEngine.Events;
public readonly struct EventListener<T>
{
#if UNITY_EDITOR
private readonly UnityAction<T> action;
#else
private readonly UnityAction action;
#endif
#if UNITY_EDITOR
public static implicit operator UnityAction<T>(EventListener<T> eventListener) => eventListener.action;
#else
public static implicit operator UnityAction(EventListener<T> eventListener) => eventListener.action;
#endif
public static EventListener<int> Create(UnityEvent @event, UnityAction<int> action, int argument)
{
#if UNITY_EDITOR
var result = new EventListener<int>(action);
UnityEventTools.AddIntPersistentListener(@event, action, argument);
#else
var result = new EventListener<int>(action, argument);
@event.AddListener(result);
#endif
return result;
}
public static EventListener<string> Create(UnityEvent @event, UnityAction<string> action, string argument)
{
#if UNITY_EDITOR
var result = new EventListener<string>(action);
UnityEventTools.AddStringPersistentListener(@event, action, argument);
#else
var result = new EventListener<string>(action, argument);
@event.AddListener(result);
#endif
return result;
}
public static EventListener<Object> Create(UnityEvent @event, UnityAction<Object> action, Object argument)
{
#if UNITY_EDITOR
var result = new EventListener<Object>(action);
UnityEventTools.AddObjectPersistentListener(@event, action, argument);
#else
var result = new EventListener<Object>(action, argument);
@event.AddListener(result);
#endif
return result;
}
#if UNITY_EDITOR
public EventListener(UnityAction<T> action) => this.action = action;
#else
public EventListener(UnityAction<T> action, T argument)
{
this.action = OnEventInvoked;
void OnEventInvoked()
{
action(argument);
}
}
#endif
}
Extension methods that make use of the callback wrapper:
#if UNITY_EDITOR
using UnityEditor.Events;
#endif
using UnityEngine;
using UnityEngine.Events;
public static class UnityEventExtensions
{
public static EventListener<int> AddListener(this UnityEvent @event, UnityAction<int> action, int argument) => EventListener<int>.Create(@event, action, argument);
public static EventListener<string> AddListener(this UnityEvent @event, UnityAction<string> action, string argument) => EventListener<string>.Create(@event, action, argument);
public static EventListener<Object> AddListener(this UnityEvent @event, UnityAction<Object> action, Object argument) => EventListener<Object>.Create(@event, action, argument);
#if UNITY_EDITOR
public static void RemoveListener(this UnityEvent @event, EventListener<int> listener) => UnityEventTools.RemovePersistentListener<int>(@event, listener);
public static void RemoveListener(this UnityEvent @event, EventListener<string> listener) => UnityEventTools.RemovePersistentListener<string>(@event, listener);
public static void RemoveListener(this UnityEvent @event, EventListener<Object> listener) => UnityEventTools.RemovePersistentListener<Object>(@event, listener);
#endif
}
Usage example:
List<EventListener<int>> listeners = new List<EventListener<int>>();
void AddFiveListeners()
{
for(int i = 0; i < 5; i++)
{
int argument = i;
listeners.Add(button.onClick.AddListener(ButtonPress, argument));
}
}
void RemoveAllListeners()
{
foreach(var listener in listeners)
{
button.onClick.RemoveListener(listener);
}
listeners.Clear();
}
void ButtonPress(int argument) => Debug.Log($"ButtonPress({argument}");