Not sure why I am unable to do this easily (or even find any information on how to do it) but here goes nothing…
I am looking for a way to call the OnClick() function of a UI button from within another script.
Now before you ask why, read on!
I have a world-space UI and as my cursor is locked to the centre and hidden, the UI is unable to find the cursor or log any of it’s clicks (seems stupid, I know).
My solution to this (if inefficient or there is a better way, please speak up), I have a script that raycasts to the UI, and gets the clicked object.
If this is a button, I want to be able to call the OnClick function of said button.
Is this do-able?
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using UnityEngine.UI;
public class UIWorldDetect : MonoBehaviour
{
private GameObject player;
void Start()
{
player = GameObject.FindGameObjectWithTag("Player").gameObject;
}
void Update()
{
if (Vector3.Distance(transform.position, player.transform.position) <= 1)
{
RaycastWorldUI();
}
}
void RaycastWorldUI()
{
if (Input.GetMouseButtonDown(0))
{
PointerEventData pointerData = new PointerEventData(EventSystem.current);
pointerData.position = Input.mousePosition;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
if (results.Count > 0)
{
//WorldUI is my layer name
if (results[0].gameObject.layer == LayerMask.NameToLayer("World UI"))
{
//string dbg = "Root Element: {0} \n GrandChild Element: {1}";
//Debug.Log(string.Format(dbg, results[results.Count - 1].gameObject.name, results[0].gameObject.name));
Debug.Log("Root Element: "+ results[results.Count-1].gameObject.name);
if(results[results.Count - 1].gameObject.GetComponent<Button>())
{
//results[results.Count - 1].gameObject.GetComponent<Button>().onClick.AddListener();
}
//Debug.Log("GrandChild Element: "+results[0].gameObject.name);
results.Clear();
}
}
}
}
}
I’m thinking coroutine with a wait between hover, click and return to idle (or whatever it’s called)
edit: everything below is more a “how to dig into ui elements to see how they work since they’re open source” braindump
or if you want to dig into what the button is actually doing. Buttons are extensions of “selectable” which handles the states like highlighted/clicked/etc.
In that case, I don’t suppose there is an alternative to using the raycast method I am using… perhaps an alternative to hiding the cursor with Cursor.lockState = CursorLockMode.Locked;?
I went a little off piste there, if you want to emulate the button’s graphical state changes I think you just need to manipulate the button’s sprite state.
That doesn’t work anymore, at least not in Unity 2021.3.x
However, I am adding onClick action myself, the button when instantiated doesn’t have any action linked to it
Any other solution to call the onClick from a button from script? Would be very useful for a double click kind of action… I already registered my actions on my button when I instantiated it, so I’d like to just call whatever the action is…
My script:
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class CampaignSlotGO : MonoBehaviour, IPointerClickHandler
{
public Button button;
public TMP_Text saveName;
// PRIVATE VARS
float _lastClick = 0f;
float _interval = 0.4f;
// PRIVATE VARS
public void OnPointerClick(PointerEventData eventData)
{
if ((_lastClick + _interval) > Time.time)
{
Debug.Log("here");
button.onClick.Invoke();
}
_lastClick = Time.time;
}
}
Trying to raise a button’s click event manually from code just to execute the click action is… really weird. The cleaner solution is just to assign the click event to a method, which you can call any time you want, from anywhere…
private void Awake()
{
myButton.onClick.AddListener(MyButtonClicked); // or assign it from Editor if you want
}
public void MyButtonClicked()
{
// Handle button clicked here...
}
Now just call MyButtonClicked from any script you want: