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

Bizarre issue with using for loop to add event listeners to a collection of sliders

Discussion in 'Scripting' started by gmfbrown, Dec 19, 2020.

  1. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
    I am trying to create a collection of game objects each of which is a child object of the previous one, and each of which has a set of sliders to control certain features of that specific game object. I have a script to go through each of the game objects, instantiate the sliders, and add an event listener for the OnValueChanged event, which will call an event handler on that specific game object. But, when I run it I am getting an IndexOutOfRange exception.

    Below is the code where I add the sliders to each of the game objects:

    Code (CSharp):
    1.         for (int i = 0; i < parameters.Length; i++)
    2.         {
    3.             // Get cyclopod
    4.             if (i == 0)
    5.             {
    6.                 cyclopods[i] = epicyclopod.transform.Find("Cyclopod(Clone)").gameObject.GetComponent<Cyclopod>();
    7.             }
    8.             else
    9.             {
    10.                 cyclopods[i] = cyclopods[i - 1].transform.Find("Cyclopod(Clone)").gameObject.GetComponent<Cyclopod>();
    11.             }
    12.  
    13.             // Create amplitude slider
    14.             amplitudeSliders[i] = Instantiate(amplitudeSliderPrefab).GetComponent<Slider>();
    15.             amplitudeSliders[i].transform.SetParent(canvas.transform, false);
    16.             amplitudeSliders[i].onValueChanged.AddListener(delegate { cyclopods[i].ChangeAmplitude(amplitudeSliders[i].value); });
    17.         }
    The IndexOutOfRange error occurs whenever I change the value of the slider and it always points to line 16 in the code above as the source of the problem. This is the line where I attached the event listener. I tried some debugging where I replaced that line with

    amplitudeSliders[i].onValueChanged.AddListener(delegate { Debug.Log(i.ToString()); });
    When I do that, it bizarrely says that every single slider is trying to call index paramaters.Length, which is one more then the largest index. Why is it doing that? And how can I get it so that each slider controls the appropriate game object?
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
  3. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,921
    Not an error, but delegate is old C# syntax. They eventually changed it to
    ()=> { stuff }
    . It's the same either way, but the new way makes it easier to see it's a 0-input function, and to add inputs if you need them, and it's the way everyone else always did it.
     
  4. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
    How would the line be reformatted using that new notation. I tried changing it to
    amplitudeSliders[i].onValueChanged.AddListener()=> { cyclopods[index].ChangeAmplitude(amplitudeSliders[i].value); };
    but that gave error messages saying a ; and a } were expected, even though the line has two semi-colons and the same number of open brackets as close brackets.
     
  5. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    You should be able to just do this inside your loop:
    Code (CSharp):
    1. int capturedLoopValue = i;
    2. amplitudeSliders[i].onValueChanged.AddListener(delegate { cyclopods[capturedLoopValue].ChangeAmplitude(amplitudeSliders[capturedLoopValue].value); });
    Whether you use the
    delegate { /* code */ }
    syntax or the
    () => { /* code */}
    syntax won't make a difference to your actual issue.
     
    Last edited: Dec 19, 2020
  6. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    The correct syntax would be:
    Code (CSharp):
    1. AddListener(() => { /*delegate code*/ });
     
  7. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
  8. gmfbrown

    gmfbrown

    Joined:
    Jan 9, 2019
    Posts:
    25
    That produces a new error saying "'UnityAction<float>' does not take 0 arguments".
     
  9. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    In that case, you just need to add the float parameter within the parenthesis:
    Code (CSharp):
    1. AddListener((float someValue) => { /*delegate code*/ });
    You add the corresponding parameters in the delegate function based on what the event is providing when invoked.
    For example, if your event had multiple parameters like so:
    Code (CSharp):
    1. UnityEvent<string, Vector3> someEvent;
    Then the listeners you add must also have the same parameters:
    Code (CSharp):
    1. AddListener((string someString, Vector3 someVector3) => { /*delegate code*/ });