【问题标题】:Understanding the issue with my custom class for Item and inventory system for game了解我的项目自定义类和游戏库存系统的问题
【发布时间】:2013-08-09 19:16:09
【问题描述】:

对不起,我不能在没有介绍的情况下提出问题。 (如果您不确定是否要全部阅读,我仍然尝试提出一个问题,问题是如果我尝试更改我的项目属性,它适用于所有其他相同的项目,如何解决这个问题?)

我有类 Item 具有一些属性和变量,例如 itemCostdurability 等。

Weapon 类继承 Item

我有ItemGenerator 类,它初始化一个数组中的所有项目。它包含以下功能:

private static Weapon CreateUniqueWeaponForItemList(int ID, int itemLevel, 
..... string description) {
   Weapon item = new Weapon();

   item.ID = ID;
   item.ItemLevel = itemLevel;
   ...
   ...
   item.Description = description
   return item;
}

类似的东西。 此项目列表在游戏开始时初始化。 我有包含所有项目的项目数组。

public static Item[] ItemList = new Item[200];

下面是游戏中所有独特物品的列表,由上面的函数创建,:

ItemList[1] = CreateUniqueWeaponForItemList(1, 5, ....., "this is item!");
ItemList[2] = ....

就像那样。 这现在很好用。当我生成项目时,我只使用项目 ID 来指定我要创建的项目。也很容易保存和加载,只需将项目的ID 存储在PlayerPrefs 中。

但是当我开始添加额外的功能(比如物品升级、伤害升级等等)时,我意识到这种架构很糟糕。

如果玩家有两个或更多相同的项目,问题就从这里开始。如果我尝试更改Item 属性,它们将应用于ItemList[ID],而不是我想要的项目。

我认为我需要在此处粘贴代码才能清楚。我有库存系统,有 private List<Item> _inventory = new List<Item>();Player 类中。我有宝箱,可以从ItemList 获取物品来创建一些

loot.Add(ItemGenerator.CreateUniqueItem(2));

lootItem 类中的 Chest 变量。下面是CreateUniqueItem的解释

   public static Item CreateUniqueItem(int ID) {
      if (ID > ItemList.Length) {
         return null;
      }

      if (ItemList[ID] != null) {
         return ItemList[ID];
      }
      else {
         return ItemList[0];
      }
   }

创建物品后,玩家可以将其抓取到库存中。我只是_inventory.add(item);,例如itemItemList[2]

    Player.Insntance.Inventory.Add(chest.loot[2]);
    chest.loot.RemoveAt(2);

chest.loot 是

public List<Item> loot = new List<Item>();

包含所有要抓取的物品。

我认为问题就在这里。

------------------------------------------

所以这是问题本身。 如果我想做升级项目,我会使用

_inventory[0].MaxDamage++

但是玩家库存中所有其他相同物品的 MaxDamage 都会增加,我不明白,为什么?我不使用ItemList[ID].MaxDamage++

我认为我应该将所有创建的独特项目保存在某个文件或类似文件中,并从库存中引用它们,而不是提供库存链接。

或仅将项目 ID 存储在文件中,并添加到项目 int 变量(如 upgradeLevel)并存储它。所以取决于upgradeLevel 的物品可以得到增益伤害。

但这对吗?什么是最好的方法?

------------------------------------------

这里是Item类的简短粘贴:

public class Item  {
private int _ID
private int _itemLevel;
...
...

    public Item(){
       _ID = 0;
       _itemLevel = 0;
       ...
       ...
    }

    public ID{
       get { return _ID; }
       set { _ID = value; }

    public int ItemLevel {
       get { return _itemLevel; }
       set { _itemLevel = value; }
    }
...
...
...
}

武器类别相同,但有额外的变量,如伤害。

public class Weapon : Item
{
    private int _maxDamage;

    public Weapon() {
        _maxDamage = 0;
    }

    public int MaxDamage {
       get { return _maxDamage; }
       set { _maxDamage = value; }
    }
}

如有必要,我可以在 GitHub 上提供完整的代码清单。但我认为,我粘贴在上面的代码绰绰有余。我希望我不会忘记任何事情。

如果问题是微不足道的,我不会感到惊讶,但由于某种原因我无法理解它并导致头痛。

如果文字过多,我很抱歉,但我不能更短。

也很抱歉我的英文拼写不好。

提前致谢。

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    您将引用存储在数组中,因此只有一个项目存在。可以通过多种方式解决这个问题。本质上,您的“项目列表”几乎就像一个模板列表。有点像柏拉图的完美形式理论。每当您将物品带入游戏中时(无论是在箱子中,还是在玩家的库存中),您都想要克隆该物品。

    把你的剑物品阵列想象成“剑的概念”,只要箱子里有一个,我们就会“克隆”那个模板。箱子现在包含模板的副本。很明显,当玩家拿起剑时,我们只是从一个容器转移到另一个容器(我们不会把剑留在箱子里,它会转移到玩家的库存中)。

    那么,我们该怎么做呢?好吧,我们可以使用克隆。因此,您的项目基类可能如下所示:

    // Not tying ourselves just to weapons here...  what about food, clothes, etc..?
    public abstract class Item
    {
        public int ID { get; set; }
        public string Name { get; set; }
    
        // Let's have a copy constructor
        public Item(Item other)
        {
            ID = other.ID;
            Name = other.Name;
        }
    
        // This part is important!
        public abstract Item Clone();
    }
    

    很好。我们有一个项目。它具有一些基本属性。让我们制造一把武器。

    public class Weapon : Item
    {
        public int Damage { get; set; }
    
        // also has a copy constructor
        public Weapon(Weapon other) : base(other) // Item copies it's stuff!
        {
            Damage = other.Damage;
        }
    
        // Need to implement this:
        public override Item Clone() { return new Weapon(this); }
    }
    

    您现在可以推导出一大堆其他东西(食物、衣服、脏杂志、柏拉图著作等)。

    现在,您可以拥有这些Item 实例的数组,并且当您想在游戏中将一个放在箱子中时,您只需执行以下操作:

    Item newItem = itemList[index].Clone();
    

    这有效地创建了一个无论该项目是什么的新实例。食物将被正确克隆。所以,如果玩家有一把克隆剑,现在可以克隆它,并增加它的伤害——因为它是一把不同剑(但基于柏拉图的原剑!)。

    这不是解决此问题的唯一方法,当项目具有多种不同类型的属性并且可能存在数百个细微变化时,继承树可能会变得非常混乱。在这些情况下,我更喜欢基于组件的设计,但这有点超出了这个答案的范围。

    【讨论】:

    • 哦,非常感谢您提供快速和描述性的答案!现在效果很好!对不起,我的名声不允许我给你打分。再次非常感谢
    【解决方案2】:

    如果您从 ScriptableObject 派生项目,您可能会发现这个问题会变得更简单。

    ScriptableObject(如 GameObject)必须显式实例化,以便您在处理实例和处理 ScriptableObject 资产时清楚。另一个好处是您可以创建模板资产(“+1 剑”、“隐形斗篷”等)并手动编辑它们(设置单个项目属性等)并检查它们。此外,管理实例 ID 和手动序列化等所有无聊的事情都已为您完成。

    【讨论】:

    • 感谢您提供有关 ScriptableObject 的信息,直到您提到它,我才知道。
    【解决方案3】:

    考虑只使用一个物品类别。使用枚举切换项目类型,在我看来它更灵活。 我正在使用类似下面的代码,因为使用继承是一件很麻烦的事情......所以^^

    ehm...这个例子是针对统一用户的;)

    using UnityEngine;
    using System.Collections.Generic;
    using System.Collections;
    
    public class Player : MonoBehaviour
    {
        public List<Item> Items = new List<Item>();
    
        void Start()
        {
            Test();
    
            AddItem( new ItemGenerator().GetRandomItem());
            AddItem( new ItemGenerator(ItemType.Weapon, "New Cool Weapon Item", UnityEngine.Random.Range(1,100),  UnityEngine.Random.Range(1,100)));
            Item item = new Item();
            item.ItemType = ItemType.Quest;
            AddItem(item);
            AddItem(new Item());
            AddItem( new ItemGenerator().GetRandomItem("Random Test Item 1 2 3"));
    
            AddItem( item.Clone());
        }
    
        public void AddItem(Item item)
        {
            Items.Add(item);
        }
    
        void Test()
        {
            Debug.Log("Test starts");
            // example flexibility
            Item item = new Item();
    
            item.ItemType = Itemtype.Weapon;
            if(item.ItemType == Itemtype.Weapon)
               Debug.Log(item.WeaponProperties.GetDps(item));
    
    
    
            item.ItemType = Itemtype.Armor;
            if(item.ItemType == Itemtype.Armor)
               Debug.Log(item.ArmorProperties.Defense);
    
    
            switch(item.ItemType)
            {
                case ItemType.Weapon: Debug.Log("Do Weapon Stuff - instantiate at weapon spawnpoint"); break;
                case ItemType.Armor: Debug.Log("Do Armor Stuff - instantiate at spawnpoint"); break;
                default: Debug.Log("what ever..."); break;
            }
    
            // example item generator
            Item item2 = new ItemGenerator(ItemType.Weapon);
            Debug.Log(item2.Name);
    
            Item item3 = new ItemGenerator(ItemType.Armor, "New Armor Item");
            Debug.Log(item3.Name);
    
            item3.ItemType = ItemType.Weapon;
            item3.Name = "Ultra Sword";
            Debug.Log(item3.Name);
    
            Item item4 = new ItemGenerator(ItemType.Weapon, "New Weapon Item", UnityEngine.Random.Range(1,100),  UnityEngine.Random.Range(1,100));
            Debug.Log(item3.Name);
    
            Item item5 = new ItemGenerator().GetRandomItem();
            Debug.Log(item2.Name);
            Debug.Log(item2.ItemType);
    
            // clone item
    
            Item item6 = item5.Clone();
            Debug.Log(item2.Name);
    
            Debug.Log("Test ends");
        }
    }
    
    
    public enum ItemType
    {
        Weapon, Armor, Consumable, Quest, Key //...
    }
    
    public class Item // :ScriptableObject
    {
        public string Name = "FooItem";
        public ItemType Itemtype;
        public Attributes Attributes = new Attributes();
        public WeaponProperties WeaponProperties = new WeaponProperties();
        public ArmorProperties ArmorProperties = new ArmorProperties();
        public Item Clone()
        {
            return (Item)MemberwiseClone();
        }
    }
    
    [System.Serializable]
    public class WeaponProperties
    {
        public int MinimalDamage = 10;
        public int MaximalDamage= 20;
        public float Speed = 1.5f;
        public static float GetDps(Item weapon)
        {
            return Mathf.RoundToInt(((weapon.WeaponProperties.MinimalDamage + weapon.WeaponProperties.MaximalDamage) * 0.5f) / weapon.WeaponProperties.Speed);
        }
    }
    [System.Serializable]
    public class ArmorProperties
    {
        public int Defense = 25;
    }
    [System.Serializable]
    public class Attributes
    {
        public int Strength = 25;
        public int Stamina = 20;
    }
    
    public class ItemGenerator : Item
    {
        public ItemGenerator(ItemType itemType) : this(itemType, "NewNamelessItem")
        {
        }
        public ItemGenerator(ItemType itemType, string name)this(itemType, name, 0,  0)
        {
        }
        public ItemGenerator(ItemType itemType, string name, int strength, int stamina) : this(itemType, name, strength, stamina,  0)
        {
        }
        public ItemGenerator(ItemType itemType, string name, int strength, int stamina, int defense)
        {
            Name = name;
            Attributes.Strength = strength;
            Attributes.Stamina = stamina;
            switch(item.ItemType)
            {
                case ItemType.Weapon: 
                    break;
                case ItemType.Armor: 
                    ArmorProperties.Defense = defense;
                    break;
                default: Debug.Log("what ever..."); break;
            }
        }
    
        public Item GetRandomItem()
        {
            return GetRandomItem( "New Random Item");
        }
    
        public Item GetRandomItem(string name)
        {
            Name = name;
            Attributes.Strength = Random.Range(0,100);
            Attributes.Stamina = Random.Range(0,100);
            var values = Enum.GetValues(typeof(ItemType));
            ItemType = (ItemType)values.GetValue(Random.Range(0, values.Length));
            switch(item.ItemType)
            {
                case ItemType.Weapon: 
                    break;
                case ItemType.Armor: 
                    ArmorProperties.Defense = Random.Range(0,100);
                    break;
                default: Debug.Log("what ever..."); break;
            }
            return this;
        }
    }
    

    最后,这意味着每个项目都可以更改为完全不同的项目,并且您可以完全控制所有变量。

    【讨论】:

      猜你喜欢
      • 2020-07-17
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 1970-01-01
      • 1970-01-01
      • 2020-10-04
      • 1970-01-01
      • 2014-02-23
      相关资源
      最近更新 更多