UIToolKit's buttons not working

I just got started on using UIToolkit, i managed to create a menu UI just fine, however, when doing basically the same thing for my Pause UI, the buttons won’t work.
I believe the issue is related to the fact that i’m setting the game object that holds the UIDocument to inactive when the game starts, but i’m not sure.

using System;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UIElements;
using Utility;
using Player;
using UnityEngine.Serialization;
using PlayerInputManager = Player.PlayerInputManager;

namespace UI
{
    [RequireComponent(typeof(AudioSource))]
    public class PauseMenu : MonoBehaviour
    {
        private UIDocument _uiDocument;
        
        [SerializeField] private AudioSource _audioSource;
        [SerializeField] private AudioClip _clickResumeSound;
        [SerializeField] private AudioClip _clickSettingsSound;
        [SerializeField] private AudioClip _clickQuitSound;
        [SerializeField] private AudioClip _hoverSound;
        [SerializeField] private AudioClip _openPauseMenuSound;
        
        private Button _resumeButton;
        private Button _settingsButton;
        private Button _quitButton;
        
        private GameObject _player;
        private PlayerInputManager _input;

        private void Start()
        {
            _uiDocument = GetComponentInChildren<UIDocument>();
            _audioSource = GetComponent<AudioSource>();
            
            _player = GameObject.FindGameObjectWithTag("Player");
            _input = _player.GetComponent<PlayerInputManager>();
            
            _resumeButton = _uiDocument.rootVisualElement.Q<Button>("Resume");
            _resumeButton.RegisterCallback<ClickEvent>(OnClickResumeButton);
            _resumeButton.RegisterCallback<PointerEnterEvent>(OnPointerEnter);
            
            _settingsButton = _uiDocument.rootVisualElement.Q<Button>("Settings");
            _settingsButton.RegisterCallback<ClickEvent>(OnClickSettingsButton);
            _settingsButton.RegisterCallback<PointerEnterEvent>(OnPointerEnter);
            
            _quitButton = _uiDocument.rootVisualElement.Q<Button>("Quit");
            _quitButton.RegisterCallback<ClickEvent>(OnClickQuitButton);
            _quitButton.RegisterCallback<PointerEnterEvent>(OnPointerEnter);

            _uiDocument.gameObject.SetActive(false);
        }

        private void OnPointerEnter(PointerEnterEvent evt)
        {
            Debug.Log("Pointer enter");
            AudioHelper.PlayAudio(_audioSource, _hoverSound);
        }

        private void OnClickResumeButton(ClickEvent evt)
        {
            Debug.Log("resume button resumed");
            AudioHelper.PlayAudio(_audioSource, _clickResumeSound);
            //Time.timeScale = 1;
        }
        
        private void OnClickSettingsButton(ClickEvent evt)
        {
            Debug.Log("Click settings button");
            AudioHelper.PlayAudio(_audioSource, _clickSettingsSound);
        }
        
        private void OnClickQuitButton(ClickEvent evt)
        {
            Debug.Log("click quit button");
            AudioHelper.PlayAudio(_audioSource, _clickQuitSound);
            Application.Quit();
        }

        private void Update()
        {
            if (_input.IsPausePressed)
            {
                AudioHelper.PlayAudio(_audioSource, _openPauseMenuSound);
                _uiDocument.gameObject.SetActive(true);
                //Time.timeScale = 0;
            }
        }
    }
}

Disabling and re-enabling a UIDocument component or its game object will cause it to flush its current visual tree, and rebuilt it when re-enabled. This means any registered events will be lost.

If you want to hide and show a simple UI, it’d be better to flip the VisualElement.visibility of the root visual element instead.

Also buttons already have a .clicked callback, which you can subscribe to for clicks, rather than needing to register a ClickEvent callback.

1 Like

The best way to handle this is to run all setup in the OnEnable of a component on the same object as the UI Document. This will ensure the lifecycle will always match. This also allows using the live reload with the UI Builder so that you can immediately see the changes to the UI reflected in the game view.

1 Like