Assigning an onClick function at runtime

I’ve been bashing on this for about a day now, and I’m pretty convinced that the last piece is probably obvious, I just can’t figure out what I’m missing. The general idea is that I’ve loaded in an XML file with level data, and I roll through that to create the UI. So far, so good. I’ve created prefabs, given them unique names, and I’m to the point where each of those prefabs updates with the correct text. However, the button for most of the prefabs doesn’t click, except for the second button, which links to the level referenced in the last element of the XML. This is the relevant chunk of JS work.

for (var child : Transform in allChildren) {
// Due to an earlier issue, this step verifies we’re updating the prefab that matches the level file name
if (child.name == file) {
UpdateLevel = child.gameObject;
// Poorly named, but gets all the pieces of the prefab
var allThings = UpdateLevel.GetComponentsInChildren(Transform);

for (var Thing : Transform in allThings) {
if (Thing.name == ‘TitleText’) {
// This one works fine, it sets the title of the level in the UI
LevelTitle = Thing.GetComponent(‘Text’);
LevelTitle.text = level_name;
}
// This is where the drama begins.
if (Thing.name == ‘TitleButton’) {
// This actually detects like it’s supposed to, and finds only the instances of those buttons
var this_button : Button = Thing.GetComponent(‘Button’);
this_button.onClick.AddListener(function(){
// This function mostly just aliases Application.LoadLevel(LevelName);
// But if I were to Debug.Log(LevelName) here, it would always throw the name of the last XML element
LoadThisLevel(file);
});
}
}
}
}

It appears in action that the Thing.GetComponent(‘Button’) is searching for the first button it can find, setting that, and ignoring the context entirely. So, how can I ensure that the button in the prefab gets the onClick event? (In the editor, no click events are assigned to any of the prefab buttons, oddly enough.)

I’m a bit confused by the code. You say Debug.Log(LevelName) always write the name of the last XML element, but where is LevelName set? I see LevelTitle being set. Do you mean that Debug.Log(LevelTitle) always writes the same?

One thing worth checking is your closure handling. A common mistake when adding event handler functions in JavaScript is to forget that the closure of the handler function refers to the variable, not the value. An example:

for (var aLevel : levels) {
  aLevel.button.onClick.AddListener(function() {
     Debug.Log(aLevel.name);
  }
}

This will always print the name of the last level, because the listener function has access to the aLevel variable, not the value of it. So it will print the name of the level that aLevel points to when the function is called, which is the last level that was handled in the for-loop, not the level it pointed to when the listener was added.