You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何递归调用随机战利品表方法以避免物品重复?

问题分析与修复

你的代码陷入无限循环崩溃,核心有三个问题:

  1. 递归触发时机错误:在for循环的else分支直接递归调用SpawnItem(),哪怕当前只是权重没匹配到,也立刻重新roll,完全跳过后续物品遍历;如果所有物品都已在商店列表,会无限递归导致栈溢出。
  2. 权重扣除逻辑位置错误:randomValue -= ShopItemPool[i].dropRarity;放在判断分支之后,未命中当前物品时权重不会被扣除,后续权重判断完全失效。
  3. 存在性检查不可靠:用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 12:48:23