Search Unity

Inventory System - From scratch - Too advanced for me

Discussion in 'Scripting' started by Loff, Jan 27, 2015.

  1. Loff

    Loff

    Joined:
    Dec 3, 2012
    Posts:
    81
    Hello,
    (C#, 2D game, Platformer)
    I started working on a inventory system for the first time and I can already see issues with the way I script it.
    I did know that my script would be bad by the way, but I wanted to give it a go just to learn and earn experience.

    First problem I ran into: I don't know loops, and then I wrote this to draw the slots. Which works but in the end I would love just making a easy loop to draw inventory slots ( the Background image for the slots)
    Code (CSharp):
    1. // Draw Inventory Slots:
    2.     //0,0
    3.         GUI.DrawTexture (new Rect (slot_x_pos + (slotXsize * 0), slot_y_pos + (slotYsize * 0), slotXsize, slotYsize), slot);
    4.     //0,1
    5.         GUI.DrawTexture(new Rect(slot_x_pos+(slotXsize*1),slot_y_pos+(slotYsize*0),slotXsize,slotYsize),slot);
    6.     //0,2
    7.         GUI.DrawTexture(new Rect(slot_x_pos+(slotXsize*2),slot_y_pos+(slotYsize*0),slotXsize,slotYsize),slot);
    8.     //0,3
    9.         GUI.DrawTexture(new Rect(slot_x_pos+(slotXsize*3),slot_y_pos+(slotYsize*0),slotXsize,slotYsize),slot);
    10.     //1,0
    11.         GUI.DrawTexture(new Rect(slot_x_pos+(slotXsize*0),slot_y_pos+(slotYsize*1),slotXsize,slotYsize),slot);
    12.     //1,1
    13.         GUI.DrawTexture(new Rect(slot_x_pos+(slotXsize*1),slot_y_pos+(slotYsize*1),slotXsize,slotYsize),slot);

    For my database I tried two examples, but I see issues in both. Maybe I shouldn't do it this way at all or create single array for each item, I don't know.
    Code (CSharp):
    1. // 1 = Food
    2.     // 2 = Armor
    3.     // 3 = One-hander
    4.     // 4 = Shield
    5.     // 5 = Two-hander
    6.     public Texture[] Icon1;
    7.     [HideInInspector] public string[] Name1 = new string[6] {"Apple","Pie", "", "", "",""};
    8.     [HideInInspector] public string[] Desc1 = new string[6] {"Eat me","Eat me NOW", "", "", "",""};
    9.  
    10.     public Texture[] Icon2;
    11.     [HideInInspector] public string[] Name2 = new string[6] {"Basic Leather Armor","Basic Cloth Armor", "", "", "",""};
    12.     [HideInInspector] public string[] Desc2 = new string[6] {"Armor Test","My skin is protected by nothing", "", "", "",""};

    Code (CSharp):
    1.     [HideInInspector] public string[] Type = new string[6] {"Food","Armor", "One-hander", "Shield", "Two-hander};
    2.    [HideInInspector] public string[] Name = new string[6] {"Apple","Basic Leather Chest", "", ""};
    3.    [HideInInspector] public string[] Desc = new string[6] {"Eat Me","Test Armor", "", ""};
    4.    [HideInInspector] public int[] lvlReq = new int[6] {1,1,0,0,0,0};
    5.    [HideInInspector] public int[] Health = new int[6] {20,0,0,0,0,0};
    6.    [HideInInspector] public int[] Stamina = new int[6] {0,0,0,0,0,0};
    7.    [HideInInspector] public int[] Strength = new int[6] {0,5,0,0,0,0};

    The last thing I started on was right clicking items in the inventory and grabbing/showing items in the inventory.
    Code (CSharp):
    1.     if (slot00 == true) {
    2.                 if (slot00_type == 1) {
    3.                         if (GUI.Button (new Rect (slot_x_pos + (slotXsize * 0) + 4, slot_y_pos + (slotYsize * 0) + 4, iconXsize, iconYsize), item_database.Icon1 [slot00_value], "")) {
    4.                     editing = true;
    5.                         }
    6.                 }
    7.         }
    Code (CSharp):
    1. // Right clicked Fruit
    2.         if (editing)
    3.         {
    4.             for (int x = 0; x < 3; x++)
    5.             {
    6.                 if (GUI.Button(new Rect(Box.x, (Box.height * x) + Box.y + Box.height, Box.width, Box.height),btns_food[x], ""))
    7.                 {
    8.                     selected_btn_food = btns_food[x];
    9.                     editing = false;
    10.                 }
    11.             }
    12.         }
    13.  
    14.         // What happens when the food selection is choosen:
    15.         if (selected_btn_food == "Eat") {
    16.             slot00_value = 0;
    17.             slot00_type = 0;
    18.             slot00 = false;
    19.             selected_btn_food = "None";
    20.         }
    21.         if (selected_btn_food == "Drop") {
    22.             selected_btn_food = "None";
    23.         }
    24.         if (selected_btn_food == "Cancel") {
    25.             selected_btn_food = "None";
    26.         }
    27.    

    Yepp, this is all really static and bad. so if I wanted to continue on this I need to make a work around getting it more dynamic or use thousands of If statements, but I can see this is way to bad to bother with it.
    At the moment I will read about Enum and loops trying to understand them. My inventory doesn't have to be super advance, I just hope to learn and create something I understand and can Use 100%. I don't wanna buy a asset just to take a shortcut even if it's tempting at times as I really want a inventory system to work :)

    After reading what I tried to created you might have ideas for me. The original thread is here if you wanna take a look.
    Motivated to learn and make this work, but I'm having a hard time learning it by googling as it's a advance task for me.
     
  2. DanSuperGP

    DanSuperGP

    Joined:
    Apr 7, 2013
    Posts:
    408
    JoeStrout and Kiwasi like this.
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Loops are kind of fundamental to any programming language. You won't get far without them.
     
  4. Loff

    Loff

    Joined:
    Dec 3, 2012
    Posts:
    81
    I have a main goal which is a working inventory system.
    On my road to finishing this system I have to learn different things, and loop is for sure one of them.
    As I said in the post that I'm reading about it, but I'm a bit slow when it comes to reading/learning scripts by myself but it will be done.
     
  5. DanSuperGP

    DanSuperGP

    Joined:
    Apr 7, 2013
    Posts:
    408
    Yeah, you're going about it backwards.

    You need to work on your programming fundamentals, until loops are as second nature as breathing.

    Then worry about the game systems you are trying to build first.
     
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I'm a fan of just in time learning. But you have a lot of catching up here. Learn to write loops. Learn to write classes to encapsulate data.

    You could probably get a lot of this just by watching a few Youtube tutorials on inventory systems.

    I would also suggest upgrading to the new UI tools from 4.6
     
    Loff likes this.
  7. simone9725

    simone9725

    Joined:
    Jul 19, 2014
    Posts:
    234
    Hi I follow this tutorial To make a shop
    I hope It will be helpful
     
    Loff likes this.
  8. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Loff: Well, kudos to you for giving it a yeoman's try!


    There is a lot to just trying to figure it out, and learning on the way...

    ... but you are a long way out on a limb:


    Why don't you first try what DanSuperGP suggested, which fits in with BoredMormon's "just in time" learning:
    - go thru the basic scripting videos on the web site (see the link above), and you also might want to run the "Roll-a-Ball" series (it's short!) to get a handle on basic scripting.

    Then:

    Make a plain class that represents your inventory item. Have it include all of the things you think it will need. Then, in a monobehaviour make a generic list of the Type of your inventory item class. This generic list will be the actual inventory for your player - the things your player is carrying. Make sure you have set the access to your plain class and your generic list so you can use it in your monobehaviour. Don't worry about how to display this inventory yet. Don't worry about creating your inventory items yet.

    When you've done this, ping us again.

    Now, if you don't know what any of these things are in bold then you'll need to do some research and find out.

    (^_^)!
     
    Loff and Kiwasi like this.
  9. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    I watched a good bit of this one at the beginning and then spot checked it throughout.

    I'm not convinced this is the way I would approach an inventory system, but that being said, you will learn something no matter what you try. This system seems tightly coupled between the way the player is moving and what its touching (collisions, etc.) and how the UI responds and how the inventory system works.

    Ultimately, for scale, you will want an inventory system that is divorced or decoupled from the UI, and a UI that can read the data from the inventory system, but neither are locked into each other.

    There were a couple hard wired approaches to the UI as well. Not a bad place to start, but if it were me, I would look a the Panes, Panels and Windows session in the Live Training archive to see how to build a window, and then look at layout groups to control the size and shape of the content. This way, I would be able to resize the inventory window at runtime and have the content fit.
     
    theANMATOR2b and Loff like this.
  10. Loff

    Loff

    Joined:
    Dec 3, 2012
    Posts:
    81
    So I tried what you said. I might have mixed the stuff I added into the "inventory" script. Maybe that should be a script adding items to database or so instead.
    Is this anything like what you were thinking of?
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. public class itemDatabase{
    6.  
    7.     //public enum SlotType {None, Bag , Hand, Head, Chest, Ring, OneHandWep, Shield, TwoHandWep}
    8.  
    9.     public string name;
    10.     public int lvlReq;
    11.     public int strength;
    12.     //public Texture2D itemIcon;
    13.    
    14.     public itemDatabase(string newName, int newLvlReq, int newStrength /*Texture2D newItemIcon*/)
    15.     {
    16.         name = newName;
    17.         lvlReq = newLvlReq;
    18.         strength = newStrength;
    19.         //itemIcon = newItemIcon;
    20.  
    21.     }
    22.  
    23. }

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. public class inventory : MonoBehaviour {
    5.  
    6.     void Start () {
    7.  
    8.         List<itemDatabase> itemdatabase = new List<itemDatabase>();
    9.  
    10.         itemdatabase.Add ( new itemDatabase ("Leather Jacket", 1, 1));
    11.         itemdatabase.Add ( new itemDatabase ("Cloth Robe", 1, 0));
    12.         itemdatabase.Add ( new itemDatabase ("Mail Chest", 2, 3));
    13.  
    14.    
    15.     } // Start end
    16.     void Update () {
    17.     } // Update end
    18.     void OnGUI() {
    19.     } //OnGUI end
    20. }
     
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    That's the general idea.

    As a quick tip I'd suggest a little refactoring of the names. This will help you dramatically if you need to walk away from the code for a while and come back. itemDatabase is a class, call it ItemDatabase (or better yet, Item). inventory should be Inventory. Write out things like lvlReq in full, between autocomplete and the compiler you won't loose any performance.

    Your next step is to create a couple of methods on your Inventory class. AddItem and RemoveItem. For the moment they can just add the item on to the end of the list. Removing is slightly trickier, I would suggest doing it via index.
     
    Loff likes this.
  12. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    BoredMormon is correct. As a matter of standard practice:
    • variables start with a lowerCaseLetter
    • Functions start with an UpperCaseLetter
    A few more notes:

    To make a class merely to hold some data, you don't need the name spaces for such simple data. You can simply have:
    Code (csharp):
    1. public class MyClass {
    2.  
    3. }
    Moreover:

    As this is simply defining the structure of the class, you may find you don't need to declare a specific constructor.

    Lastly, this isn't defining your "Database" of items, but will be used to define one particular item at a time (and you will have many instances of this - one for each item), so you can simply write:

    Code (csharp):
    1. public class Item {
    2.    public string name;
    3.    public int lvlReq;
    4.    public int strength;
    5. }
    As this doesn't derive from Monobehaviour, it can safely be added to the script document containing your inventory, or it can be in it's own .cs document. Unity doesn't care.

    -

    Now the next step is looking at all the elements you will need and where they will be placed.

    Already there is some confusion over the data and the enclosing database.

    Try to sketch out, on paper is fine, how all of this will go together.

    Some feelings:

    You'll need a class to define the item.
    You'll need a database of the items available in your game.
    You'll need a list of the items that your player has in his specific inventory.
    Later, you'll need a list of items that are equipped.
    Keep in mind that in the game there will have to be individual or groups of items available for pickup.

    Think about the idea that you don't want your UI to be tightly coupled with your character's inventory.

    (This is a rough sketch of who owns what and where it's written in blocky format - because if you don't know what you're going to do, how can you do it?)
     
    BrandyStarbrite and Loff like this.
  13. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    I should have added: "Don't worry about how to implement this structure, but just rough out what you will need, and then we can figure out the HOW.
     
  14. Loff

    Loff

    Joined:
    Dec 3, 2012
    Posts:
    81
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. public class Inventory : MonoBehaviour {
    5.  
    6. }
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. public class Item{
    6.     public string name;
    7.     public int levelRequired;
    8.     public int strength;
    9.     public Texture2D itemIcon;
    10. }
    "You'll need a class to define the item."
    - Is this like "item" having different vars that it gonna store vars for the single item? While database is holding the vars for every item. When a item for example is picked up, it has to load the vars from the database to the single item? So that the item class will now have vars from the database?

    "You'll need a database of the items available in your game."
    - Will all items have for example "strength" with zero or higher, or should only items with strength have that var?
    I just wonder if every item is gonna use a standard var layout or if I should do the items 1 by 1 with the vars it needs.

    "You'll need a list of the items that your player has in his specific inventory.
    You'll need a list of the items that your player has in his specific inventory.
    Later, you'll need a list of items that are equipped.
    Keep in mind that in the game there will have to be individual or groups of items available for pickup."
    - It sounds simple enough, but not sure how I'm gonna do it. What is a good way to build these lists? Should all of them be a own C# script in unity or put them in a single file?

    My inventory is gonna be pretty much like WoW's if I ever manage it. So I know what features I want, I'm just not sure how it's buildt behind so it's hard to for example sketch it out, but I get the ideas of how you want to build it.

    This is more a feature list and doesn't show anything of how they are connected.
    http://imgur.com/GOPgwS9
     
    Last edited: Feb 7, 2015
  15. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Sorry - I forgot to reply when this popped up as new, and then I didn't get another flag in my face. Just occurred to me I missed this!

    Sorry! My bad...

    [Reply below]
     
  16. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Yes, this is correct, and your basic class is correct. You will probably need more details, but yes.

    I would suggest something like this:
    Code (CSharp):
    1. public enum SlotType    {Empty, Head, Chest, Waist, Legs, Feet, Back, Neck, Hands, Ring, MainHand, OffHand, Bag}
    2. public enum ItemType    {Junk, Weapon, Clothing, Consumable, Container}
    3.  
    4. [System.Serializable]                                            //    Our Representation of an InventoryItem
    5. public class InventoryItem {
    6.     public string itemName = "New Item";                        //    What the item will be called in the inventory
    7.     public Sprite itemIcon = null;                                //    What the item will look like in the inventory
    8.     public SlotType slotType = SlotType.Bag;                    //    What slot the item will fit in
    9.     public ItemType itemType = ItemType.Junk;                    //    What type of item it is - primarily for the Editor Window
    10.     public int containerSize = 0;                                //    The number of "slots" or "spaces" in the container
    11.     public Rigidbody itemObject = null;                            //    Optional slot for a PreFab to instantiate when discarding
    12.     public bool isUnique = false;                                //    Optional checkbox to indicate that there should only be one of these items per game
    13.     public bool isIndestructible = false;                        //    Optional checkbox to prevent an item from being destroyed by the player
    14.     public bool isQuestItem = false;                            //    Examples of additional information that could be held in InventoryItem
    15.     public bool isStackable = false;                            //    Examples of additional information that could be held in InventoryItem
    16.     public int stackSize = 0;
    17.     public bool destroyOnUse = false;                            //    Examples of additional information that could be held in InventoryItem
    18.     public float encumbranceValue = 0;                            //    Examples of additional information that could be held in InventoryItem
    19. }
    There are a couple of "take-aways" from this code:
    • Serializable: To be able to see this custom class, it needs to be "serialized" or saved into simple data. To do this, you need to access the "System" namespace and use the "Serializable" attribute.
    • First variable is the string itemName (this could be "name"): When looking at a class in a collections like an Array or a List, if the first variable is a string, the element "slot" / "location" / "index" will be named after the string, not just named "Element 0, Element 1, ... Element N"
    • Initialization: These variables are initialized to a value. Not normally critical, but in this case, you will have default values in the inspector when you create a collection of these.
    • Enums: Enums are a constant value. They have names that represent / correspond to underlying ints. You could have a simplified code where each slot is represented by an int value: 0 for empty or none, 1 for head, 2 for chest... or you could use an enum that does the same thing and you can human-read these values by their names Empty, Head, Chest... and access them with SlotType.Head; etc.
    Essentially, you will need a definition of every single item that will ever be used in your game. This is the item data base. This will probably consist of a collection (List or Array) of the type of your Item class. Each entry will be the definition of one item: It's name, it's icon, slot type, etc..

    How should we store this? There are some more advanced techniques, which we may or may not get into here (read up on ScriptableObjects when you get the chance!), but you can start, for an example, with a script that simply holds a list of these items, which the rest of the system can reference. This will ultimately be inadequate, but for the sake of learning, it's a good place to start.

    As your "Item" class will define all of your items, there will be at least a default value for all of the items in your game, even if it's 0, or null, or "", so even if an item doesn't need it you will probably have a definition there for it. You could get into inheritance to be more efficient/organize your items better, but that's not necessary yet.

    Don't worry about how this will display later in the game. You can deal with that once you get there.

    The player will need a list or array to represent the inventory on the character. A collection of items, defined by the item class, available to the player. I would suggest a generic list as you can more easily add and remove items to and from a list.
    Code (CSharp):
    1. using System.Collections.Generic;
    2.  
    3. public class MyPlayer : Monobehaviour {
    4.     public int inventorySize;
    5.     public List <Item> playerInventory;
    6.     public List <Item> equippedItems;
    7.  
    8.     void Awake () {
    9.         playerInventory = new List <Item> ();
    10.         equippedItems = new List <Item> ();
    11.         for (int i = 0; i < inventorySize; i++) {
    12.             playerInventory.Add(null);
    13.         }
    14.     }
    15. }
    This code is too simple to really use, but works as an example. This will create two lists that hold inventory items. One represents our player's inventory. The other what the player has equipped. In this example, we fill the empty inventory with a load of non-items (null). This does, however, create and fill our List in a way that we can just fill it with items when we need to.

    This will build a WoW-like inventory, as opposed to one where you need to fill a grid.

    At this stage, don't worry about your UI, look, feel, dragging or clicking. That can be done later.
     
    Loff likes this.
  17. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Also, while you're at it, go thru the scripting section of the website, and perhaps look at one of the generic C# tutorials you can find on the 'net to start building confidence with coding.

    Here's one (it's a bit dry, but it's not bad):
    http://www.csharp-station.com/Tutorial.aspx
     
    Loff likes this.
  18. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    We're making something like this:


    The next thing we have to work on is a script that controls how items are added to and removed from the Inventory.

    See if you can set this up in isolation, rather than in a work in progress game.

    Create some sample items in the database.

    Then give a go a creating the "Inventory" script that controls how to add items to the inventory, remove them when used. What you are looking for is a generic controller that other scripts in your game can interact with.
     
    Loff likes this.