Unity中for循环生成带按钮苹果时点击弹窗索引越界问题及解决
Unity 循环生成带按钮苹果时回调索引越界问题解决
问题场景
需要在运行时从列表生成20个附带按钮的苹果,点击按钮弹出对应苹果的信息。但使用for循环实现后,点击按钮会触发ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.错误,弹窗无法正常显示对应信息。
原实现代码
public class AppleInventory : MonoBehaviour { [SerializeField] private ApplesScript applPrefab; [SerializeField] private Transform applParent; public ApplesScript CreateApples() { var appl = Instantiate(applPrefab, applParent); for (int i = 0; i < apples.Count; i++) { appl = Instantiate(applPrefab, applParent); appl.InitAppleVisualization(apples[i].GetAppleSprite()); appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i)); appl.transform.position = new Vector2(apples[i].x, apples[i].y); } return appl; } }
问题验证
移除for循环,指定固定索引值(如i=7)时,代码可正常生成苹果并显示对应弹窗信息,确认问题出在for循环与回调的结合逻辑上。测试代码如下:
public ApplesScript CreateApples() { int i = 7; var appl = Instantiate(applPrefab, applParent); appl.InitAppleVisualization(apples[i].GetAppleSprite()); appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i)); appl.transform.position = new Vector2(apples[i].x, apples[i].y); return appl; }
问题原因
这是C#闭包的变量捕获特性导致的:循环中的i是同一个变量,所有回调都会捕获这个变量的引用。当循环结束后,i的值会变成apples.Count(超出列表索引范围),此时点击按钮触发回调,就会出现索引越界异常。
解决方法
方法一:拆分循环与创建逻辑
将for循环单独抽取到一个方法中,每次循环调用生成单个苹果的方法并传入当前索引。由于方法参数是值类型,每次调用会创建i的副本,回调捕获的是这个独立的副本,避免了索引值被后续循环修改。修改后的代码如下:
public void StarterOfApplesCreation() { for (int i = 0; i < apples.Count; i++) { CreateApples(i); } } public void CreateApples(int i) { var appl = Instantiate(applPrefab, applParent); appl.InitAppleVisualization(apples[i].GetAppleSprite()); appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i)); appl.transform.position = new Vector2(apples[i].x, apples[i].y); }
方法二:循环内创建局部变量保存索引
在循环内部创建局部变量保存当前索引,让回调捕获这个独立的变量副本,同样可以解决问题:
public ApplesScript CreateApples() { var appl = Instantiate(applPrefab, applParent); for (int i = 0; i < apples.Count; i++) { int currentIndex = i; // 创建局部变量保存当前索引 appl = Instantiate(applPrefab, applParent); appl.InitAppleVisualization(apples[currentIndex].GetAppleSprite()); appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(currentIndex)); appl.transform.position = new Vector2(apples[currentIndex].x, apples[currentIndex].y); } return appl; }
内容的提问来源于stack exchange,提问作者Alesf28
相关产品推荐
相关产品推荐

