我找不到的错误

时间:2019-03-01 18:38:36

标签: c# debugging unity3d

我一直在尝试创建库存系统,但是遇到了问题。我有一个名为“ items”的列表,这是管理要添加到库存的项目的函数的代码。
我在此代码中遇到的错误是,每当我尝试将不存在的项目添加到库存时,它都能正常工作,但是每当我尝试再次在库存中添加相同的项目时,无论我写多少,它都会递增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。

2 个答案:

答案 0 :(得分:1)

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

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

这里的事件顺序如下:

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

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

复制方法看起来像这样。

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;
}

答案 1 :(得分:0)

首先,通过执行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类,我们会更好地理解您的问题。