Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

problem with onClick.AddListener

Discussion in 'Scripting' started by baptisteL, Nov 3, 2016.

  1. baptisteL

    baptisteL

    Joined:
    Mar 12, 2015
    Posts:
    2
    I have a function where I instantiate buttons and add them a method that is supposed to execute on click.

    All this works fine, but when I click for the first time on a button, if I don't click somewhere else after that, every time I will instantiate new buttons will also apply my OnClick method... (which is not what I'm trying to do)

    Here is a sample of my code, if you have an idea about what I'm doing wrong here ?

    Code (CSharp):
    1. if(Input.GetKeyDown(KeyCode.Return))
    2. {
    3.             GameObject myGO = Instantiate(myExempleButton);
    4.             Button myButton = myGO.GetComponent<Button>();
    5.             myButton.onClick.AddListener(delegate {OnClickMethod();});
    6. }
    7.  

    Oh, and also, could you tell me what's the difference between those 2 lines ? In my code it works exactly the same way but maybe it's not always the case ?
    Code (CSharp):
    1.  
    2.             myButton.onClick.AddListener(delegate {OnClickMethod();});
    3.             myButton.onClick.AddListener(() => OnClickMethod ());
    4.  
    Thanks all for your help !
     
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I'm not sure exactly what this means. Could you post the OnClickMethod code and describe exactly what happens and what you expect to happen?

    The second part is understanding what the compilers sees:
    Lets assume you have this function:
    Code (CSharp):
    1. bool IsValid()
    2. {
    3.          return true;
    4. }
    When you type
    Code (CSharp):
    1. IsValid()
    2. // or
    3. bool SomeBool = IsValid();
    The compiler sees IsValid() as a bool since thats its return type.
    If you type this:
    Code (CSharp):
    1. button.onClick.AddListener(IsValid());
    The compiler sees you but a bool in there will complain. What it wants is a reference to that method. Which when you type IsValid without the () is the reference to that method. You can can get that reference in two ways:
    Code (CSharp):
    1. button.onClick.AddListener(IsValid);
    2. button.onClick.AddListener(delegate { IsValid();});
    delegate is a keyword to let the compiler now the following method so it returns a reference to that method.

    Now the () => is s lambda expression It basically is a way to create a method on the fly. It returns a delegate as its return value:

    So if you could do this:
    Code (CSharp):
    1. // This is now a type like a int or a float
    2. delegate bool MyDel(int x, int y);
    3.  
    4.     void Start() {
    5.        
    6.         int a = 5;
    7.         int b = 6;
    8.         MyDel SomeCheck = (x, y) => { if (x < y) return true; else return false; }
    9.         if (SomeCheck(a,b))
    10.             // do stuff
    11. }
    But this also returns a delegate:
    Code (CSharp):
    1. () => OnClickMethod();
    Which is why it works the same as explictly using the delegate keyword.
     
    Last edited: Nov 3, 2016
  3. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I think that's unrelated to your code; if you have a button focused and you press Enter, it clicks the button. If you want to stop that, you need to go to the Input settings for your project and change the "Submit" input to something other than Enter. Or it might be better to change your own code to not use Enter as the button-spawning key.

    Not really a big difference, though both ways make it harder to remove the listener later if you needed to. The most correct way to do it, and also the shortest, is like this:
    Code (csharp):
    1. myButton.onClick.AddListener(OnClickMethod);
     
    baptisteL and takatok like this.
  4. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Good Catch on the fact he's using enter to Instantiate new things and also the default button press.
    Another easy thing to do is Change your Instantiate code to be a different KeyPress than Enter
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Really depends on what you are doing. For me I seldom reuse buttons, so removing listeners isn't a big deal.

    I like the lambda syntax because it means I don't have to define a new method for every variation on a button. Lambda also lets you 'bake' variables into the function call.

    Of course the OP is not doing any of that. So using the method directly works just as well.
     
  6. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Yeah, it's just in this case the method is already there and the OP didn't seem aware that there was a shorter way to do it which also has the added benefit of letting you unsubscribe from the event. Lambdas are certainly useful when you need them.
     
    Kiwasi likes this.
  7. baptisteL

    baptisteL

    Joined:
    Mar 12, 2015
    Posts:
    2
    Sorry for answering late, I got a little problem on my project (lost the latest version :O ), but makeshiftwings found the reason of my previous problem, with the "return" input that is the default submit input !
    Thanks for that, I could have struggled with it for quite some time !

    Now I'll go watch some tuto about delegate cause I don't know enough about it to use it properly but it seems very powerful ! :)
    Thanks to all of you for your answers && have a nice day !