AddListener to OnPointerDown of Button instead of onClick

I’d like to register a callback to the OnPointerDown and OnPointerUp events of the UnityEngine.UI.Button(s) in my game to trigger two different sounds for down and up click. However, only the onClick event is exposed. Is there any clean method to get these properties through code? I’d rather not add the UnityEngine.EventTrigger component to every button individually and set them up in the scene, because of the amount of buttons in my project and I might want to change behaviour while testing, and therefore rather do it from one central location in code.

I was expecting this to be built into the Unity button component, because it’s such a common thing to ask for when assigning sounds to buttons and also the button must use it’s OnPointerDown event internally to trigger sprite transitions etc. Why wouldn’t they just expose these callback publicly like onClick already is?

Anyway, here is my code that got it working:

EventTrigger trigger = buttons*.gameObject.AddComponent<EventTrigger>();*

var pointerDown = new EventTrigger.Entry();
pointerDown.eventID = EventTriggerType.PointerDown;
pointerDown.callback.AddListener((e) => AkSoundEngine.PostEvent(downEvent, gameObject));
trigger.triggers.Add(pointerDown);
For every button that needs additional listeners, I add an EventTrigger component and the appropriate PointerDown EventTriggerType. This has much more overhead than I wanted it to have, but it still works better than adding sound components to 20 buttons manually.

PS: Of course, I’d still be interested in seeing a better solution than mine.
Edit - Custom UI Button
I just figured, that I could also subclass the existing Unity Button to add my desired functionality. I keep forgetting that the UI source is extendable. This works very well and feels much cleaner. Just to spread community knowledge, here is my button extension, which I use to later add two different click sounds for down and up.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using System;

// Button that raises onDown event when OnPointerDown is called.
[AddComponentMenu(“Aeronauts/AeButton”)]
public class AeButton : Button
{

  • // Event delegate triggered on mouse or touch down.*

  • [SerializeField]*

  • ButtonDownEvent _onDown = new ButtonDownEvent();*

  • protected AeButton() { }*

  • public override void OnPointerDown(PointerEventData eventData)*

  • {*

  •  base.OnPointerDown(eventData);*
    
  •  if (eventData.button != PointerEventData.InputButton.Left)*
    
  •  	return;*
    
  •  _onDown.Invoke();*
    
  • }*

  • public ButtonDownEvent onDown*

  • {*

  •  get { return _onDown; }*
    
  •  set { _onDown = value; }*
    
  • }*

  • [Serializable]*

  • public class ButtonDownEvent : UnityEvent { }*
    }
    To make the ButtonDownEvent also show up in the inspector, you will need to subclass the according ButtonEditor as well.
    using UnityEditor;
    using UnityEditor.UI;

[CustomEditor(typeof(AeButton), true)]
public class AeButtonEditor : ButtonEditor
{

  • SerializedProperty _onDownProperty;*

  • protected override void OnEnable()*

  • {*

  •  base.OnEnable();*
    
  •  _onDownProperty = serializedObject.FindProperty("_onDown");*
    
  • }*

  • public override void OnInspectorGUI()*

  • {*

  •  base.OnInspectorGUI();*
    
  •  EditorGUILayout.Space();*
    
  •  serializedObject.Update();*
    
  •  EditorGUILayout.PropertyField(_onDownProperty);*
    
  •  serializedObject.ApplyModifiedProperties();*
    
  • }*
    }

A simple way to add any event to an item via the Unity Editor is by adding an “Event Trigger” Component to any GameObject.

Just select your button, at the bottom, select “Add Component”, add the “Event Trigger”, then select “Add New Event Type”, select Pointer Down, then select Pointer Down or any other events you need.

Then drag in the script or GameObject with a script attached. The script will need to have a public function that will be called when the event is triggered. Select it from the Dropdown, and your event is ready to run!

Much easier than creating a whole new script to manage events when the generic events work.

Many thanks to this YouTuber for revealing this little gem to me. (Not in English)


–Ryan

try in the Update thread if your game logic for movement is used in Update.

Hey Guys. Thanks for your input.
I made an Extension Method out of it for my purposes.

public static EventTrigger AddTriggersEvents(this Selectable theSelectable, EventTriggerType eventTriggerType, Action<BaseEventData> onTriggerAction = null)
{
    EventTrigger eventrTrigger = theSelectable.gameObject.AddComponent<EventTrigger>();
    if (onTriggerAction != null)
    {
        EventTrigger.Entry pointerEvent = new EventTrigger.Entry();
        pointerEvent.eventID = eventTriggerType;
        pointerEvent.callback.AddListener((x) => onTriggerAction(x));
        eventrTrigger.triggers.Add(pointerEvent);
    }
    return eventrTrigger;
}

Both solutions suggested by Xarbrough are very good. I took the first and ran with it.

The inheritance solution is better if you don’t already have tons of UI elements to deal with. If you do, it requires replacing scripts and reconfiguring from scratch in Unity UI. Those settings wont copy and paste. Maybe in the past I’ve done that the wrong way, but I recall it being pretty rough.

IMyInterface and MyInterfaceMethod are pseudo code. The rest is theoretically functional, but could use some optimization:

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class LockInputComponent : MonoBehaviour, IMyInterface {
	private Button[] buttons;
	private InputField[] inputFields;
	private Slider[] sliders;
	private Toggle[] toggles;

	void Start () {
		buttons = GetComponentsInChildren<Button> (true);
		inputFields = GetComponentsInChildren<InputField> (true);
		sliders = GetComponentsInChildren<Slider> (true);
		toggles = GetComponentsInChildren<Toggle> (true);

		AddListeners ();
	}

	private void AddListeners () {
		for (int i = 0; i < buttons.Length; i++) {
			EventTrigger triggerDown = buttons*.gameObject.AddComponent<EventTrigger> ();*
  •  	var pointerDown = new EventTrigger.Entry ();*
    
  •  	pointerDown.eventID = EventTriggerType.PointerDown;*
    
  •  	pointerDown.callback.AddListener ((e) => MyInterfaceMethod (true));*
    
  •  	triggerDown.triggers.Add (pointerDown);*
    

_ EventTrigger triggerUp = buttons*.gameObject.AddComponent ();_
_
var pointerUp = new EventTrigger.Entry ();_
_
pointerUp.eventID = EventTriggerType.PointerUp;_
_
pointerUp.callback.AddListener ((e) => MyInterfaceMethod (false));_
_
triggerUp.triggers.Add (pointerUp);_
_
}*_

* for (int i = 0; i < inputFields.Length; i++) {*
_ EventTrigger triggerBeginEdit = inputFields*.gameObject.AddComponent ();
var pointerDown = new EventTrigger.Entry ();
pointerDown.eventID = EventTriggerType.PointerEnter;
pointerDown.callback.AddListener ((e) => MyInterfaceMethod (true));
triggerBeginEdit.triggers.Add (pointerDown);*_

_ EventTrigger triggerEndEdit = inputFields*.gameObject.AddComponent ();
var pointerUp = new EventTrigger.Entry ();
pointerUp.eventID = EventTriggerType.PointerExit;
pointerUp.callback.AddListener ((e) => MyInterfaceMethod (false));
triggerEndEdit.triggers.Add (pointerUp);
}*_

* for (int i = 0; i < sliders.Length; i++) {*
_ EventTrigger triggerDown = sliders*.gameObject.AddComponent ();
var pointerDown = new EventTrigger.Entry ();
pointerDown.eventID = EventTriggerType.PointerDown;
pointerDown.callback.AddListener ((e) => MyInterfaceMethod (true));
triggerDown.triggers.Add (pointerDown);*_

_ EventTrigger triggerUp = sliders*.gameObject.AddComponent ();
var pointerUp = new EventTrigger.Entry ();
pointerUp.eventID = EventTriggerType.PointerUp;
pointerUp.callback.AddListener ((e) => MyInterfaceMethod (false));
triggerUp.triggers.Add (pointerUp);
}*_

* for (int i = 0; i < toggles.Length; i++) {*
_ EventTrigger triggerDown = toggles*.gameObject.AddComponent ();
var pointerDown = new EventTrigger.Entry ();
pointerDown.eventID = EventTriggerType.PointerDown;
pointerDown.callback.AddListener ((e) => MyInterfaceMethod (true));
triggerDown.triggers.Add (pointerDown);*_

_ EventTrigger triggerUp = toggles*.gameObject.AddComponent ();
var pointerUp = new EventTrigger.Entry ();
pointerUp.eventID = EventTriggerType.PointerUp;
pointerUp.callback.AddListener ((e) => MyInterfaceMethod (false));
triggerUp.triggers.Add (pointerUp);
}
}*_

* public void MyInterfaceMethod (bool v) { //add parameters and interfaces as needed…*
* IMyInterface[] thingsWithIMyInterface = findYourGameObjectHere.GetComponentsInChildren ();*

* for (int i = 0; i < thingsWithMyInterface.Length; i++)*
_ thingsWithIMyInterface*.MyInterfaceMethod (v);
}
}*

The class is called “LockInputComponent” in my case because I’m using it to stop character functions while using UI. I’m aware that the original post is aged, so hopefully this doesn’t “trigger” anyone. Pun intended. Not sorry.
Intended use is to place this at the top of your UI hierarchy and create interfaces for the things you want effected by it._