Search Unity

Assigning functions to event triggers at runtime

Discussion in 'UGUI & TextMesh Pro' started by taylank, Aug 21, 2014.

  1. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    Has anyone figured out how to do that in code yet? i.e. I would like to generate a UI element at runtime, and assign a function on another gameobject to its OnClick event. How exactly would I go about that?
     
  2. v21

    v21

    Joined:
    Jun 13, 2009
    Posts:
    9
    Code (CSharp):
    1.  
    2.             var iconT = (Transform)Instantiate(iconPrefab);
    3.             var button = iconT.GetComponentInChildren<Button>();
    4.             button.onClick.AddListener(FunctionToCall);
     
    lukos and Tim-C like this.
  3. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    Great. And how does it work when it's not a button? Say it's a panel and I want to assign a function to its OnDrag?
     
  4. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    You would have to toy around with EventTrigger.delegates, it looks like. (Note, you will have to add UnityEngine.EventSystems to your 'using' directives)
     
  5. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    That's what I've been doing but I was hoping there would be an easier way. In any case, here is how I got it to work, if anyone's looking for the same thing:

    Code (CSharp):
    1.  
    2. EventTrigger trigger = UIElement.GetComponent<EventTrigger>();
    3. EventTrigger.Entry entry = new EventTrigger.Entry();
    4. entry.eventID = EventTriggerType.PointerClick;
    5. entry.callback = new EventTrigger.TriggerEvent();
    6. UnityEngine.Events.UnityAction<BaseEventData> call = new UnityEngine.Events.UnityAction<BaseEventData>(TestFunc);
    7. entry.callback.AddListener(call);
    8. trigger.delegates.Add(entry);
    9.  
    10. //And my TestFunc
    11. public void TestFunc( UnityEngine.EventSystems.BaseEventData baseEvent) {
    12.         Debug.Log("Lo and behold");
    13. }
     
    Last edited: Aug 21, 2014
    Matsyir, Pawciu, Aragami_ and 9 others like this.
  6. arturmandas

    arturmandas

    Joined:
    Sep 29, 2012
    Posts:
    240
    I can't seem to be able to access Button component at all, do you have to add some directive? I want to add methods to onClick event in runtime, having a button already in my scene. My code:

    var buttonConnect = GameObject.Find("Button_Connect").GetComponent<Button>();

    buttonConnectGameObject.onClick.AddListener(DoConnect());
     
  7. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    Do you have 'using UnityEngine.UI'?
     
  8. arturmandas

    arturmandas

    Joined:
    Sep 29, 2012
    Posts:
    240
    StarManta - thank you very much
     
  9. taylank

    taylank

    Joined:
    Nov 3, 2012
    Posts:
    182
    PrimalCoder likes this.
  10. RyuMaster

    RyuMaster

    Joined:
    Sep 13, 2010
    Posts:
    468
    Can anyone confirm, that it still works? I am on b18, using script provided by taylank, but no matter what I do, it doesn't work. My GUI comes from prefab, loaded at runtime, maybe that is the issue. I want to narrow the problem down somehow
     
  11. Qbert34

    Qbert34

    Joined:
    Apr 2, 2015
    Posts:
    8
    thank you taylank!
    you have mitigated my confusion on this issue.
    I can confirm this is working for me today.
    Q
     
  12. apiotuch

    apiotuch

    Joined:
    Oct 8, 2009
    Posts:
    42
    Does anyone know how to do this in javascript? I am using Unity 4.6 and no I can't upgrade to 5.

    Basically this is what I have, and I don't get errors, but my function doesn't fire. The entry does get added at runtime since I see the Select event in the inspector. However, the function doesn't appear in the inspector. I tried the c# code above and it works, but that doesn't appear in the inspector either. I don't care if it appears or not in the inspector, I just want the function to fire.

    Code (JavaScript):
    1. function Start ()
    2. {
    3.      var trigger : EventTrigger = gameObject.GetComponent (EventTrigger);
    4.      var entry : EventTrigger.Entry = EventTrigger.Entry();
    5.      entry.eventID = EventTriggerType.Select;
    6.      entry.callback = EventTrigger.TriggerEvent ();
    7.      var l_callback : UnityEngine.Events.UnityAction.<UnityEventBase> = UnityEngine.Events.UnityAction.<UnityEventBase>(OnSelectOption);
    8.      entry.callback.AddListener(function (){l_callback;});
    9.      trigger.delegates.Add (entry);
    10. }
    11.  
    12. public function OnSelectOption (baseEvent:UnityEventBase)
    13. {
    14.      Debug.Log ("Hello World");
    15. }
    I believe "entry.callback.AddListener(function (){l_callback;});" is not correct and l_callback is not assigned as a listener. So the syntax for this line is what I am having an issue with. I usually code in c#, but due to the project I am working on being all in javascript, it has to be in javascript (e.g) first pass methodology won't work since I need cross communication between scripts and assets.
     
    Last edited: Oct 29, 2015
  13. Draco18s

    Draco18s

    Joined:
    Aug 15, 2011
    Posts:
    110
    I realize this is a bit old, but apiotuch's code is exactly what I was looking for.
    To answer his question, I think this is what he was looking for:

    Code (JavaScript):
    1. entry.callback.AddListener(l_callback);
    l_callback was already of type UnityEngine.Events.UnityAction.<UnityEventBase> which is what the AddListener method was looking for.

    Also, the whole section in C#

    Code (CSharp):
    1. public void Start() {
    2.   EventTrigger trigger = btn.GetComponent<EventTrigger>();
    3.   EventTrigger.Entry entry = new EventTrigger.Entry();
    4.   entry.eventID = EventTriggerType.PointerEnter;
    5.   entry.callback = new EventTrigger.TriggerEvent();
    6.   UnityAction<BaseEventData> l_callback = new UnityAction<BaseEventData>(OnSelectOption);
    7.   entry.callback.AddListener(l_callback);
    8.   trigger.triggers.Add(entry);
    9. }
    10.  
    11. private void OnSelectOption(BaseEventData eventData) {
    12.    Debug.Log("Hello World");
    13. }
    14.  
     
  14. Boerbel

    Boerbel

    Joined:
    Jun 15, 2015
    Posts:
    4
    Is there a way to do something like this?

    Code (CSharp):
    1. public void Start() {
    2.   EventTrigger trigger = btn.GetComponent<EventTrigger>();
    3.   EventTrigger.Entry entry = new EventTrigger.Entry();
    4.   entry.eventID = EventTriggerType.PointerEnter;
    5.   entry.callback = new EventTrigger.TriggerEvent();
    6.   UnityAction<BaseEventData> l_callback = new UnityAction<BaseEventData>(OnSelectOption( ???, "BLA"));
    7.   entry.callback.AddListener(l_callback);
    8.   trigger.triggers.Add(entry);
    9. }
    10. private void OnSelectOption(BaseEventData eventData, string text) {
    11.    Debug.Log(text);
    12. }
     
  15. Omniroth

    Omniroth

    Joined:
    Feb 3, 2016
    Posts:
    2
    Managed to get this working with buttons as follows (where 'button' is just a previously defined UI.Button component):
    Code (CSharp):
    1. private void Start()
    2. {
    3.     button.onClick.AddListener(delegate { OnClick("test"); });
    4. }
    5.  
    6. private void OnClick(string text)
    7. {
    8.     Debug.Log(string.Format("Success: clicked button and returned text: {0}", text));
    9. }

    Or using the above method for OnEntry (entering the button instead of clicking it):
    Code (CSharp):
    1. private void Start()
    2. {
    3.     EventTrigger.Entry onEntry = new EventTrigger.Entry();
    4.     onEntry.eventID = EventTriggerType.PointerEnter;
    5.     onEntry.callback.RemoveAllListeners();
    6.     onEntry.callback.AddListener((eventData) => { OnEntry("test"); });
    7.  
    8.     button.gameObject.AddComponent<EventTrigger>().triggers.Add(onEntry);
    9. }
    10.  
    11. private void OnEntry(string text)
    12. {
    13.     Debug.Log(string.Format("Success: clicked button and returned text: {0}", text));
    14. }
    This all works fine, and hopefully is of help to you.


    For me the problem comes when try and do more than one button, e.g. ('buttons' being an array of previously defined UI.Button components):
    Code (CSharp):
    1. private void Start()
    2. {
    3.     for (int i = 0; i < buttons.Length; i++)
    4.         buttons[i].onClick.AddListener(delegate { OnClick(buttons[i].name); });
    5. }
    6.  
    7. private void OnClick(string text)
    8. {
    9.     Debug.Log(string.Format("Success: clicked button and returned text: {0}", text));
    10. }

    Which gives an error index out of range error on the line:
    Code (CSharp):
    1. buttons[i].onClick.AddListener(delegate { OnClick(buttons[i].name); });

    I suspect this is just me not understanding the proper usage of the line delegate {}. When I was testing this code with indexes (passing the index of the button into OnClick rather than its name), then EVERY button would get an index of 10. There are 10 buttons, so this means every button is getting the final value of i (its value when the for loop checks if i is less than 10 and fails), like i is being passed in as a reference instead of a value. The same thing happens using either method.

    So this seems like a good old PEBCAK error, but don't yet know enough to solve it. :S Any help would be appreciated.
     
  16. Omniroth

    Omniroth

    Joined:
    Feb 3, 2016
    Posts:
    2
    Solution to multiple buttons.

    I don't pretend to fully understand the solution (it involves pointers and referencing and I'd by lying if I said I fully understood - anyone please feel free to explain why this works), but storing the value I wanted in a variable before calling delegate{} solved the problem, e.g.:

    Code (CSharp):
    1. private void Start()
    2. {
    3.     for (int i = 0; i < buttons.Length; i++)
    4.     {
    5.         string name = buttons[i].name;
    6.         buttons[i].onClick.AddListener(delegate { OnClick(name); });
    7.     }
    8. }
    9.  
    10. private void OnClick(string text)
    11. {
    12.     Debug.Log(string.Format("Success: clicked button and returned text: {0}", text));
    13. }

    Or for indexes:

    Code (CSharp):
    1. private void Start()
    2. {
    3.     for (int i = 0; i < buttons.Length; i++)
    4.     {
    5.         int index = i;
    6.         buttons[i].onClick.AddListener(delegate { OnClick(index); });
    7.     }
    8. }
    9.  
    10. private void OnClick(int index)
    11. {
    12.     Debug.Log(string.Format("Success: clicked button and returned text: {0}", index.ToString()));
    13. }

    Or for OnEntry:

    Code (CSharp):
    1. private void Start()
    2. {
    3.     for (int i = 0; i < buttons.Length; i++)
    4.     {
    5.         EventTrigger.Entry onEntry = new EventTrigger.Entry();
    6.         onEntry.eventID = EventTriggerType.PointerEnter;
    7.         onEntry.callback.RemoveAllListeners();
    8.         string name  = buttons[i].name;
    9.         int    index = i;
    10.         onEntry.callback.AddListener((eventData) => { OnEntry(name, index); });
    11.         buttons[i].gameObject.AddComponent<EventTrigger>().triggers.Add(onEntry);
    12.     }
    13. }
    14. private void OnEntry(string name, int index)
    15. {
    16.     Debug.Log(string.Format("Success: clicked button {0} with name: {1}", index, name));
    17. }
     
  17. bachirk

    bachirk

    Joined:
    Mar 9, 2014
    Posts:
    16
    @Omniroth That's because for delegates, anonymous methods (C# 2.0), and lambda expressions (C# 3.0):
    So when you do:
    Code (CSharp):
    1. int    index = i;
    2. onEntry.callback.AddListener((eventData) => { OnEntry(name, index); });
    3.  
    The reference to the outer variable 'i' is captured when the delegate is created. Unlike local variables, the lifetime of a captured variable extends until the delegates that reference the anonymous methods are eligible for garbage collection.

    You can find more info here: https://msdn.microsoft.com/en-us/library/0yw3tz5k.aspx#Anchor_0
     
    Omniroth likes this.