inventory question

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.

You can have a GroundItem class that is a monobehaviour you attach to a gameobject.

public class GroundItem : MonoBehaviour {
    public Item item;
    public Texture sprite;

    public void OnRenderObject () {
        // Render your sprite here
    }
}

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.

1 Like

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:

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

namespace CoS
{
    //certain things don't want to store in Item Database?  external file system could be inefficient or a slow down
    public class Item : MonoBehaviour
    {
        heroInventory inv;
        public Transform hero;
        GameObject itemonground;

        private void Start()
        {
            inv = new heroInventory();
        }
        //Wrapper class containing old info in a prettier way
        public class ItemInfo
        {
            //Make all of these readonly so you can be sure they won't be changed.
            public readonly string name;
            public readonly string description;
            public readonly double price;
            public int quantity;   //this doesn't belong here?
            public readonly string sprite;
            public readonly string type;        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            public readonly int dr_crushing;    //need to make objects for these, so i don't have so many properties
            public readonly int dr_cutting;
            public readonly int dr_impaling;
            public readonly int pd_crushing;
            public readonly int pd_cutting;
            public readonly int pd_impaling;
            public readonly int thrusting;
            public readonly int swinging;
            public readonly int thrusting_modifier;
            public readonly int swinging_modifier;

            public ItemInfo(string name, string description, double price, int quantity, string sprite, string type,
                int dr_crushing, int dr_cutting, int dr_impaling, int pd_crushing, int pd_cutting, int pd_impaling,
                int thrusting, int swinging, int thrusting_modifier, int swinging_modifier)
            {
                this.name = name;
                this.description = description;
                this.price = price;
                this.quantity = quantity;
                this.sprite = sprite;
                this.type = type;
                this.dr_crushing = dr_crushing;
                this.dr_cutting = dr_cutting;
                this.dr_impaling = dr_impaling;
                this.pd_crushing = pd_crushing;
                this.pd_cutting = pd_cutting;
                this.pd_impaling = pd_impaling;
                this.thrusting = thrusting;
                this.swinging = swinging;
                this.thrusting_modifier = thrusting_modifier;
                this.swinging_modifier = swinging_modifier;
            }
        }

        //Keep the named IDs, but put them in an enum.
        public enum ItemID
        {
            NOTHING = 0,
            HP_POTION = 1,
            MP_POTION = 2,
            SWORD = 3,
            SHIELD = 4,
        }

        //Replacement of item
        public static class ItemDatabase
        {

            public static Dictionary<ItemID, ItemInfo> items;

            static ItemDatabase()
            {
                items = new Dictionary<ItemID, ItemInfo>(5);
                //You probably don't need an explicit representation of nothing? Kept it in.
                items[ItemID.NOTHING] = new ItemInfo("Nothing", "Nothing", 0, 0, "nothing", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
                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);
                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);
                items[ItemID.SWORD] = new ItemInfo("Sword", "Shard object", 100, 0, "obj_SWORD", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
                items[ItemID.SHIELD] = new ItemInfo("Shield", "Blocks attacks", 50, 0, "obj_SHIELD", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
            }
        }
        //doing this wrong.

        /**********************
         * Inventory Functions
         * *******************/
         // remove from inventory
        public void Inv_Remove(Item.ItemInfo item, int qnty)
        {
            //DID THIS WRONG!  Need inventory list
            //In the script we will remove an item from the inventory
            var found = -1;
            //We will check if the item exists and take its place
            for (var i = 0; i < 5; i++)
            {
                //ItemID eyedee1 = (ItemID)i;
                if (inv.inventory[i].name == item.name) //maybe have a unique id rather than use name?  possibility.
                {
                    found = i;
                    break;
                }
            }

            if (found != -1)
            {
                //If we found the item, we remove it
                inv.inventory[found].quantity -= qnty;//qnty is the amount to remove
                if (inv.inventory[found].quantity <= 0)
                {
                    inv.inventory[found] = new Item.ItemInfo("Nothing", "Nothing", 0, 0, "nothing", "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
                }  //If there is no more of the item we change the place to an empty place (Nothing item)
            }
        }

        //In this script we will add items to the inventory
        public void Inv_Add(Item.ItemInfo item, int qnty)
        {
            var found = -1;
            bool fullflag = false;

            for (var i = 0; i < inv.invcapacity; i++)
            {
                if (inv.inventory[i].name == item.name)
                {
                    found = i; //We affect to found the place of the item if we find it
                    break;
                }
            }

            //If it doesn't exist

            if (found == -1)
            {
                //Let's check if there is an empty place to add our item
                for (var j = 0; j < inv.invcapacity; j++)
                {
                    if (inv.inventory[j].name == "Nothing")
                    {
                        found = j;
                        break;
                    }
                    else if (j == (inv.invcapacity - 1) & found != j)
                    {
                        fullflag = true;
                        break;
                    }
                }
            }

            //should have an "inventory is full" contingency
            if (fullflag)
            {
                // print this out!  in a game-friendly gui - depends on whether looting or picking up, or buying
                Debug.Log("inventory is full, cannot pick up object");
            }

            //Now let's add our item to the place we have *
            if (found != -1)
            {
                inv.inventory[found] = item; //Argument 0 is the id of the item
                inv.inventory[found].quantity += qnty; //Argument 1 is the amount of the item to add
            }
            // need a script for looting, for buying, and for picking up
        }

        // Drop an item
        public void Inv_Drop(Item.ItemInfo item)
        {
            // item = item clicked in gui - right click and click drop
            //Instantiate(hero, item);
            //not sure what to use in place of "item"
            Inv_Remove(item, 1);
        }

    }
}

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.

It looks like you’re getting lost on classes.

You should have the following class architecture

public class Item : ScriptableObject {
    public string name;
    public string description;
    ...
}

public class Inventory : MonoBehaviour {
    public List<Item: items;

    public void Add (Item item) {
        for (int i = 0; i < items.Count; i++) {
            if (items[i].name == item.name) {
                items[i].quantity++; // Item is already in inventory. Increase it's quantity by 1.
                return;
            }
            items.Add(item);
        }
    }

    public void Remove (Item item) {
        for (int i = 0; i < items.Count; i++) {
            if (items[i].name == item.name) {
                items[i].quantity--; // Item is already in inventory. Decrease it's quantity by 1.
                if (items[i].quantity == 0) {
                    items.RemoveAt(i);
                }
                return;
            }
        }
    }
}

public class ItemDatabase : ScriptableObject {
    public Dictionary<string, Item> items;
}

Also note that you probably do not want to add database items to the inventory. You want to add copies of them.

1 Like

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.

1 Like

thank you very much MathiasDG! the only thing is that i used an array of iteminfo type. should i not have done that?

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

namespace CoS
{
    public class heroInventory : MonoBehaviour
    {
        public Canvas hinventorycanvas;
        public int invcapacity = 5;
        public Item.ItemInfo[] inventory;

        public Canvas hinvequippedcanvas;

        // Use this for initialization
        void Start()
        {
            hinventorycanvas.enabled = false;
            hinvequippedcanvas.enabled = false;
            Item.ItemInfo[] inventory = new Item.ItemInfo[invcapacity];
        }

        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.I))
            {
               hinvequippedcanvas.enabled = !hinvequippedcanvas.enabled;
            }

        }
      
        public void XButton()
        {
            hinventorycanvas.enabled = false;
        }

        public void InventoryIconButton()
        {
            hinventorycanvas.enabled = true;
            //ShowInventory();
        }

        // got to look at GM to figure out the types for the inventory
        /*void ShowInventory()
        {
            string labeltxt = "";
            string labelimg = "";
            int counter = 0;
            for (int i = 0; i < invcapacity; i++)
            {
                counter = i + 1;
                //update icon and text
                labeltxt = "Text" + counter;
                labelimg = "Image" + counter;

                Text text = GameObject.Find(labeltxt).GetComponent("Text") as Text;
                text.text = inventory[i, 1] + " x " + inventory[i, 2];    // Need to fill out the inventory
                Image image = GameObject.Find(labelimg).GetComponent("Image") as Image;
                Sprite invicon = Resources.Load<Sprite>(inventory[i, 3]);
                image.sprite = invicon;
            }
        }*/
    }
}

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.

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.

1 Like

how would i make a “copy” of a database item?

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?

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.

1 Like