Item creation

I want to create an inventory system that auto sorts items into different tabs depending on the item type (weapons, potions, etc.). But before I can sort the items I must first create them. I have seen several ways of doing this but they all seem like a headache.

Is there a simple way to create items and add different stat effects to them(I will sort out how to apply the effects myself)? If so an example or sample code would be helpful.

This is what I have so far,

public class Item
{
    public enum ItemType
    {
        Sword,
        Bow,
        Staff
    }
    public ItemType itemType;

    public enum Rarity
    {
        Common,
        Uncommon,
        Rare,
        Epic,
        Legendary
    }
    public Rarity rarity;

    public Item(string ID, ItemType itemType, Rarity rarity, int healthMod, int magicMod, int strengthMod, int dexterityMod, int luckMod)
    {

    }
}

bump

This is a pretty big subject and can be broken down in to 3 parts I think

  • Creating the data structures of an item
  • How to create actual data for specific items and read them at run time
  • How to instantiate and keep track of items during the game

I’ll go over 1 in this post:

  1. I would look into implementing a component based system like you were doing for the classes. You have a base Item class that every item in your game inherits from (swords, potions, scrolls , shields). This class contains the basic data that every item should have. You then implement Interfaces with any functionality an item should have. Create derived classes from Item that implement the appropriate Interface like this:
public interface IMeleeWeapon
{
    float AutoAttack(GameObject enemy); // Some Auto Attack function
}

Avoid putting fields in the Interface unless they specifically are needed to be exposed to other classes. Only thing that goes into an interface is something another class needs to see.

Your items could look like this:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum ItemType
{
    sword,
    bow,
    potion,
}

public enum ItemRarity
{
    common,
    uncommon,
    rare,
    epic,
    legendary
}

// this is just a struct to help Initialize a new Item
public struct ItemInit
{
    public string name;
    public ItemType type;
    public ItemRarity rarity;
    public int value;
}

// this is just a struct to help iniitalize a new melee weapon
public struct MeleeInit
{
    public bool twohanded;
    public int numberDice;
    public int typeDice;
    public int flatDamage;
}
// things every Item in your game needs
public class Item
{
    string name;
    ItemType type;
    ItemRarity rarity;
    int value; // money for selling this thing

    public Item(ItemInit data)
    {
        this.name = data.name;
        this.type = data.type;
        this.rarity = data.rarity;
        this.value = data.value;
    }
}

public class MeleeWeapon : Item, IMeleeWeapon
{
    bool twoHanded;

    // if your damage was 3d7+5
    int numberDice;  // 3
    int typeDice;    // 7
    int flatDamage;  // 5

    public MeleeWeapon(MeleeInit meleeData, ItemInit itemData)
        :base (itemData)
    {
        this.twoHanded = meleeData.twohanded;
        this.numberDice = meleeData.numberDice;
        this.typeDice = meleeData.typeDice;
        this.flatDamage = meleeData.flatDamage;

    }
    public float AutoAttack(GameObject enemy)
    {
        float damage = 0;
        for (int i=0;i<numberDice;i++)
            damage += Random.Range(1, (typeDice + 1));
        damage += flatDamage;
        return damage;
    }
}

Then later on say you had code in your player to auto attack it might look like this:

// Somewhere in the Update function
if (timer > autoAttackDelay)
{
        Item item = GetMyWeapon();
        AutoAttack(Item);
}

// then the AutoAttack method
void AutoAttack(Item item)
    {
        IMeleeWeapon  weapon = item as IMeleeWeapon;
        GameObject target = GetMyTarget();
        Debug.Log(weapon.AutoAttack(target));
    }

Notice the code just passes in a generic Item. This is because the item could be ANY class that derived from Item, as long as that class implements the IMeleeWeapon. This way your AutoAttack code doesn’t care or need to know specifically what kind of weapon its getting.

Now lets say you decided to add a new type of weapon that was a mace weapon. This weapon is just like a normal melee weapon but it has a chance to stun the enemy:

// exactly like any normal melee weapon
// but all maces can stun opponents
public class MaceWeapon : MeleeWeapon, IMeleeWeapon
{
    public MaceWeapon(MeleeInit meleeData, ItemInit itemData)
        : base(meleeData, itemData)
    {
    }
    public new float AutoAttack(GameObject enemy)
    {
        // Check if we stun them
        if (Random.Range(0, 100) < 5)
            enemy.SetStatus(StatusEnum.Stun);
        float damage = 0;
        for (int i = 0; i < numberDice; i++)
            damage += Random.Range(1, (typeDice + 1));
        damage += flatDamage;
        return damage;
    }
}

Your existing code doesn’t change at all! The current code to implement an autoattack just cares about IMeleeWeapon Interface. It doesn’t care how a particular item implements it as long as its there.

Now lets talk about 3 Instantiating new items. (i’ll get to 2 )
This can get a little tricky, but if you notice I added a public struct that had all the fields of a particular class in it. This will make creating items easier. To create an item of a specific class… You just have to create an Initialize the struct of its class, and the struct of each parent up the chain. If you look at how I set up the public constructors, they take these structs and call their parents constructors with them as well.

So for example this code could create a new MeleeWeapon:

void Awake()
    {
        // normally you would get all this data from a file
        MeleeInit meleeData = new MeleeInit();
        meleeData.twohanded = false;
        meleeData.numberDice = 3;
        meleeData.typeDice = 7;
        meleeData.flatDamage = 5;

        ItemInit data = new ItemInit();
        data.name = "sword";
        data.type = ItemType.sword;
        data.rarity = ItemRarity.common;
        data.value = 10;

        weapon = new MeleeWeapon(meleeData, data);

A quick note about interfaces I should have mentioned in the last post:
Anytime you can check the validity of an item with the is keyword:

void SomeFunction(Item item)
{
       if (!(item is IMeleeWeapon))
            return; // maybe throw exception

        // you can also check this way
        IMeleeWeapon = item as IMeleeWeapon;
        if (IMeleeWeapon == null)
           // throw a fit

So the hardest part might be creating and storing all these items. However, the BinaryFormatter gives you an easy way to serialize Data of derived classes. You can create a method to write out all your items as Item, and read them back in. However, if an Item is actually a MaceWeapon, it will store all that data. When you read it back in, it will read it back in as a MaceWeapon even if you’ve cast it as Item.

Here is some Write and Read methods. Probably best to make these static in some script somewhere:

    private void WriteItemToFile<T>(string path, T item)
    {
        // create a new formatter instance
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
            new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

        // open a filestream
        using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
        {
            formatter.Serialize(stream, item);
            stream.Close();
        }
  
    }

    private T ReadItemFromFile<T>(string path, ref long position)
    {
        // create a new formatter instance
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter =
            new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();

        // read the item as position back
        T item = default(T);
        using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            if (position < stream.Length)
            {
                stream.Seek(position, SeekOrigin.Begin);
                item = (T)formatter.Deserialize(stream);
                position = stream.Position;
            }
        }
        return item;
    }

Now to actually use these here is a sample code, but not necessarily how you’d want to do this:

    MeleeWeapon weapon;

    void Awake()
    {
        MeleeInit meleeData = new MeleeInit();
        meleeData.twohanded = false;
        meleeData.numberDice = 3;
        meleeData.typeDice = 7;
        meleeData.flatDamage = 5;

        ItemInit data = new ItemInit();
        data.name = "sword";
        data.type = ItemType.sword;
        data.rarity = ItemRarity.common;
        data.value = 10;

        Type type = Type.GetType("MeleeWeapon");

        weapon = new MeleeWeapon(meleeData, data);
        WriteItemToFile<Item>(Path.Combine(Application.dataPath, "ItemList"),weapon);

        long position = 0;
        Item testItem = ReadItemFromFile<Item>(Path.Combine(Application.dataPath, "ItemList"),ref position);

        IMeleeWeapon testWeapon = testItem as IMeleeWeapon;
        if (testItem is MeleeWeapon)
            Debug.Log("its a melee weapon");
        if (testItem is MaceWeapon)
            Debug.Log("its a mace weapon");

        Debug.Log(testWeapon.AutoAttack(gameObject));
    }

Ideally what you would do. Is once you’ve written up all your Interfaces, and made all your classes, for example you might have something like this:

public class Potion: Item, IDrinkable
{
}

You will need to create a separate Item creation program. This is probably easiest done in .net on Visual Studio… but you could use Unity or whatever you like. This program will have all your Interface and Item Class definitions. Then you can create a series of buttons/dropdowns whatever you like to create a new item. And of course InputFields to input the correct data. You’ll have to play around a bit with a few items, to get the hang of how to read in data, and store it, without overwriting data you’ve already done. Once you’ve got that done, you Item File can just be read by your main Game program with the ReadFunction. Then you just read in all the items your game will have. You can sort them into Lists based on class type if you’d like (using if (item is ClassType). Or whatever structure you need to handle all the potential items in the game.

Note: In the Writer Method I listed, it has FileMode.Create. This means it will create a brand new file everytime its called. You’ll probably want to use this sometimes if your just going to dump the full data into the file and want to override changes you’ve made live into the file. But if your just adding new data, you can change it to FileMode.Append. Maybe add a bool flag to the function to switch between types.

In general when working with serialized data, its fairly difficult to seek to a specific item and load just it. (thought its possible). Unless your working with Tens of thousands of items, you item creation program would work easiest just loading every Item you’ve created. Then its easy to create UI elements to search through existing items as well as create new ones. When your done just save the entire thing back out into the file with Create mode

Last note: as you may have started to realize, a good inventory system for an RPG game can be a lot of work if you want to do it right. It will be a major chunk of the time you spend on the game. Once you get the actual items sorted out and how your game will handle them, the original question (sorting them into tabs) will be cinch.

The information you have given me is in no doubt very helpful, but I feel like this is a bit more complex than it has to be for my game specifically. I am creating a turned based game like fire emblem. Stuff like auto attack are not really necessary and I am simply adding any stat buffs from the weapon equipped to the main stat pool (maybe extra effects like stun later on).
EX:

 public int health { get { return currentClass.health + baseHealth + equippedWeapon.healthMod; } }

I would them use the main health (or attack, magic, etc) stat in my calculations.
What I really need is an instance of an item with all the information I need to add stat mods and evaluate weapon types for advantages/disadvantages. What you have given me would work better for faster paced games.

Also do not worry about telling me how to save things. I already have a firm grasp on that topic.

I found the contents of this video to be close to what I want:

I think the Event Component design is hard to wrap your head around at first, but I actually find it makes programming easier not more complex. So I would still definitely use it if I were writing Fire Emblem. That being said, with the classical approach your using, it seems have an array that holds the primary stats of an item, or player the simplest way to go. Then you can expose public properties like you posted above to let other objects have access to the secondary stats. Was there a specific issue you were running into trying to develop this?

I have the scriptable objects working but what would be the best way to go about saving and loading them? This is a similar system to how I am saving and loading all of my characters.
this is what I have so far:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class ItemStorage : MonoBehaviour {

    public static List<BaseItem> allItems = new List<BaseItem>();
    public List<string> allItemID = new List<string>();

    public static ItemStorage itemStorage;

    void Awake()
    {
        if (itemStorage == null)
        {
            itemStorage = this;
            DontDestroyOnLoad(gameObject);
        }
        else if (itemStorage != this)
        {
            Destroy(gameObject);
        }

        LoadItems();
    }

    public void GetAllItems()
    {
        allItemID.Clear();
        foreach(BaseItem item in allItems)
        {
            allItemID.Add(item.ItemID);
        }
    }
    public void SaveItems()
    {
        BinaryFormatter bf = new BinaryFormatter();
        FileStream file = File.Create(Application.persistentDataPath + "/ItemData.dat");

        GetAllItems();
        ItemData data = new ItemData();

        data.items = allItemID;
        bf.Serialize(file, data);
        file.Close();
        Debug.Log("Save Compleated");
        print(Application.persistentDataPath);

    }
    public static void LoadItems()
    {
        if (File.Exists(Application.persistentDataPath + "/ItemData.dat"))
        {
            BinaryFormatter bf = new BinaryFormatter();
            FileStream file = File.Open(Application.persistentDataPath + "/ItemData.dat", FileMode.Open);
            ItemData data = (ItemData)bf.Deserialize(file);
            file.Close();

            foreach(string itemID in data.items)
            {
                BaseItem item = ItemDatabase.GetItem(itemID);
                if (item != null)
                {
                    Debug.Log(string.Format("Item ID: {0}, Item Name: {1}, Item Description: {2}", item.ItemID, item.ItemName, item.ItemDescription));
                    allItems.Add(item);
                }

            }
            Debug.Log("Item Load Successful");
        }
    }
}
[System.Serializable]
public class ItemData
{
    public List<string> items;

}

Well if all your items inherit from a base Item class. You can use the BinaryFormatter to both load and save them regardless of their actual class by saving them all cast as BaseItem. IT will still save all the relevant data, as well as load it. You would just need to store some variable in the base item class that tells you what class it really is… then you can recast it to that.

What do you mean by saving and loading ScriptableObjects? They are already serialized assets…

Sorry that did not come across correctly. What I meant was the instances of the scriptable object when I spawn a new one. What I am currently doing is saving a list of all of the item id’s in the inventory and just spawning new items every time I load them. I can see how this would create a lot of problems when I want to load in an equipped item. The equipped item would not retain information as to who it is equipped to because it is a new instance of the item.

I am not sure if there would be an easy fix to this because I plan to create an inventory of about 7 slots(equipped Items) for each of my characters and a main inventory list(all items/item storage) that has all obtained items regardless as to if they are equipped or not.

I assume your items of the same type are not all identical? Obviously a long sword item would be different than a mace item. But is every long sword the same… or do long swords have a range of stats they might instantiate with. Some are +1 str, but others might be +2 or even +3 str?

If so, you would need to give every instatiated item a unique ID (different than an ID saying its a long sword). This would be a global static counter that started at 0, and went to infinity never resetting. This should be part of the base item class. So when you save all the objects that are currently instantiated in the world, they will also be saved with their unique personal ID. Then you would save your characters, you would save an array of these unique IDs.

So when you load the game back up. Your character loads and sees that equipment[0] = some int ID. (equipment[0] could always be the helmet). You just write a function that parses your list of loaded objects and returns the one whose ID = unique ID you give it. Then requip that item to the characters helmet on game load

An alternate method could be to have all of your items predefined as prefab then simply have seralisable data classes that save the name of the prefab and any additional transforming data relating to those objects. This data could be its location in the world, its health, level, bonuses, whatever. This is quite space efficient as you are only storing the “Differences” compared to what an object starts in. Not suitable in every case but i’m personally use it for a project where the world and the items are all generated at run-time by random values (so not exactly procedural as i’m not using a seed). This allows me to store and re-load all of the items.

@absolute_disgrace Thats a great idea. I’d still combine it with mine as well. The unique ID would be part of the differences from the Prefab. Just because i suspect its easier to have the character have a list of objects that its equipping, than to have every object have a spot that says “Nope i’m not equipped” or “I’m equipped HERE”.

They would be useful for storing items in stashes banks, etc. Since the bank could have a list of unique IDs its should have, and them just grab them from the global list of items that exist in the world on gameLoad

Yeah this is a problem i’m yet to solve too, simply because i’m not there in the project yet. I like your idea too.

Before we can go any further, I still need to finalize how things save. I would first like to note that items will spawn in from the folder holding all of my scriptable objects, so there will not be any stat generation because they are already predefined.

As I said before, currently I am saving all of the inventory items by only saving the unique Item id and spawning them back into the inventory on load via the ID. From what I see, I have 2 options, I could either assign the ID of the character the item is equipped to the item and save the Character ID along with the item ID(probably with the dictionary feature). Or I could find a way to save and load the the same item(similar to how I had to get around saving my characters without including the monobehavior.) instance each time to a blank scriptable object.

I am just spitting ideas though. I am self taught, so I could be sounding like an idiot right now.

Since each item you save already has an ID its saved with, I still think its easier to have the characters have an array of IDs for their equipment. Then you GameLoad logic is like this:

  • Load all the objects in the world
  • Load the characters after.
  • As the characters are loaded they use their equipment array to look up in to the objects list what object htey neeed to equip and your code equips it on them.

Easier to have one list of say 10 IDs attached to the characters, then have 1 ID pointing to a character (or null) on hundreds(thousands?) of items.

Ok, what I am thinking is to have 2 arrays attached to my characters regarding their equipped items. the first would be the array with all of the items, this would be empty every time the character is loaded. The second will be an array consisting of the equipped item ID and will be saved with the rest of the character data. First I will let all of the items load in, and the characters second. During character load, I will have it go through each ID in the array and assign the items again.

Yes, that is exactly what I was trying to get across.