Basic conception of inventory and pickup system for rpg-based project.

Greetings everyone, im noob at scripting and you are free to beat some good knowledge at my head if i get something wrong. I have a pair of questions regarding title - first one is how do you "make" an item for "use" in such systems? Do you make it as prefab or as part of database List<RPG_Item> (i have only vague understanding how this one works)? Second - how do you implement inventory? Lets make it absolute simple - does a script at “character” containing List<RPG_Item>, pick up as coping RPG_Item into List from target item and destroying its Gameobject is ok to do ( i actually doubt about last one - what will happen if player picks up an item while its targeted by some effect?)?
Some specifics about what i try to learn to make and may help in decisions:
Every RPG_Item have “hp” value, can be “buffed” and can be destroyed (and so each item hp is tracked for a whole “game”).
Combat is turn based with some “reaction” interrupts.

Only you can beat knowledge into your own head, and the only effective way to do this is to actually do the work in Unity and develop a feel for how these things are done.

There are lots of tutorials out there to take you from zero all the way though completion of an RPG. Hie thee to Google and type!

When you reach a point where something doesn’t make sense scripting wise, that is when you come here and post the snippet of code, say “I don’t understand why it does this” and that will leverage this forum to the fullest, while giving you the best chance of understanding what you need to understand to make the game yourself.

Well, thats why i came here - "What is good way to do this?" I made 2 examples how i see it can be done, but im sure they are somewhat different and each has it`s + and -.

There are a lot of ways to do this, and it depends greatly on your scope, requirements and goals for the system.

Have you looked at any other systems as examples?

1: Generally you will have a base clas that you can overload the functions of to get different use cases working. E.g

Class BaseItem
{
Public bool hasUse;

Public virtual void Use()
{
}
}

Class HealthPotion : BaseItem
{
HealthPotion()
{
hasUse = true;
}

Public override void Use()
{
//heal me here
}
}

You will have more to your clases but for use something like that will work.

2: You should definatly look at some tutorials to get a clear idea for creating an inventory properly. The main thing to remember though is the physical object is just a mesh the actual item is just a gameobject with a mesh, the items data will be separate stored in a class to link the two together e.g.

Class EquipPair
{
GameObject itemMesh;
BaseItem itemData;
}

This way deleting a physical version wont wipe the item away.

1 Like

There are many ways to do it. Either way, you probably want to split your “3D world object” from your “inventory/database object” as mentioned above.

To pick up an item, the 3d world objects normally just point to a dictionary entry, then the dictionary item gets copied to your inventory as some kind of InventoryItem, and the 3d object is disabled/destroyed.

Although, you could have actual 3D world items double as your actual inventory items, by parenting them to a character and making them invisible. It’s truer to real life, but not usually done.

2 Likes

Thats the exact point of my interest in first question - how do you "unite" mesh and items data so when you “spawn” object (lets say you drop it from your inventory) the item that appears will visually be this item and will contain all items data?

I would probably use a ScriptableObject for this. ScriptableObjects are Unity’s notion of a generic “data bag.”

If you made a SO for a basic inventory item, it would have such things as:

  • weight
  • prefab when in world (what it looks like in game)
  • icon for in the inventory
  • cost
  • etc.

Then you can make as many of these items as you need and each one is a discrete asset in your project that can be loaded, cloned, varied, etc.

I would NOT bother with inheritance at first, as it will only make your code brittle and unmanageable when you are still learning. Ultimately there may be value in discerning inventory items that are potions, weapons, etc., but it will not be obvious to you how to break this up until you are a fair way into the process of actually implementing it. Then you can just quickly refactor it at that point, if the benefit is high enough.

I was asking myself the same questions a while ago, and am applying the finishing touches to my own inventory system.

Similar to some of the comments above, my inventory is a List . I then have another list that acts as a database for all items in the game, and I can add items to my inventory by looping through the database and grabbing an item (by itemName, ID etc.)

The UI stuff can be as simple or as complex as you want, depending on how you want the user to interact with items (i.e drag and drop, double click to Use (), stackable items, equipping and unequipping etc.)

I am still fairly new to Unity, and learning some of the things I have coded so far were a steep learning curve. The tutorials you’ll find on YouTube are great, but you’ll find a lot of them drop off after a certain point and never get completed (I think this is due to the vast amount of ways such an inventory can be implemented, but it all boils down to your own game design). I ended up building my own from scratch, albeit with a lot of help from the good people on these forums, and I now have a functioning inventory system (with filter and sort) and a loot system (enemy drops) for adding items to it. If you need any help with yours feel free to drop me a line! Good luck

If you want to see how a huge AAA RPG does it, I’d suggest picking up Skyrim if you don’t already own it, and downloading the creation kit, and then looking at how their system works. In Skyrim, there are item records, which contain all the base data of the item, like name, price, weight, what mesh to use when it’s dropped, what mesh to use when it’s held in first person view, etc. So all iron swords are based off the iron sword record. The item records have ID’s. Then there are item instances, like this specific iron sword in this room that you just found. Each instance has both a record ID that says what it is (an Iron Sword), an instance ID to track which instance it is, and any info that’s specific to only a particular instance (its position, its sharpened level, its particular enchantment, etc). A container or inventory is just a list of Item Record ID’s with an amount. Item Instance ID’s are created on the fly if you take something out of the container or inventory and place it in the world. So you wouldn’t have 20,000 instance id’s for each arrow in your pack, you just have an entry with the ID for iron arrow and the amount 20,000. If you take an arrow out and drop it on the ground, it gets assigned a new instance ID and position/rotation, etc. If you pick it back up, that instance ID goes away and it goes back to just being part of the amount in the container.

2 Likes

I’ve never played Skyrim, but I’m curious to know how they dealt with instance-specific data. Like if you have an item, used it for some time, and it lost sharpness, when you drop the item I’m assuming you’re instantiating a new mesh from the item record, and so if you were to pick it back up again, how would it be able to restore the old sharpness? unless the item records are being cloned.

I mentioned this a bit above, but there are two main pieces of data: The Item Record for “Iron Sword” has all the data that all Iron Swords have in common (mesh, base damage, type of weapon, etc), and then an Item Instance for each individual Iron Sword that contains the ID of the Item Record and all the data that is specific to each individual sword (sharpness, poison, enchantment, etc). So while the sword is in your inventory, it still has all the Item Instance data on it, and when you drop it, it spawns the mesh and attaches the Item Instance data to it and removes it from your inventory. So basically that item instance data sits around as part of the saved game state of whatever location you dropped it in, with the saved position and rotation of the mesh object. With Skyrim in particular, it does a cleanup occassionally where it deletes any saved item instances on floors and things that you haven’t seen in about three in-game days, because otherwise your save data would get really bloated by saving the positions and instance data of all those cheese wheels you knocked off a table on the other side of the world three weeks ago. :smile:

1 Like

Oooh ok, so if I understand correctly, then what is in your inventory is a list of Item Instances with their associated amounts.

Yes, exactly.

aaah ok, Thank you so much. So if I were to do something similar in Unity, would the following be a correct representation?

class ItemRecord : ScriptableObject
{
     int id;
     int maximumStacks;
     string itemName;

    public virtual string GetDescription()
    {
         return "No description";
    }
}

class UsableItemRecord : ItemRecord
{
     List<UsableItemEffect> itemEffects;
 
     public override string GetDescription()
     {
          //return a string of all the descriptions of each effect
     }
}


interface IItemData
{
    public string GetName();
    public int GetMaximumStacks();
    public string GetDescription();
}
[System.Serializable]
class ItemData : IItemData
{
     protected ItemRecord itemRecord;
 
    public string GetName(){ return itemRecord.itemName; }
    public int GetMaximumStacks(){ return itemRecord.maximumStacks; }
    public string GetDescription(){ return itemRecord.GetDescription(); }
}

[System.Serializable]
class UsableItemData : IItemData
{
    UsableItemRecord usableItemRecord;

    public string GetName(){ return usableItemRecord.itemName; }
    public int GetMaximumStacks(){ return usableItemRecord.maximumStacks; }
    public string GetDescription(){ return usableItemRecord.GetDescription();}

}


class Item : MonoBehaviour
{
      protected ItemData itemData;
 
      protected virtual void Start()
      {
            Debug.Log(getName());
      }
     
      protected virtual void getName { return itemData.GetName(); }
}

class UsableItem : Item
{
     protected UsableItemData usableItemData;

     protected override void Start()
     {
          this.itemData = usableItemData;
          base.Start();
     }

    protected override void getName(){ return usableItemData.GetName(); }
}

Yeah that seems fine. Though one thing you might want to consider, and I think Skyrim does this too if I recall, is that instead of having things like UsableItem inherit from Item, keep them as two separate components, like “Item” and “UsableEffects” and then have UsableItem contain an Item component and a UsableEffects component. Kind of like how in Unity you use a bunch of components on a GameObject instead of having them inherit from base classes. Then you can do things like have two different items use the same UsableEffects without having to duplicate all that data.

1 Like

aaah ok, that does make more sense. So if I’m following correctly, would the below be a more accurate representation?

    interface IItemRecord
    {
         GetMaximumStacks();
         GetItemName();
    }

    interface IUsableItemRecord : IItemRecord
    {
        GetItemRecord();
    }

    class ItemRecord : ScriptableObject, IItemRecord
    {
         int id;
         int maximumStacks;
         string itemName;

              public GetMaximumStacks(){ return maximumStacks; }
              public GetItemName(){ return itemName; }

        public string GetDescription()
        {
             return "No description";
        }
    }

    class UsableItemRecord : ScriptableObject, IUsableItemRecord
    {
         ItemRecord itemRecord;
         List<UsableItemEffect> itemEffects;

             public ItemRecord GetItemRecord()
             {
                   return itemRecord;
             }

              public GetMaximumStacks(){ return itemRecord.GetMaximumStacks(); }
              public GetItemName(){ return itemRecord.GetItemName(); }

         public string GetDescription()
         {
              //return a string of all the descriptions of each effect
         }
    }


    interface IItemData
    {
       string GetName();
       int GetMaximumStacks();
       string GetDescription();
    }

    interface IUsableItemData : IItemData
    {
       ItemData GetItemData();
    }

    [System.Serializable]
    class ItemData : IItemData
    {
         ItemRecord itemRecord;

        public string GetName(){ return itemRecord.itemName; }
        public int GetMaximumStacks(){ return itemRecord.maximumStacks; }
        public string GetDescription(){ return itemRecord.GetDescription(); }
    }

    [System.Serializable]
    class UsableItemData : IUsableItemData
    {
        ItemData itemData;

        UsableItemRecord usableItemRecord;

        public ItemData GetItemData(){ return itemData; }

        public string GetName(){ return itemData.GetName(); }
        public int GetMaximumStacks(){ return itemData.GetMaximumStacks(); }
        public string GetDescription(){ return usableItemRecord.GetDescription();}
    }

    interface IItem
    {
 
    }

    interface IUsableItem : IItem
    {
        
    }


    class Item : MonoBehaviour, IItem
    {
         ItemData itemData;

         void Start()
          {
                Debug.Log(itemData.GetName());
          }
    }

    class UsableItem : MonoBehaviour, IUsableItem
    {
         Item item;
         UsableItemData usableItemData;

         void Start()
         {
              this.item.itemData = usableItemData.GetItemData();
              item.Start();
         }
    }

Yeah that’s kind of how I designed mine. I’m sure there are lots of ways to do it though, so really it all comes down to what works for your particular game. :slight_smile:

1 Like

Aaah I see, thanks so much for sharing that, and I’m sure a lot of people can benefit. This is certainly the most solid thing I’ve found thus far from all the tutorials and everything I’ve come across.