How can I call a function every time the currently selected button changes?
I’m looking at this page:
https://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.html
and it has currentSelectedGameObject but not directly this. There’s also sendNavigationEvents, but I have no idea what that does. Can I subscribe to those navigation events?
There is no global delegate / event to which you could subscribe. The eventsystem executes OnDeselect on the old object if it implements the “IDeselectHandler” interface and OnSelect to the new object if it implements the ISelectHandler interface.
If you want to detect from outside if the selection has changed, just use an “old”-variable and check inside Update if it has changed. So if currentSelectedGameObject is different from your old-variable you know it has changed. Don’t forget to set your “old”-variable to currentSelectedGameObject when that happens.
“sendNavigationEvents” is just a boolean which enables / disables navigation events (cursor keys, enter key to submit, …)
I wrote an inputmanager that fires selection change events and disables input while an InputField has focus:
(ExternalCoroutine is not needed if your InpuManager is a MonoBehaviour)
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class InputManager
{
public static InputManager instance;
public static InputClass controls;
public UnityEvent<GameObject> OnSelectedChange = new FocusChangeEvent();
GameObject selected;
InputField selectedInputField;
bool inputLocked = false;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void init()
{
new InputManager().Start();
}
private void Start()
{
if (instance == null)
{
instance = this;
}
else
{
return;
}
#region setup input
if (controls == null)
{
controls = new InputClass();
}
controls.UI.Enable();
#endregion
ExternalCoroutine.instance.StartChildCoroutine(CheckFocus());
}
private IEnumerator CheckFocus()
{
EventSystem eventSystem = EventSystem.current;
while (true)
{
if (selectedInputField != null)
{
if (selectedInputField.isFocused)
{
if (!inputLocked)
{
inputLocked = true;
controls.UI.Disable();
controls.Player.Disable();
}
}
else
{
if (inputLocked)
{
inputLocked = false;
controls.UI.Enable();
}
}
}
if (selected != eventSystem.currentSelectedGameObject)
{
selected = eventSystem.currentSelectedGameObject;
if (selected != null)
{
selectedInputField = selected.GetComponent<InputField>();
}
OnSelectedChange.Invoke(selected);
}
yield return null;
}
}
class FocusChangeEvent : UnityEvent<GameObject>
{
}
}
From 2018.1 they’ve added a new event to cover this: Unity - Scripting API: Selection.selectionChanged
Just as Bunny83 said there is no such an event. But you can make your own one. Just replace the default input module component in the EventSystem with your own implementation. You can grab the source code of say StandaloneInputModule here and change it whatever you like.
But let’s dig a little bit deeper. Notice there’s a ‘Process’ method which processes events. If you Debug.Log something out of there you’ll see that the method gets called every frame. So your initial solution of tracking currentSelectedGameObject on every Update isn’t much worse.