Calling OnClick() from a script?

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();
                }
            }
        }
    }
}

It’s an event, not a method. http://docs.unity3d.com/ScriptReference/UI.Button.ButtonClickedEvent.html

GetComponent<Button>().onClick.Invoke();
8 Likes

@KelsoMRK this works well, wasn’t coming up on IntelliSense for some reason though :expressionless:

Is there a way to get it to act the same way as a button though with the colour tint on hover and click, or is this something I can’t get at?

1 Like

that’s nothing to do with the event.

When you interact with the button there is the code which handles what the button does, and then there is the things the button calls.

You’ll likely have to manipulate the “spriteState” of the button seperately to simulate the graphical changes the button goes through

https://docs.unity3d.com/ScriptReference/UI.Selectable-spriteState.html

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 :smile:

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.

selectable source
https://bitbucket.org/Unity-Technologies/ui/src/0155c39e05ca5d7dcc97d9974256ef83bc122586/UnityEngine.UI/UI/Core/Selectable.cs?at=5.2&fileviewer=file-view-default

around line 300 there is an enum which has the states, you’re looking for something in there that is based off that enum :smile:

edit: which appears to be DoStateTransition (line 253) which has lines like

private SpriteState m_SpriteState;
//...
transitionSprite = m_SpriteState.highlightedSprite;
transitionSprite = m_SpriteState.disabledSprite;

which ultimately calls DoSpriteSwap (line 473)

image.overrideSprite = newSprite; // newSprite being "transitionSprite" as a parameter from the code pasted above
1 Like

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.

Love Man <3

Can you post your script/code, to see how you set it ?

Use button.Select() to select it, thus changing the color.

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;
    }
}

The docs say onClick is deprecated and to use clicked instead - Unity - Scripting API: UIElements.Button.clicked

If that doesn’t work, you could try using HandleEvent or SendEvent and pass in a ClickEvent instance.

Thanks for answering me :slight_smile:

If you get errors on new Unity versions
Change the

using UnityEngine.UIElements;

to

using UnityEngine.UI;

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:

scriptThatContainsButton.MyButtonClicked();