C# 了解我的游戏物品和库存系统自定义类的问题

C# 了解我的游戏物品和库存系统自定义类的问题,c#,unity3d,C#,Unity3d,对不起,没有介绍我不能提问。(如果您不确定是否要全部阅读,我仍会尝试问一个问题,问题是如果我尝试更改我的项目属性,它将应用于所有其他相同的项目,如何解决此问题?) 我有类Item,带有一些属性和变量,比如itemCost,耐久性等等 类武器继承物品 我有一个ItemGenerator类,它初始化一个数组中的所有项。它包含如下功能: private static Weapon CreateUniqueWeaponForItemList(int ID, int itemLevel, ..... s

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

我有类
Item
,带有一些属性和变量,比如
itemCost
耐久性
等等

武器
继承
物品

我有一个
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
中即可

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

如果玩家有两个或两个以上相同的物品,问题就从这里开始。如果我试图更改
项目
属性,它们将应用于
项目列表[ID]
,而不是我想要更改的项目

我想我需要把代码粘贴在这里,这样才能清楚。我有库存系统,有
private List_inventory=new List()
Player
类中。我有一个宝箱,可以从
ItemList
中获取物品来创建一些

loot.Add(ItemGenerator.CreateUniqueItem(2));
loot
cost
类中是
Item
变量。下面是对
CreateUniqueItem

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

      if (ItemList[ID] != null) {
         return ItemList[ID];
      }
      else {
         return ItemList[0];
      }
   }
创建物品后,玩家可以将其抓取到库存中。我只是
\u库存。添加(项目),例如
项目
项目列表[2]

    Player.Insntance.Inventory.Add(chest.loot[2]);
    chest.loot.RemoveAt(2);
战利品是

public List<Item> loot = new List<Item>();
武器等级相同,但有额外的变量,如伤害

public class Weapon : Item
{
    private int _maxDamage;

    public Weapon() {
        _maxDamage = 0;
    }

    public int MaxDamage {
       get { return _maxDamage; }
       set { _maxDamage = value; }
    }
}
如有必要,我可以在GitHub上提供完整的代码列表。但我想,我贴在上面的代码太多了。我希望我没有忘记任何事情

如果这个问题微不足道,我不会感到惊讶,但出于某种原因,我无法理解它,它会引起头痛

如果文本太多,我很抱歉,但我不能缩短

也为我糟糕的英语拼写感到抱歉


提前感谢。

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

将您的剑类物品阵列想象为“剑的概念”,每当胸前有剑时,我们都会“克隆”该模板。箱子现在包含模板的副本。显然,当玩家拿起剑时,我们只是从一个容器转移到另一个容器(我们不会把剑留在箱子里,而是转移到玩家的库存中)

那么,我们如何才能做到这一点呢?我们可以用克隆技术。因此,您的项目基类可以如下所示:

// 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 newItem = itemList[index].Clone();
这将有效地创建项目的新实例。食物将被正确地克隆。因此,如果玩家有一把克隆剑,现在可以克隆它,并增加它的伤害-因为它是一把不同的剑(但基于柏拉图的原始剑!)


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

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


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

考虑只使用一个item类。通过使用枚举切换项目类型,我认为这样更灵活。 我正在使用类似以下代码的东西,因为使用继承是一件混乱的事情。。。苏欧^^

嗯。。。此示例适用于unity用户;)

使用UnityEngine;
使用System.Collections.Generic;
使用系统集合;
公共类玩家:单一行为
{
公共列表项=新列表();
void Start()
{
Test();
AddItem(新的ItemGenerator().GetRandomItem());
附加项(新项目生成器(项目类型.武器,“新酷武器项目”,UnityEngine.Random.Range(1100),UnityEngine.Random.Range(1100));
项目=新项目();
item.ItemType=ItemType.Quest;
增编(项目);
附加项(新项());
AddItem(新的ItemGenerator().GetRandomItem(“随机测试项目1 2 3”));
AddItem(item.Clone());
}
公共无效附加项(项目)
{
项目。添加(项目);
}
无效测试()
{
Log(“测试开始”);
//示例灵活性
项目=新项目();
item.ItemType=ItemType.Answare;
if(item.ItemType)==
Item newItem = itemList[index].Clone();
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;
    }
}