How to add Persistent Listener to Button.onClick event in Unity Editor Script

12,957

After spending so much time on this, I came to conclusion that this is a bug. This is a Mono bug.

Here are the references:

Ref 1, Ref 2, Ref 3 and Ref 4

Unity wont fix this anytime soon. They usually don't fix stuff related to Mono since they are already working to upgrade to the latest Mono run-time.

Luckily, there are two other workarounds:

  1. Use AddObjectPersistentListener and UnityAction with generic parameter then pass in the generic to the Delegate.CreateDelegate function.

    MyScript myScriptInstance = FindObjectOfType<MyScript>();
    var go = new GameObject();
    var btn = go.AddComponent<Button>();
    
    var targetinfo = UnityEvent.GetValidMethodInfo(myScriptInstance,
        "OnButtonClick", new Type[] { typeof(GameObject) });
    
    UnityAction<GameObject> action = Delegate.CreateDelegate(typeof(UnityAction<GameObject>), myScriptInstance, targetinfo, false) as UnityAction<GameObject>;
    
    UnityEventTools.AddObjectPersistentListener<GameObject>(btn.onClick, action, go);
    
  2. Don't use Delegate.CreateDelegate at-all. Simply use AddObjectPersistentListener.

    MyScript myScriptInstance = FindObjectOfType<MyScript>();
    var go = new GameObject();
    var btn = go.AddComponent<Button>();
    
    UnityAction<GameObject> action = new UnityAction<GameObject>(myScriptInstance.OnButtonClick);
    UnityEventTools.AddObjectPersistentListener<GameObject>(btn.onClick, action, go);
    

Both of these gives you this:

enter image description here

The second solution does not require finding the function with reflection. You must bind the function before run-time. The first one uses reflection.

You probably need to use the first solution as it is very similar to what you are doing and you can provide the function name as a string variable.

Share:
12,957
Umair M
Author by

Umair M

Flutter | iOS | .NET | Angular | Unity3D | Xamarin

Updated on June 05, 2022

Comments

  • Umair M
    Umair M about 2 years

    I am trying to do a simple thing:

    1. Create a new GameObject
    2. Add a Button component to the GameObject.
    3. Add a persistent Listener to Button's OnClick event.

    The method I am trying to register is in some other script. Here is piece of code that I am trying:

    MyScript myScriptInstance = FindObjectOfType<MyScript>(); 
    var go = new GameObject();
    var btn = go.AddComponent<Button>();
        
    var targetinfo = UnityEvent.GetValidMethodInfo(myScriptInstance,
    "OnButtonClick", new Type[]{typeof(GameObject)});
    
    var action = (UnityAction) Delegate.CreateDelegate(typeof(UnityAction),go, targetinfo, false);
    UnityEventTools.AddPersistentListener(btn.onClick, action);
    

    MyScript.cs looks like this:

    public class MyScript : MonoBehaviour
    {
        public void OnButtonClick(GameObject sender)
        {
            // do some stuff here.
        }
    }
    

    When I run this code, Buttons Onclick listener is empty like this:

    enter image description here

    If I change the line

    var action = (UnityAction) Delegate.CreateDelegate(typeof(UnityAction),
    go, targetinfo, false);
    

    to

    var action = (UnityAction) Delegate.CreateDelegate(typeof(UnityAction),
    go, targetinfo, true);
    

    I get :

    ArgumentException: method argument length mismatch System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, Boolean throwOnBindFailure)

    I followed these instructions but don't know what went wrong here.

    Any kind of help is truly appreciated.

  • Umair M
    Umair M over 7 years
    As I am working on editor script, I preferred second method. Thanks anyway
  • Programmer
    Programmer over 7 years
    I don't think you understood the question. Take a look the image in his question then the image in my answer. OP is trying to register the Button and make it appear the Editor. onClick.AddListener cannot do that.