1
\$\begingroup\$

I want to ask about the best method of storing data that represents information's about specified objects.

I want to find a better way to storing somewhere items in my RPG game before problems had occurred, I had big ugly class, that was defining armors, weapons etc.

[System.Serializable]
public class Item
{
    public string   name;
    public int      cost;
    public float    weaponRange;
    public string   imagePath; //starting from Resources
    public ItemType itemType;
    public enum ItemType   { Item, Armor, Weapon };
    public enum WeaponType { None, ShortSecant, LongSecant, Blunt, Prickly, Bow };
    public enum ArmorType  { None, Cuirass, Helmet, Boots };

    public WeaponType   weaponType;
    //public int          damages;
    public CharacterPropertyList damages = CharacterPropertyList.AttackProperties;
    public bool         twoHanded;

    public ArmorType    armorType;
    //public int          armorPoints;
    public CharacterPropertyList armorPoints = CharacterPropertyList.DefenseProperties;

    public int   infoIndex = -1; // Represents item info position in Model Spawner Coordinate array, -1 when item have not model info -> item can't be spawned.

    [XmlIgnore]
    public bool itemExpanded = false; //expanded item in inspector?
}

But I have been able to nicely render it in inspector using PropertyDrawer.. until I had to refactor it...

I add to code some class to have better data management:

[Serializable]
public class CharacterProperty
{
    [SerializeField]
    private string name;
    [SerializeField]
    private float value;

    public float Value { get { return value; } set { this.value = value; } }
    public string Name { get { return name; } }

    public CharacterProperty(string name, float value = 0.0F)
    {
        this.value = value;
        this.name = name;
    }
}

[Serializable]
public class CharacterPropertyList
{
    [SerializeField]
    private List<CharacterProperty> properties = new List<CharacterProperty>();

    public List<CharacterProperty> Properties
    {
        get { return properties; }
    }

    public CharacterPropertyList(params CharacterProperty [] properties)
    {
        this.properties.AddRange(properties);
    }

    public CharacterProperty this[string name]
    {
        get
        {
            return
                properties.Find(p => p.Name == name);
        }}
    }

    public CharacterProperty this[int index]
    {
        get
        {
            return properties[index];
        }

        set
        {
            properties[index] = value;
        }
    }
}

And I cannot Render it correctly so finally I want to remove that big ugly Item class, and find better, more OO solution, without inspector rendering.

So I want to ask if my idea is good:

Let's say that I have a class Item, and that class will have a list that will contain child items of some interface / abstract class and that objects will be representing functionality of that Item, for example:

interface IComponent 
{
 //some general data
};

public class Valuable : IComponent 
{
//override some data
int Value {get;set;}
}

public class DamagesItem : IComponent
{
//damages 
}

public class ProtectionItem : IComponent
{
//protection
}

public class Item
{
public List<IComponent> Components {get; set;} 

public Item(IComponent [] components)
{
Components = new List<IComponent>();
Components.AddRange(components);
}
}

And next I will create those items in code, or via data stored in some XML file, for example:

var sword = new Item(new IComponent[]{ new DamagesItem(), new Valuable()});
\$\endgroup\$
13
  • \$\begingroup\$ I can imagine that many items are very similar in nature but cause different inputs/outputs. You could have 2 type of healing potions with both different graphics, visual effects and healing output. But in reality they are the exact same thing underneath the hood, just controlled differently. You might want to consider this as well. I think if you look for a data-driven approach ( doesn't have to a be a pure one ) it could help you as well. \$\endgroup\$ – Sidar Jul 24 '17 at 15:45
  • \$\begingroup\$ Everything you're describing sounds massively over-engineered to me. I'd just put data about all my items in an XML file, done \$\endgroup\$ – jhocking Jul 24 '17 at 15:46
  • \$\begingroup\$ @jhocking You should elaborate more, you're just describing a database rather than how to go about implementing things. Michael: developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/… \$\endgroup\$ – Sidar Jul 24 '17 at 15:49
  • \$\begingroup\$ Well you're right, that I totally hand-waved the specifics. That's why I posted it as a comment, not an answer. My point was mostly about the over-engineering. \$\endgroup\$ – jhocking Jul 24 '17 at 15:55
  • \$\begingroup\$ Will you ever need more than one DamagesItem, ProtectionItem and Valuable on an item? Also, it seems a bit like you are reinventing Unity's own component system. Maybe you could use the actual system instead? That will of course only make sense if items actually have a physical presence in the game and are not just abstract entries in an inventory list. \$\endgroup\$ – Philipp Jul 24 '17 at 15:59
1
\$\begingroup\$

I have personally used three different methods to solve this exact same problem. All solutions work, and have different trade offs.

Method 1: Inheritance

Simply explained enough: a BaseItem class with a derived class for a number of different related item types. May decide to use one derived class for WeaponItem, or may decide to go all the way to ShortSwordItem and DaggerItem.

Upside: none to speak of. I suppose it is easy to describe the method.

Downside: you wind up with a gazillion little classes and you go crazy when trying to find one. Your solution is brittle and unmaintainable, and you gradually drown in the weight of using OOP.

Method 2: Property Bag

More or less your own solution up until now. A GetProperty and SetProperty method, a Dictionary<TIdentifier,object> collection, and a lot of template methods.

Upside: All item types look exactly the same code-wise, as they are all one class.

Downside: You have moved your problem from the "too many classes" problem of the Inheritance method into the "too many flexible properties" problem.

Method 3: Descriptor/Instance Pairing

This is my current method of choice. I have two classes: ItemDescriptor and ItemInstance.

The ItemDescriptor has all properties common to all classes of item, but as Func<T> and Action<T> values, so you can plug them in. Typically the parameters are ItemInstance, so that you can base values on the instance.

Consider ItemDescriptor to be more or less static methods for all items of a particular class of items.

The ItemInstance has properties common to all instances of items, typically as discreet values. It needs a reference to its ItemDescriptor to make use of the descriptor's operations.

Upside: Two classes only.

Downside: Using a Descriptor/Instance pair looks a lot like using a C API.

Here's an actual code example of what I mean by "C API":

//nextCell is the cell to which the creature is moving, and it contains an item.
//note that nextCell.Item has to be passed to a call that starts with nextCell.Item
nextCell.Item.Descriptor.OnPickUp(creature, nextCell.Item);

This is still not perfect, as ItemInstance winds up with a lot of properties it doesn't need most of the time, as does ItemDescriptor. However, I find it better than too many classes or too flexible of properties.

For the record, my answer is "Method 3". At least it is today. Ask me again tomorrow.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.