Search Unity

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

Discussion in 'Scripting' started by Mr_Oger, Aug 10, 2017.

  1. Mr_Oger

    Mr_Oger

    Joined:
    May 24, 2017
    Posts:
    9
    Greetings everyone, i`m 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? Let`s 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 it`s Gameobject is ok to do ( i actually doubt about last one - what will happen if player picks up an item while it`s 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.
     
    Last edited: Aug 10, 2017
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    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.
     
  3. Mr_Oger

    Mr_Oger

    Joined:
    May 24, 2017
    Posts:
    9
    Well, that`s why i came here - "What is good way to do this?" I made 2 examples how i see it can be done, but i`m sure they are somewhat different and each has it`s + and -.
     
  4. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,536
    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?
     
  5. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    1: Generally you will have a base clas that you can overload the functions of to get different use cases working. E.g

    Code (CSharp):
    1. Class BaseItem
    2. {
    3. Public bool hasUse;
    4.  
    5. Public virtual void Use()
    6. {
    7. }
    8. }
    9.  
    10. Class HealthPotion : BaseItem
    11. {
    12. HealthPotion()
    13. {
    14. hasUse = true;
    15. }
    16.  
    17. Public override void Use()
    18. {
    19. //heal me here
    20. }
    21. }
    22.  
    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.

    Code (CSharp):
    1. Class EquipPair
    2. {
    3. GameObject itemMesh;
    4. BaseItem itemData;
    5. }
    This way deleting a physical version wont wipe the item away.
     
    Mr_Oger likes this.
  6. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    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.
     
    Mr_Oger and Kurt-Dekker like this.
  7. Mr_Oger

    Mr_Oger

    Joined:
    May 24, 2017
    Posts:
    9
    That`s the exact point of my interest in first question - how do you "unite" mesh and item`s data so when you "spawn" object (let`s say you drop it from your inventory) the item that appears will visually be this item and will contain all item`s data?
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    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.
     
  9. qqqbbb

    qqqbbb

    Joined:
    Jan 13, 2016
    Posts:
    113
  10. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    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 <Item>. 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
     
  11. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    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.
     
    Stardog and Kurt-Dekker like this.
  12. Wasiim

    Wasiim

    Joined:
    May 2, 2015
    Posts:
    228
    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.
     
  13. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    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. :D
     
    Wasiim likes this.
  14. Wasiim

    Wasiim

    Joined:
    May 2, 2015
    Posts:
    228
    Oooh ok, so if I understand correctly, then what is in your inventory is a list of Item Instances with their associated amounts.
     
  15. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Yes, exactly.
     
  16. Wasiim

    Wasiim

    Joined:
    May 2, 2015
    Posts:
    228
    aaah ok, Thank you so much. So if I were to do something similar in Unity, would the following be a correct representation?

    Code (CSharp):
    1.  
    2. class ItemRecord : ScriptableObject
    3. {
    4.      int id;
    5.      int maximumStacks;
    6.      string itemName;
    7.  
    8.     public virtual string GetDescription()
    9.     {
    10.          return "No description";
    11.     }
    12. }
    13.  
    14. class UsableItemRecord : ItemRecord
    15. {
    16.      List<UsableItemEffect> itemEffects;
    17.  
    18.      public override string GetDescription()
    19.      {
    20.           //return a string of all the descriptions of each effect
    21.      }
    22. }
    23.  
    24.  
    25. interface IItemData
    26. {
    27.     public string GetName();
    28.     public int GetMaximumStacks();
    29.     public string GetDescription();
    30. }
    31. [System.Serializable]
    32. class ItemData : IItemData
    33. {
    34.      protected ItemRecord itemRecord;
    35.  
    36.     public string GetName(){ return itemRecord.itemName; }
    37.     public int GetMaximumStacks(){ return itemRecord.maximumStacks; }
    38.     public string GetDescription(){ return itemRecord.GetDescription(); }
    39. }
    40.  
    41. [System.Serializable]
    42. class UsableItemData : IItemData
    43. {
    44.     UsableItemRecord usableItemRecord;
    45.  
    46.     public string GetName(){ return usableItemRecord.itemName; }
    47.     public int GetMaximumStacks(){ return usableItemRecord.maximumStacks; }
    48.     public string GetDescription(){ return usableItemRecord.GetDescription();}
    49.  
    50. }
    51.  
    52.  
    53. class Item : MonoBehaviour
    54. {
    55.       protected ItemData itemData;
    56.  
    57.       protected virtual void Start()
    58.       {
    59.             Debug.Log(getName());
    60.       }
    61.      
    62.       protected virtual void getName { return itemData.GetName(); }
    63. }
    64.  
    65. class UsableItem : Item
    66. {
    67.      protected UsableItemData usableItemData;
    68.  
    69.      protected override void Start()
    70.      {
    71.           this.itemData = usableItemData;
    72.           base.Start();
    73.      }
    74.  
    75.     protected override void getName(){ return usableItemData.GetName(); }
    76. }
    77.  
     
    Last edited: Jan 8, 2021
  17. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    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.
     
    Wasiim likes this.
  18. Wasiim

    Wasiim

    Joined:
    May 2, 2015
    Posts:
    228
    aaah ok, that does make more sense. So if I'm following correctly, would the below be a more accurate representation?

    Code (CSharp):
    1.  
    2.  
    3.     interface IItemRecord
    4.     {
    5.          GetMaximumStacks();
    6.          GetItemName();
    7.     }
    8.  
    9.     interface IUsableItemRecord : IItemRecord
    10.     {
    11.         GetItemRecord();
    12.     }
    13.  
    14.     class ItemRecord : ScriptableObject, IItemRecord
    15.     {
    16.          int id;
    17.          int maximumStacks;
    18.          string itemName;
    19.  
    20.               public GetMaximumStacks(){ return maximumStacks; }
    21.               public GetItemName(){ return itemName; }
    22.  
    23.         public string GetDescription()
    24.         {
    25.              return "No description";
    26.         }
    27.     }
    28.  
    29.     class UsableItemRecord : ScriptableObject, IUsableItemRecord
    30.     {
    31.          ItemRecord itemRecord;
    32.          List<UsableItemEffect> itemEffects;
    33.  
    34.              public ItemRecord GetItemRecord()
    35.              {
    36.                    return itemRecord;
    37.              }
    38.  
    39.               public GetMaximumStacks(){ return itemRecord.GetMaximumStacks(); }
    40.               public GetItemName(){ return itemRecord.GetItemName(); }
    41.  
    42.          public string GetDescription()
    43.          {
    44.               //return a string of all the descriptions of each effect
    45.          }
    46.     }
    47.  
    48.  
    49.     interface IItemData
    50.     {
    51.        string GetName();
    52.        int GetMaximumStacks();
    53.        string GetDescription();
    54.     }
    55.  
    56.     interface IUsableItemData : IItemData
    57.     {
    58.        ItemData GetItemData();
    59.     }
    60.  
    61.     [System.Serializable]
    62.     class ItemData : IItemData
    63.     {
    64.          ItemRecord itemRecord;
    65.  
    66.         public string GetName(){ return itemRecord.itemName; }
    67.         public int GetMaximumStacks(){ return itemRecord.maximumStacks; }
    68.         public string GetDescription(){ return itemRecord.GetDescription(); }
    69.     }
    70.  
    71.     [System.Serializable]
    72.     class UsableItemData : IUsableItemData
    73.     {
    74.         ItemData itemData;
    75.  
    76.         UsableItemRecord usableItemRecord;
    77.  
    78.         public ItemData GetItemData(){ return itemData; }
    79.  
    80.         public string GetName(){ return itemData.GetName(); }
    81.         public int GetMaximumStacks(){ return itemData.GetMaximumStacks(); }
    82.         public string GetDescription(){ return usableItemRecord.GetDescription();}
    83.     }
    84.  
    85.     interface IItem
    86.     {
    87.  
    88.     }
    89.  
    90.     interface IUsableItem : IItem
    91.     {
    92.        
    93.     }
    94.  
    95.  
    96.     class Item : MonoBehaviour, IItem
    97.     {
    98.          ItemData itemData;
    99.  
    100.          void Start()
    101.           {
    102.                 Debug.Log(itemData.GetName());
    103.           }
    104.     }
    105.  
    106.     class UsableItem : MonoBehaviour, IUsableItem
    107.     {
    108.          Item item;
    109.          UsableItemData usableItemData;
    110.  
    111.          void Start()
    112.          {
    113.               this.item.itemData = usableItemData.GetItemData();
    114.               item.Start();
    115.          }
    116.     }
    117.  
     
    Last edited: Jan 8, 2021
  19. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    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. :)
     
    Wasiim likes this.
  20. Wasiim

    Wasiim

    Joined:
    May 2, 2015
    Posts:
    228
    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.
     
  21. Wasiim

    Wasiim

    Joined:
    May 2, 2015
    Posts:
    228
    Sorry to bother you again, but just out of curiosity, how did you design your system? Specifically how you went about attaching the data to the mesh/GameObject, because every mesh expects a different type of data, a sword mesh vs a gun mesh, where the gun mesh would expect a SwordData object, the gun mesh might expect a GunData object. So how do you resolve those things? I was thinking of using Visitor pattern, but not quite sure.
     
    Last edited: Jan 11, 2021