很抱歉,但如果没有介绍,我就无法提问。 (如果你不确定是否要全部阅读,我仍然会尝试提问,问题是如果我尝试更改我的项目属性,它适用于所有其他相同的项目,如何解决这个问题?)
我的课程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;
}
这样的事情。
此项目列表在游戏开始时初始化。
我有一个包含所有项目的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));
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);
,例如item
是ItemList[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
物品可以让buff增加伤害。
但是不是吗?有什么最好的方法呢?
以下是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上提供完整的代码列表。但是我认为,代码我被粘贴在上面而已。我希望我不会忘记任何事情。
如果这个问题很简单,我不会感到惊讶,但由于某种原因,我无法理解它,这会引起头痛。
对不起,如果文字太多,我就不能缩短。
也很抱歉我的英文拼写错误。
提前致谢。
答案 0 :(得分: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();
这有效地创建的新实例,无论项目是。食物将被妥善克隆。因此,如果玩家拥有克隆剑,现在可以克隆它,并增加它的伤害 - 因为它是不同的剑(但基于柏拉图的原始剑!)。
这不是解决这个问题的唯一方法,当项目具有多种不同类型的属性并且可能存在数百个轻微变化时,继承树会变得非常混乱。在这些情况下,我赞成基于组件的设计,但这有点超出了这个答案的范围。
答案 1 :(得分:1)
如果从 ScriptableObject 派生项目,您可能会发现此问题变得更简单。
ScriptableObject(如GameObject)必须显式实例化,以便在处理实例和处理ScriptableObject资产时可以清楚。另一个好处是你可以创建你的模板资产('+1剑','隐形的斗篷'等)并手动编辑(设置单个项目属性等)并检查它们。此外,还为您完成了管理实例ID和手动序列化等所有无聊的工作。
答案 2 :(得分:0)
考虑只使用一个项目类。使用枚举切换项目类型,在我看来它更灵活。 我使用类似下面的代码,因为使用继承是一件混乱的事情...... sooo ^^
嗯...这个例子是针对团结用户的;)
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;
}
}
最后,这意味着每个项目都可以更改为完全不同的项目,并且您可以完全控制所有变量。