【问题标题】:A bug that I couldn't find我找不到的错误
【发布时间】:2019-03-02 03:44:51
【问题描述】:

我一直在尝试创建库存系统,但遇到了问题。我有一个名为“项目”的列表,这里是管理要添加到库存中的项目的函数的代码。
我在使用此代码时遇到的错误是,每当我尝试将不存在的物品添加到库存中时,它都能正常工作,但是每当我尝试在库存中再次添加相同的物品时,无论我写的数量如何,它都会增加stackSize 减 1 并且不再添加。我也尝试过使用数组,但它仍然无法正常工作,该项目还发生了一些其他奇怪的事情,但我想先解决这个问题。谢谢!

public void AddToInventory(Item _item, int amount = 1)
{

   foreach(Item item in items)
    {
        if(item.Name == _item.Name)
        {
            item.stackSize += amount;
            FlushList();
            return;
        }
    }

    Item item2 = new Item();
    item2 = _item;
    item2.stackSize = amount;

    items.Add(item2);
}

这是我测试它的方式:

private void Awake()
{
    items = new List<Item>();
    itemsInstantiated = new List<GameObject>();

    AddToInventory(itemDb.GetItem("Material"));
    AddToInventory(itemDb.GetItem("Material"));
    AddToInventory(itemDb.GetItem("Material"));
    AddToInventory(itemDb.GetItem("Material"));
    // Stacksize of materials after this code is 2.
    // Doesn't matter how many times i call the function above.

    AddToInventory(itemDb.GetItem("Water Bottle(Full)"), 21);
    AddToInventory(itemDb.GetItem("Apple"));
    FlushList();
}

FlushList() 方法:

 public void FlushList()
{
    // Visual stuff.
    // There is a more efficent way but it's just a project to pass time so 
    // didn't bother.
    foreach(GameObject go in itemsInstantiated)
    {
        Destroy(go);
    }

    foreach(Item item in items)
    {
        GameObject inst = Instantiate(ItemPrefab, ListView.transform);
        itemsInstantiated.Add(inst);

        inst.transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = item.Name;
        inst.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = item.Description;
        inst.transform.GetChild(2).GetComponent<TextMeshProUGUI>().text = item.stackSize.ToString();

        if(item.isConsumeable)
        {
            inst.transform.GetChild(3).gameObject.SetActive(true);
            inst.transform.GetChild(3).GetComponent<Button>().onClick.AddListener(() => { ConsumeItem(item); });
        }
    }
}

ConsumeItem() 方法:

public void ConsumeItem(Item item)
{
    item.consumeAction?.Invoke();

    PlayerNeeds pn = GetComponent<PlayerNeeds>();
    // Increase methods don't touch the list.
    pn.IncreaseHunger(item.hungerIncrease);
    pn.IncreaseThirst(item.thirstIncrease);
    pn.IncreaseSleepiness(item.thirstIncrease);

    RemoveItem(item);
}

物品数据库: 项目数据库是我存储所有项目的地方。那里的项目只有名称和描述。

public Item GetItem(string itemName, int amount = 1)
{
    // Amount here is used by other functions such as crafting recipes.
    // I feel like this is where the problem is.
    foreach(Item item in AllItems)
    {
        if(item.Name == itemName)
        {
            Item newItem = item;
            newItem.stackSize = amount;
            return newItem;
        }
    }
    print("Item: " + itemName + " could not be found!");
    return null;
}

物品类别:

public class Item
{
  public string Name;
  public string Description;
  public int categoryIndex = 1;
  public bool isConsumeable;

  public int stackSize = 1;

  public int hungerIncrease;
  public int thirstIncrease;
  public int sleepIncrease;
  public Action consumeAction;
}

_item 是物品数据库中的物品,它的堆栈大小始终为 1,我要求输入“物品”,因为如果玩家的库存中不存在该物品,我将添加该物品。

一些例子: * AddToInventory(item A, 3)
结果:我的库存中有 3 件 A 件(效果很好)。

*AddToInventory(item A, 3)
AddToInventory(item A, 3)
结果:4 项 A。

**AddToInventory(item A, 45)
AddToInventory(item A, 57)
结果:46 项 A。

【问题讨论】:

  • 您可能需要添加_item.stackSize 而不是amount。而你的Item item2 = new Item(); 什么也没做……
  • _item 只是项目数据库中的一个项目,它的堆栈大小始终为 1。我也不明白为什么 item2 没有做任何事情。
  • 当您发现该项目已在列表中时,您return 无需再次将其添加到列表中。
  • @JohnnyMopp,为什么我们需要再次添加它?它已经是列表的一部分..
  • @JohnnyMopp 他没有尝试向列表中添加第二个重复项目,他正在更新列表中的项目以显示库存中有多少

标签: c# debugging unity3d


【解决方案1】:

问题在于GetItem 方法,并且与使用引用有关。

GetItem 方法中,您创建了AllItems 列表中项目的“副本”,但实际上,您只是对同一对象进行了新引用。然后为stackSize 分配一个值(在您的示例中,它始终是1 的默认值。

这里的事件顺序如下:

  1. 您从AllItems 列表中获取项目,并使用提供给函数的值(或默认值1)更新其stackSize
  2. 您将此物品添加到库存中
  3. 您从AllItems 列表中获得相同的项目,并再次将stackSize 更新为1。问题出现在这里。由于您复制了对同一对象的引用,因此当您将对象的堆栈大小从原来的值更改回 1 时,清单中的对象也会更新。
  4. 您将项目添加到您的库存中,但这个唯一的对象的堆栈大小刚刚重置为 1,因此每次大小都会增加为 2。

这可以通过删除GetItem 中更改stackSize 的行来解决,因为它似乎没有在您的示例中使用,或者您可以向Item 类添加一个方法来手动将每个字段复制到一个新对象。

复制方法看起来像这样。

public Item CopyItem()
{
    var newItem = new Item();

    newItem.Name = this.Name;
    newItem.Description = this.Description;
    newItem.categoryIndex = this.categoryIndex;
    newItem.isConsumeable = this.isConsumeable;
    newItem.stackSize = this.stackSize;
    newItem.hungerIncrease = this.hungerIncrease;
    newItem.thirstIncrease = this.thirstIncrease;
    newItem.sleepIncrease = this.sleepIncrease;
    newItem.consumeAction = this.consumeAction;

    return newItem;
}

并且会这样调用:

Item newItem = item.CopyItem();

【讨论】:

  • 非常感谢您的帮助。现在我对参考文献有了更多的了解。
【解决方案2】:

首先,通过执行item2 = _item,您将删除刚刚创建的新对象一行。然后,这样做的后果是您列表中的项目不仅是原始项目的副本,而且它们是项目。如果您在此函数中修改一个(通过增加堆栈大小),那么它们将在使用它们的任何地方进行修改。也许这是你想要的,也许不是。

您的函数实际上可以像这样重写,没有添加或删除任何功能:

public void AddToInventory(Item _item, int amount = 1)
{
    if (!items.Contains(_item))
    {
        items.Add(_item);
        _item.stackSize = amount;
    }
    else
    {
        _item.stackSize += amount;
       FlushList(); //I don't know what this function is supposed to be doing.
    }
}

如果这不是你的目标,也许你需要澄清你的问题。

如果您可以展示 Item 类,我们可能会更好地理解您的问题。

【讨论】:

  • 复制粘贴了函数,但还是一样。很可能问题不在AddToInventory() 函数上。但我不明白这样做的后果是您列表中的项目不仅是原始项目的副本,而且它们是项目。 部分。请您进一步解释一下吗?
  • @BerkePiriççi 请发布您的 Item 类的代码。
猜你喜欢
  • 2015-10-28
  • 2013-11-24
  • 2021-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-19
相关资源
最近更新 更多