【问题标题】:C# Lambda: Manipulate single item in List<T>C# Lambda:操作 List<T> 中的单个项目
【发布时间】:2020-08-05 16:06:29
【问题描述】:

我在做什么

我正在为我的 Unity 2D RPG 游戏开发一些原型系统(库存、设备……)。我遇到了玩家持有的List&lt;Stat&gt; stats 的问题。

我要做什么

如果玩家捡起一件物品,它会直接装备(目前)。该项目可以包含一个List&lt;StatModifier&gt; modifiers,它被添加到PlayerStats。每当装备一个物品时,就会触发以下委托,这是EquipmentSystem 类(单例)的一部分。

public delegate void OnEquipmentChanged(EquipableItem newItem, EquipableItem oldItem);
public OnEquipmentChanged onEquipmentChanged;

PlayerStats 类在创建时订阅此委托。

// Start is called before the first frame update
void Start()
{
    List<StatModifier> empty = new List<StatModifier>();

    // Initialize Stats
    health = new Stat(100, empty, StatType.HP);
    mana = new Stat(200, empty, StatType.MANA);

    // Add stats to list
    stats = new List<Stat>();
    stats.Add(health);
    stats.Add(mana);

    // Subscribe new Function
    EquipmentSystem.instance.onEquipmentChanged += OnEquipmentChanged; 
}

OnEquipmentChanged()-方法然后通过从旧项目中删除 StatModifiers 并从新项目中添加 StatModifiers 来重新计算玩家的统计数据。这些统计信息存储在PlayerStats 类中的private List&lt;Stat&gt; stats 中。

我的问题是什么

OnEquipmentChanged()-方法被正确调用,但它没有按预期工作。仅当StatTypeStatStatType 相同时,才应添加项目的StatModifiers。所以HP应该只加到HP上,Mana只加到Mana上等等。 StatType 是一个公共枚举顺便说一句。但该功能目前将所有StatModifiers 添加到玩家拥有的所有Stats 中。函数如下:

void OnEquipmentChanged(EquipableItem newItem, EquipableItem oldItem)
{
    if (oldItem != null) { //remove modifiers }
    if (newItem != null)
    {
        // add modifiers of new item if new item exists
        foreach (StatModifier item_sm in newItem.GetStatModifiers())
        {                
            int index = stats.FindIndex(stat => stat.GetStatType() == item_sm.GetStatType());

            Debug.Log("Found stat " + stats[index].GetStatType());

            stats[index].AddModifier(item_sm);
            Debug.Log("Added " + item_sm.GetModifierValue() + " " + (StatType)item_sm.GetStatType() + " to " + stats[index].GetStatType());
        }
    }
}

设置断点显示,在第一次迭代之后,StatModifier 已经被添加到 Stats 中。这是为什么呢?

编辑 1:这是用于将 StatModifier 添加到任何 Stat 的(非常简单的)代码。每个Stat 都有自己的StatModifier 私有列表。

public void AddModifier(StatModifier modifier)
{
    if (modifier != null)
    {
        modifiers.Add(modifier);
    }
}

谢谢!

【问题讨论】:

  • 我强烈建议将玩家基础统计数据保留为您的基础状态并根据需要计算有效统计数据,而不是让您的状态包含调整后的统计数据。如果您将基本统计数据保持为状态,那么有问题的项目只会在装备时影响事物。如果您保留调整后的统计数据,那么有问题的物品会具有永久效果(每次您重新装备它时可能会叠加)。
  • 另外,请不要将单例用于此目的。有时您可能需要另一个库存来存放其他物品,更不用说未经授权的人可能会弄乱您的库存。一般来说,可变的全局状态是不好的。
  • 您是否尝试过在第一次迭代之前设置断点?每次迭代我只看到 1 个索引访问,所以我认为它一定是其他东西弄乱了修饰符。
  • @BenVoigt:您能详细说明一下吗? Stat 的最终值由其基值和存储在其私有列表中的所有 StatModifiers 的总和组成。返回该最终值的方法使用局部变量,因此仅在需要时才进行计算。但为了进行计算,我需要更新我的StatModifiers 列表,对吗?

标签: c# list lambda


【解决方案1】:

你的问题出在这一行:

stats[index].AddModifier(item_sm);

无论你有什么AddModifier 的实现,它都做错了。

虽然整个代码是不必要的复杂和脆弱,为什么你必须用FindIndex 找到代表不同统计数据的相同对象列表?这就是字典的用途。事实上,这就是单独变量的用途,它会让您的代码更加高效。

另一个真正的大问题是你不能像这样装备物品,首先移除你替换的物品的效果,然后添加新物品。考虑一下这里的操作顺序,任何被删除的flat life stat 都会在你其他装备的life% stat 贡献之前被删除。正确的方法是保留每个修饰符的列表,无论其来源如何,并在需要时将其全部检查,以正确的顺序总结正确的内容,然后使用统计数据。

【讨论】:

  • 我已经发布了AddModifier的代码。那里看不出有什么问题,但也许我已经盯着它看太久了。感谢您指出词典。我一直在考虑使用这些。键是StatType,值是Stat。关于你的最后一点:我不会重新计算统计数据,我只是更新StatModifier 的列表,以便在需要时获得最终值。每个Stat 都有一个GetFinalValue(),它接受baseValue 并将所有StatModifiers 添加到它。
【解决方案2】:

我会采取不同的方法,我不认为 lambda 是要走的路。这是伪代码我没有你所有的类代码,所以我不能完全为你编写代码,但我会做这样的事情。

foreach (StatModifier item_sm in newItem.GetStatModifiers())
{           
     foreach (Stat stat in stats) {
          if (item_sm.GetStatType() == stat.statType) {
          // remove previous stat from a previous equipped item
          stat.AddModifier(item_sm);
          }
     }
}

【讨论】:

  • 这是我原来的方式。然而它的结果是一样的。这可能强化了 Blindys 的声明,即我的 AddModifier 做错了什么......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 2011-10-21
  • 2021-10-31
  • 2011-11-30
  • 1970-01-01
  • 2014-06-30
  • 1970-01-01
相关资源
最近更新 更多