I am trying to do a simple thing:
- Create a new
GameObject
- Add a
Button
component to the gameObject.
- 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:
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.
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 if these gives you this:
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.
Perhaps i didn't understand your question, but the Button script exposes the onClick event which you can add listeners to (in code).
Here's the code i used:
// Use this for initialization
void Start () {
MyScript myScriptInstance = FindObjectOfType<MyScript> ();
GameObject go = DefaultControls.CreateButton (new DefaultControls.Resources());
var btn = go.GetComponent<Button> ();
btn.onClick.AddListener (myScriptInstance.TestMethod);
}
Here's the code for MyScript.TestMethod:
public void TestMethod ()
{
Debug.Log ("TestMethod");
}
A few things to note:
- You manually create a GameObject and add a Button component to it. UI elements are a bit different since they have a RectTransform and must exist under the Canvas object to be rendered.
- In my example, I create the button in a similar way to how it's being created from the UI menu item itself (see reference here). This will create it successfully, but will not add it under the canvas.
- Last thing - in my case, the TestMethod I defined simply prints a log. When clicking the button, it logged the message, but in the Button's inspector i could not see any onClick event listed. This probably just means that the inspector is not refreshed when adding listeners to it at runtime.