Unity中如何递归调用随机战利品表方法以避免物品重复?
问题分析与修复
你的代码陷入无限循环崩溃,核心有三个问题:
- 递归触发时机错误:在for循环的else分支直接递归调用
SpawnItem(),哪怕当前只是权重没匹配到,也立刻重新roll,完全跳过后续物品遍历;如果所有物品都已在商店列表,会无限递归导致栈溢出。 - 权重扣除逻辑位置错误:
randomValue -= ShopItemPool[i].dropRarity;放在判断分支之后,未命中当前物品时权重不会被扣除,后续权重判断完全失效。 - 存在性检查不可靠:用
ShopItemPool[i].item.ToString()判断存在性,GameObject默认ToString返回格式是"GameObject (物品名称)",重名或实例化后可能导致判断出错,建议直接用自定义的name字段。
修复后的代码
方案一:先过滤可用物品(推荐,避免无效roll)
先筛选出未加入商店的物品,只在可用池中roll取,从根源避免重复roll取,也不会出现无限循环:
public GameObject shopManager; public ShopManager shopManagerScript; [System.Serializable] public class DropItem { public string name; public GameObject item; public int dropRarity; } public List<DropItem> ShopItemPool = new List<DropItem>(); private void Start() { shopManager = GameObject.FindGameObjectWithTag("ShopManager"); shopManagerScript = shopManager.GetComponent<ShopManager>(); SpawnItem(); } void SpawnItem() { // 过滤出未在商店中存在的物品 var availableItems = ShopItemPool.Where(item => !shopManagerScript.shopItems.Contains(item.name)).ToList(); // 无可用物品直接返回,防止无限循环 if (availableItems.Count == 0) { Debug.LogWarning("所有物品已存在于商店,无法生成新物品"); return; } // 计算可用物品总权重 int totalWeight = availableItems.Sum(item => item.dropRarity); int randomValue = Random.Range(0, totalWeight); // 遍历找到命中的物品 foreach (var dropItem in availableItems) { if (randomValue <= dropItem.dropRarity) { Instantiate(dropItem.item, transform.position, Quaternion.identity); shopManagerScript.shopItems.Add(dropItem.name); return; } randomValue -= dropItem.dropRarity; } }
方案二:保留原逻辑但修复错误点
如果想保留原有roll取逻辑,仅修复错误:
public GameObject shopManager; public ShopManager shopManagerScript; [System.Serializable] public class DropItem { public string name; public GameObject item; public int dropRarity; } public List<DropItem> ShopItemPool = new List<DropItem>(); private void Start() { shopManager = GameObject.FindGameObjectWithTag("ShopManager"); shopManagerScript = shopManager.GetComponent<ShopManager>(); SpawnItem(); } void SpawnItem() { // 先检查是否有可用物品,防止无限递归 bool hasAvailable = ShopItemPool.Any(item => !shopManagerScript.shopItems.Contains(item.name)); if (!hasAvailable) { Debug.LogWarning("所有物品已存在于商店,无法生成新物品"); return; } int itemWeight = 0; for (int i = 0; i < ShopItemPool.Count; i++) { itemWeight += ShopItemPool[i].dropRarity; } int randomValue = Random.Range(0, itemWeight); for (int i = 0; i < ShopItemPool.Count; i++) { var currentItem = ShopItemPool[i]; if (randomValue <= currentItem.dropRarity) { // 检查物品是否已存在 if (!shopManagerScript.shopItems.Contains(currentItem.name)) { Instantiate(currentItem.item, transform.position, Quaternion.identity); shopManagerScript.shopItems.Add(currentItem.name); return; } else { // 已存在则重新roll取 SpawnItem(); return; } } // 无论是否命中,都扣除当前物品权重 randomValue -= currentItem.dropRarity; } }
关键修复说明
- 前置可用物品检查:在递归或roll取前判断是否还有未加入商店的物品,从根源避免无限循环。
- 调整权重扣除位置:将权重扣除移到判断分支外,确保每次循环都正确更新随机值,保证roll取逻辑正确。
- 优化存在性判断:改用自定义的
name字段,比GameObject默认ToString更稳定。
内容的提问来源于stack exchange,提问作者charlieyin
相关产品推荐
相关产品推荐

