4

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

我有Item一些属性和变量的类itemCost,比如durability等。

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 来指定我要创建的项目。也很容易保存和加载,只需将项目存储IDPlayerPrefs.

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

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

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

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

loot在类中是Item可变的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 上提供完整的代码清单。但我认为,我粘贴在上面的代码绰绰有余。我希望我不会忘记任何事情。

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

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

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

提前致谢。

4

3 回答 3

4

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

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

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

// 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();

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

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

于 2013-08-09T19:25:53.027 回答
1

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

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

于 2013-08-09T19:56:45.450 回答
0

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

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

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

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

于 2014-10-26T13:51:25.400 回答