Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

inventory question

Discussion in 'Scripting' started by gibberingmouther, Oct 10, 2017.

  1. gibberingmouther

    gibberingmouther

    Joined:
    Dec 13, 2016
    Posts:
    259
    so my inventory coding is progressing well. i have an array of type Item.ItemInfo. so an item is an abstract collection of properties (speaking colloquially, not necessarily the programming definitions of those terms). but i need a version of the item that is displayed on the ground (i'm using a generic sack sprite for all my items; the item icons however will be unique). the item's depiction in the inventory is the same as in loot containers or shops, so that's taken care of. one of the properties from ItemInfo holds the item's icon sprite.

    between school and this inventory, my creativity is sapped. does anybody have any ideas how i can make the connection to having a game object that is on the ground? it would be a game object with a sprite ... how do i do what i can't even explain? brain so tired....

    basically trying to wrap my mind around the architecture for the inventory system and how it relates to the rest of my code. you guys can only help so much i know (without me paying you to look at too much code for your brain) but i would be grateful for any suggestions.
     
    Last edited: Oct 10, 2017
  2. MathiasDG

    MathiasDG

    Joined:
    Jul 1, 2014
    Posts:
    114
    You can have a GroundItem class that is a monobehaviour you attach to a gameobject.

    Code (csharp):
    1.  
    2. public class GroundItem : MonoBehaviour {
    3.     public Item item;
    4.     public Texture sprite;
    5.  
    6.     public void OnRenderObject () {
    7.         // Render your sprite here
    8.     }
    9. }
    10.  
    I'm not sure OnRenderObject is the callback you want for this. You can probably make it work easily by also adding a GUITexture or Image component on the same object that renders the texture for you. Then you will just have to pass the texture reference and translate your transform.position into a screen position to draw the sprite.
     
    gibberingmouther likes this.
  3. gibberingmouther

    gibberingmouther

    Joined:
    Dec 13, 2016
    Posts:
    259
    while i'm at it, i think i'm doing a lot wrong. because i'm dumb ... not an easy fix for that lol.

    here's my code. i'm trying to wrap my mind around how i should use the architecture:
    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. namespace CoS
    7. {
    8.     //certain things don't want to store in Item Database?  external file system could be inefficient or a slow down
    9.     public class Item : MonoBehaviour
    10.     {
    11.         heroInventory inv;
    12.         public Transform hero;
    13.         GameObject itemonground;
    14.  
    15.         private void Start()
    16.         {
    17.             inv = new heroInventory();
    18.         }
    19.         //Wrapper class containing old info in a prettier way
    20.         public class ItemInfo
    21.         {
    22.             //Make all of these readonly so you can be sure they won't be changed.
    23.             public readonly string name;
    24.             public readonly string description;
    25.             public readonly double price;
    26.             public int quantity;   //this doesn't belong here?
    27.             public readonly string sprite;
    28.             public readonly string type;        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    29.             public readonly int dr_crushing;    //need to make objects for these, so i don't have so many properties
    30.             public readonly int dr_cutting;
    31.             public readonly int dr_impaling;
    32.             public readonly int pd_crushing;
    33.             public readonly int pd_cutting;
    34.             public readonly int pd_impaling;
    35.             public readonly int thrusting;
    36.             public readonly int swinging;
    37.             public readonly int thrusting_modifier;
    38.             public readonly int swinging_modifier;
    39.  
    40.             public ItemInfo(string name, string description, double price, int quantity, string sprite, string type,
    41.                 int dr_crushing, int dr_cutting, int dr_impaling, int pd_crushing, int pd_cutting, int pd_impaling,
    42.                 int thrusting, int swinging, int thrusting_modifier, int swinging_modifier)
    43.             {
    44.                 this.name = name;
    45.                 this.description = description;
    46.                 this.price = price;
    47.                 this.quantity = quantity;
    48.                 this.sprite = sprite;
    49.                 this.type = type;
    50.                 this.dr_crushing = dr_crushing;
    51.                 this.dr_cutting = dr_cutting;
    52.                 this.dr_impaling = dr_impaling;
    53.                 this.pd_crushing = pd_crushing;
    54.                 this.pd_cutting = pd_cutting;
    55.                 this.pd_impaling = pd_impaling;
    56.                 this.thrusting = thrusting;
    57.                 this.swinging = swinging;
    58.                 this.thrusting_modifier = thrusting_modifier;
    59.                 this.swinging_modifier = swinging_modifier;
    60.             }
    61.         }
    62.  
    63.         //Keep the named IDs, but put them in an enum.
    64.         public enum ItemID
    65.         {
    66.             NOTHING = 0,
    67.             HP_POTION = 1,
    68.             MP_POTION = 2,
    69.             SWORD = 3,
    70.             SHIELD = 4,
    71.         }
    72.  
    73.         //Replacement of item
    74.         public static class ItemDatabase
    75.         {
    76.  
    77.             public static Dictionary<ItemID, ItemInfo> items;
    78.  
    79.             static ItemDatabase()
    80.             {
    81.                 items = new Dictionary<ItemID, ItemInfo>(5);
    82.                 //You probably don't need an explicit representation of nothing? Kept it in.
    83.                 items[ItemID.NOTHING] = new ItemInfo("Nothing", "Nothing", 0, 0, "nothing", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    84.                 items[ItemID.HP_POTION] = new ItemInfo("HP Potion", "Heals HP", 10, 0, "obj_HP_potion", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    85.                 items[ItemID.MP_POTION] = new ItemInfo("MP Potion", "Restores MP", 10, 0, "obj_mp_potion", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    86.                 items[ItemID.SWORD] = new ItemInfo("Sword", "Shard object", 100, 0, "obj_SWORD", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    87.                 items[ItemID.SHIELD] = new ItemInfo("Shield", "Blocks attacks", 50, 0, "obj_SHIELD", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    88.             }
    89.         }
    90.         //doing this wrong.
    91.  
    92.         /**********************
    93.          * Inventory Functions
    94.          * *******************/
    95.          // remove from inventory
    96.         public void Inv_Remove(Item.ItemInfo item, int qnty)
    97.         {
    98.             //DID THIS WRONG!  Need inventory list
    99.             //In the script we will remove an item from the inventory
    100.             var found = -1;
    101.             //We will check if the item exists and take its place
    102.             for (var i = 0; i < 5; i++)
    103.             {
    104.                 //ItemID eyedee1 = (ItemID)i;
    105.                 if (inv.inventory[i].name == item.name) //maybe have a unique id rather than use name?  possibility.
    106.                 {
    107.                     found = i;
    108.                     break;
    109.                 }
    110.             }
    111.  
    112.             if (found != -1)
    113.             {
    114.                 //If we found the item, we remove it
    115.                 inv.inventory[found].quantity -= qnty;//qnty is the amount to remove
    116.                 if (inv.inventory[found].quantity <= 0)
    117.                 {
    118.                     inv.inventory[found] = new Item.ItemInfo("Nothing", "Nothing", 0, 0, "nothing", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    119.                 }  //If there is no more of the item we change the place to an empty place (Nothing item)
    120.             }
    121.         }
    122.  
    123.         //In this script we will add items to the inventory
    124.         public void Inv_Add(Item.ItemInfo item, int qnty)
    125.         {
    126.             var found = -1;
    127.             bool fullflag = false;
    128.  
    129.             for (var i = 0; i < inv.invcapacity; i++)
    130.             {
    131.                 if (inv.inventory[i].name == item.name)
    132.                 {
    133.                     found = i; //We affect to found the place of the item if we find it
    134.                     break;
    135.                 }
    136.             }
    137.  
    138.             //If it doesn't exist
    139.  
    140.             if (found == -1)
    141.             {
    142.                 //Let's check if there is an empty place to add our item
    143.                 for (var j = 0; j < inv.invcapacity; j++)
    144.                 {
    145.                     if (inv.inventory[j].name == "Nothing")
    146.                     {
    147.                         found = j;
    148.                         break;
    149.                     }
    150.                     else if (j == (inv.invcapacity - 1) & found != j)
    151.                     {
    152.                         fullflag = true;
    153.                         break;
    154.                     }
    155.                 }
    156.             }
    157.  
    158.             //should have an "inventory is full" contingency
    159.             if (fullflag)
    160.             {
    161.                 // print this out!  in a game-friendly gui - depends on whether looting or picking up, or buying
    162.                 Debug.Log("inventory is full, cannot pick up object");
    163.             }
    164.  
    165.             //Now let's add our item to the place we have *
    166.             if (found != -1)
    167.             {
    168.                 inv.inventory[found] = item; //Argument 0 is the id of the item
    169.                 inv.inventory[found].quantity += qnty; //Argument 1 is the amount of the item to add
    170.             }
    171.             // need a script for looting, for buying, and for picking up
    172.         }
    173.  
    174.         // Drop an item
    175.         public void Inv_Drop(Item.ItemInfo item)
    176.         {
    177.             // item = item clicked in gui - right click and click drop
    178.             //Instantiate(hero, item);
    179.             //not sure what to use in place of "item"
    180.             Inv_Remove(item, 1);
    181.         }
    182.  
    183.     }
    184. }
    185.  
    specifically, i think i may have done the inv_remove and inv_add scripts wrong. i should be using the item database but i'm not? it seems like there's something wrong, if anybody could point it out for me. thanks MathiasDG btw.
     
  4. MathiasDG

    MathiasDG

    Joined:
    Jul 1, 2014
    Posts:
    114
    It looks like you're getting lost on classes.

    You should have the following class architecture

    Code (csharp):
    1.  
    2. public class Item : ScriptableObject {
    3.     public string name;
    4.     public string description;
    5.     ...
    6. }
    7.  
    8. public class Inventory : MonoBehaviour {
    9.     public List<Item: items;
    10.  
    11.     public void Add (Item item) {
    12.         for (int i = 0; i < items.Count; i++) {
    13.             if (items[i].name == item.name) {
    14.                 items[i].quantity++; // Item is already in inventory. Increase it's quantity by 1.
    15.                 return;
    16.             }
    17.             items.Add(item);
    18.         }
    19.     }
    20.  
    21.     public void Remove (Item item) {
    22.         for (int i = 0; i < items.Count; i++) {
    23.             if (items[i].name == item.name) {
    24.                 items[i].quantity--; // Item is already in inventory. Decrease it's quantity by 1.
    25.                 if (items[i].quantity == 0) {
    26.                     items.RemoveAt(i);
    27.                 }
    28.                 return;
    29.             }
    30.         }
    31.     }
    32. }
    33.  
    34. public class ItemDatabase : ScriptableObject {
    35.     public Dictionary<string, Item> items;
    36. }
    37.  
    Also note that you probably do not want to add database items to the inventory. You want to add copies of them.
     
    gibberingmouther likes this.
  5. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    I just finished answering an inventory related question, so check here for how I generally set things up. I would like to note that the ScriptableObject approach from Mathias above is really only beneficial if item data is all preconstructed in the editor and written out to an asset file. Essentially, if items are something that have randomized stats or any instance data in them, only the portion that exists as static data (the stuff that's the same between all instances of an item) can be saved in the ScriptableObject- you can't write ScriptableObject assets at runtime after all.

    That's fine though- just delete the ScriptableObject references above and it works perfectly fine for instance data- you can save it to XML or JSON files instead of internal assets.

    That's getting a bit complicated though. To answer your specific question, I would make each of the characters have an inventory, each of the areas in the game have an inventory (for "dropped" items) and each container have an inventory, and all of those inventories exist in a static manager class. I don't like having things exist only on GameObjects in the scene hierarchy- it makes things tedious for no reason. In my case, I would generate scene GameObjects for all dropped items based on the area inventory at scene load, making sprite pickups for each and then just destroying them at scene change (or putting them back into a pool) and the only data they would contain is a link to the item within the area's inventory, and displaying a sprite loaded from that item.

    Does that make sense? I may have a somewhat odd way of managing things, but it's pretty solid, and it means the great majority of my systems work in other games and even other game engines without much editing.
     
    gibberingmouther likes this.
  6. gibberingmouther

    gibberingmouther

    Joined:
    Dec 13, 2016
    Posts:
    259
    thank you very much MathiasDG! the only thing is that i used an array of iteminfo type. should i not have done that?
    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. namespace CoS
    8. {
    9.     public class heroInventory : MonoBehaviour
    10.     {
    11.         public Canvas hinventorycanvas;
    12.         public int invcapacity = 5;
    13.         public Item.ItemInfo[] inventory;
    14.  
    15.         public Canvas hinvequippedcanvas;
    16.  
    17.         // Use this for initialization
    18.         void Start()
    19.         {
    20.             hinventorycanvas.enabled = false;
    21.             hinvequippedcanvas.enabled = false;
    22.             Item.ItemInfo[] inventory = new Item.ItemInfo[invcapacity];
    23.         }
    24.  
    25.         // Update is called once per frame
    26.         void Update()
    27.         {
    28.             if (Input.GetKeyDown(KeyCode.I))
    29.             {
    30.                hinvequippedcanvas.enabled = !hinvequippedcanvas.enabled;
    31.             }
    32.  
    33.         }
    34.      
    35.         public void XButton()
    36.         {
    37.             hinventorycanvas.enabled = false;
    38.         }
    39.  
    40.         public void InventoryIconButton()
    41.         {
    42.             hinventorycanvas.enabled = true;
    43.             //ShowInventory();
    44.         }
    45.  
    46.         // got to look at GM to figure out the types for the inventory
    47.         /*void ShowInventory()
    48.         {
    49.             string labeltxt = "";
    50.             string labelimg = "";
    51.             int counter = 0;
    52.             for (int i = 0; i < invcapacity; i++)
    53.             {
    54.                 counter = i + 1;
    55.                 //update icon and text
    56.                 labeltxt = "Text" + counter;
    57.                 labelimg = "Image" + counter;
    58.  
    59.                 Text text = GameObject.Find(labeltxt).GetComponent("Text") as Text;
    60.                 text.text = inventory[i, 1] + " x " + inventory[i, 2];    // Need to fill out the inventory
    61.                 Image image = GameObject.Find(labelimg).GetComponent("Image") as Image;
    62.                 Sprite invicon = Resources.Load<Sprite>(inventory[i, 3]);
    63.                 image.sprite = invicon;
    64.             }
    65.         }*/
    66.     }
    67. }
    68.  
    my brain-neutering meds just kicked in, unfortunately. will drink some coffee and get to looking at this code. hard to be creative when you're on a med that blocks dopamine...kind of uselessly.
     
    Last edited: Oct 10, 2017
  7. MathiasDG

    MathiasDG

    Joined:
    Jul 1, 2014
    Posts:
    114
    Using an array is fine if you're not resizing it, but I'd still prefer a List in this situation because it handles automatic insertion and count increment, as well as automatic resizing if needed.

    I said you were getting lost on classes because it looks like each one of your items is creating a new invetory instance for it on Start.

    Also, like Lysander said, you dont need to use ScriptableObjects. They're nice if you're creating items on the editor. If items are created at runtime, then don't use ScriptableObjects.
     
    gibberingmouther likes this.
  8. gibberingmouther

    gibberingmouther

    Joined:
    Dec 13, 2016
    Posts:
    259
    how would i make a "copy" of a database item?
     
  9. gibberingmouther

    gibberingmouther

    Joined:
    Dec 13, 2016
    Posts:
    259
    if i make item a scriptable object and then create a custom item asset that i can put in the editor assets folder do i really even need an item database?
     
  10. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    If you'll see from Mathias' example, there's actually two ScriptableObject types- the items individually, and the item database. The "item" stores all of the specific data to that item type, while the database stores all of the items in one collection. Both can be saved out as asset files in the editor, and if you're clever, and only need the items within that one database, you can even save the item assets within the item database asset, so it's only technically one file.

    The database itself is not strictly necessary depending on the needs of your game, but it simplifies a lot of things. You can reference the entire database so you don't need to reference the items one by one where you need them, and this can save a lot of time. Depending on your needs, you can even use separate item databases in separate locations, some with more (or different) items than others. This can be used in a multitude of ways, from filters to item generators.

    It's best to look at a ScriptableObject database in this context as a lookup table- a reference sheet. It's something to look up your item in and say "okay, this is the type of item I'm looking at" without making copies of that information in a thousand different places and wasting resources. It contains the methods to find the information you want. So, generally yes, you do need the database- the database may be ALL that you need, if you don't use anything in the way of instance object information. Like early Zelda games, for instance- the only instance data there is just the number you possess, for consumable items, so the static item database is the great majority of the data.
     
    gibberingmouther likes this.