Unity动态按钮列表中如何传递当前点击的按钮并获取其文本?
Unity按钮点击监听器索引越界问题修复
问题说明
在循环中为列表内的按钮添加点击监听器时,触发点击会出现索引越界错误,原因是闭包捕获了循环变量的引用,而非每次循环的变量副本。当点击事件触发时,循环变量已经达到最大值,超出了列表的索引范围。
原因分析
C# 闭包默认捕获循环变量的引用,而非值副本。在你的代码中,i 是循环变量,所有Lambda表达式共享同一个 i 的引用。当循环结束后,i 的值等于 ButtonList.Count,此时点击按钮访问 ButtonList[i] 自然会超出列表的索引边界。
修复方案
方案一:创建局部变量存储当前按钮实例
在循环内部定义局部变量,保存当前迭代的按钮对象,让闭包捕获这个局部变量的副本:
public List<GameObject> ButtonList = new List<GameObject>(); void Start() { for (int i = 0; i < ButtonList.Count; i++) { ButtonList[i].SetActive(true); // 用局部变量保存当前循环的按钮实例 var currentButton = ButtonList[i]; ButtonList[i].GetComponent<Button>().onClick.AddListener(() => GetButtonText(currentButton)); } } public void GetButtonText(GameObject selectedButton) { Debug.Log(selectedButton.GetComponentInChildren<TMP_Text>().text); }
方案二:使用foreach循环(C# 5+)
在C# 5及以后的版本中,foreach 会为每个迭代创建独立的变量副本,闭包捕获的是每个迭代的变量,避免索引越界问题:
public List<GameObject> ButtonList = new List<GameObject>(); void Start() { foreach (var btnObj in ButtonList) { btnObj.SetActive(true); btnObj.GetComponent<Button>().onClick.AddListener(() => GetButtonText(btnObj)); } } public void GetButtonText(GameObject selectedButton) { Debug.Log(selectedButton.GetComponentInChildren<TMP_Text>().text); }
方案三:直接获取点击的按钮实例
无需依赖列表索引,在回调中通过事件系统获取当前点击的按钮对象:
public List<GameObject> ButtonList = new List<GameObject>(); void Start() { for (int i = 0; i < ButtonList.Count; i++) { ButtonList[i].SetActive(true); ButtonList[i].GetComponent<Button>().onClick.AddListener(OnButtonClicked); } } private void OnButtonClicked() { var clickedObj = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject; var buttonText = clickedObj.GetComponentInChildren<TMP_Text>(); Debug.Log(buttonText.text); }
内容的提问来源于stack exchange,提问作者Douglas Lewis
相关产品推荐
相关产品推荐

