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. Join us on Thursday, June 8, for a Q&A with Unity's Content Pipeline group here on the forum, and on the Unity Discord, and discuss topics around Content Build, Import Workflows, Asset Database, and Addressables!
    Dismiss Notice

Question Inventory Item Stacking Problem

Discussion in 'Scripting' started by Deanford, Mar 31, 2023.

  1. Deanford

    Deanford

    Joined:
    Oct 17, 2014
    Posts:
    93
    Hi Guys,

    I am attempting to stack items that are already in the inventory however, I pick up one item and its shows as '2' but when I pick up another item it shows as '4' and then doubling everytime.

    Any help would be appreciated! Thanks!

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using TMPro;
    6. using ZSerializer;
    7. using static UnityEditor.Progress;
    8.  
    9. public class InventoryManager : PersistentMonoBehaviour
    10. {
    11.     public static InventoryManager Instance;
    12.     public List<Item> Items = new List<Item>();
    13.  
    14.     public Transform itemContent;
    15.     public GameObject slot;
    16.  
    17.     public InventoryItemController[] inventoryItems;
    18.  
    19.     public bool enableRemove = false;
    20.  
    21.     public GameObject mySlot;
    22.  
    23.     private void Awake()
    24.     {
    25.         Instance = this;
    26.     }
    27.  
    28.     public void AddItem(Item item)
    29.     {
    30.         if (item.isStackable)
    31.         {
    32.             bool itemAlreadyInInventory = false;
    33.  
    34.             foreach (Item inventoryItem in Items)
    35.             {
    36.                 //If the item type already exists in our inventory...
    37.                 if (inventoryItem.itemType == item.itemType)
    38.                 {
    39.                     //Add to our item amount
    40.                     inventoryItem.amount += item.amount;
    41.  
    42.                     itemAlreadyInInventory = true;
    43.                 }
    44.             }
    45.  
    46.             if (!itemAlreadyInInventory)
    47.             {
    48.                 Items.Add(item);
    49.             }
    50.         }
    51.         else
    52.         {
    53.             Items.Add(item);
    54.         }
    55.  
    56.         ListItems();
    57.     }
    58.  
    59.     public void RemoveItem(Item item)
    60.     {
    61.         Items.Remove(item);
    62.  
    63.         Destroy(gameObject);
    64.     }
    65.  
    66.     public void ListItems()
    67.     {
    68.         foreach (Transform item in itemContent)
    69.         {
    70.             Destroy(item.gameObject);
    71.         }
    72.  
    73.         foreach (var item in Items)
    74.         {
    75.             mySlot = Instantiate(slot, itemContent);
    76.  
    77.             var itemName = mySlot.transform.Find("ItemName").GetComponent<TextMeshProUGUI>();
    78.             var itemCount = mySlot.transform.Find("ItemCount").GetComponent<TextMeshProUGUI>();
    79.             var itemIcon = mySlot.transform.Find("ItemIcon").GetComponent<Image>();
    80.  
    81.             itemName.text = item.itemName;
    82.             itemIcon.sprite = item.icon;
    83.            
    84.  
    85.             if (item.amount > 1)
    86.             {
    87.                 itemCount.SetText(item.amount.ToString());
    88.             }
    89.             else
    90.             {
    91.                 itemCount.text = "";
    92.             }
    93.         }
    94.  
    95.         SetInventoryItems();
    96.     }
    97.  
    98.     public void SetInventoryItems()
    99.     {
    100.         inventoryItems = itemContent.GetComponentsInChildren<InventoryItemController>();
    101.  
    102.         for (int i = 0; i < Items.Count; i++)
    103.         {
    104.             inventoryItems[i].AddItem(Items[i]);
    105.         }
    106.     }
    107. }
    108.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    33,680
    Time to start debugging! Here is how you can begin your exciting new debugging adventures:

    You must find a way to get the information you need in order to reason about what the problem is.

    Once you understand what the problem is, you may begin to reason about a solution to the problem.

    What is often happening in these cases is one of the following:

    - the code you think is executing is not actually executing at all
    - the code is executing far EARLIER or LATER than you think
    - the code is executing far LESS OFTEN than you think
    - the code is executing far MORE OFTEN than you think <--- my money is HERE!
    - the code is executing on another GameObject than you think it is
    - you're getting an error or warning and you haven't noticed it in the console window

    To help gain more insight into your problem, I recommend liberally sprinkling
    Debug.Log()
    statements through your code to display information in realtime.

    Doing this should help you answer these types of questions:

    - is this code even running? which parts are running? how often does it run? what order does it run in?
    - what are the names of the GameObjects or Components involved?
    - what are the values of the variables involved? Are they initialized? Are the values reasonable?
    - are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

    Knowing this information will help you reason about the behavior you are seeing.

    You can also supply a second argument to Debug.Log() and when you click the message, it will highlight the object in scene, such as
    Debug.Log("Problem!",this);


    If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

    You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

    You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

    You could also just display various important quantities in UI Text elements to watch them change as you play the game.

    If you are running a mobile device you can also view the console output. Google for how on your particular mobile target, such as this answer or iOS: https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/ or this answer for Android: https://forum.unity.com/threads/how-to-capturing-device-logs-on-android.528680/

    If you are working in VR, it might be useful to make your on onscreen log output, or integrate one from the asset store, so you can see what is happening as you operate your software.

    Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

    Here's an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

    https://forum.unity.com/threads/coroutine-missing-hint-and-error.1103197/#post-7100494

    When in doubt, print it out!(tm)

    Note: the
    print()
    function is an alias for Debug.Log() provided by the MonoBehaviour class.

    Just as a point of reference:

    These things (inventory, shop systems, character customization, crafting, etc) are fairly tricky hairy beasts, definitely deep in advanced coding territory.

    They contain elements of:

    - a database of items that you may possibly possess / equip
    - a database of the items that you actually possess / equip currently
    - perhaps another database of your "storage" area at home base?
    - persistence of this information to storage between game runs
    - presentation of the inventory to the user (may have to scale and grow, overlay parts, clothing, etc)
    - interaction with items in the inventory or on the character or in the home base storage area
    - interaction with the world to get items in and out
    - dependence on asset definition (images, etc.) for presentation

    Just the design choices of an inventory system can have a lot of complicating confounding issues, such as:

    - can you have multiple items? Is there a limit?
    - if there is an item limit, what is it? Total count? Weight? Size? Something else?
    - are those items shown individually or do they stack?
    - are coins / gems stacked but other stuff isn't stacked?
    - do items have detailed data shown (durability, rarity, damage, etc.)?
    - can users combine items to make new items? How? Limits? Results? Messages of success/failure?
    - can users substantially modify items with other things like spells, gems, sockets, etc.?
    - does a worn-out item (shovel) become something else (like a stick) when the item wears out fully?
    - etc.

    Your best bet is probably to write down exactly what you want feature-wise. It may be useful to get very familiar with an existing game so you have an actual example of each feature in action.

    Once you have decided a baseline design, fully work through two or three different inventory tutorials on Youtube, perhaps even for the game example you have chosen above.

    Breaking down a large problem such as inventory:

    https://forum.unity.com/threads/weapon-inventory-and-how-to-script-weapons.1046236/#post-6769558

    If you want to see most of the steps involved, make a "micro inventory" in your game, something whereby the player can have (or not have) a single item, and display that item in the UI, and let the user select that item and do things with it (take, drop, use, wear, eat, sell, buy, etc.).

    Everything you learn doing that "micro inventory" of one item will apply when you have any larger more complex inventory, and it will give you a feel for what you are dealing with.

    Breaking down large problems in general:

    https://forum.unity.com/threads/opt...n-an-asteroid-belt-game.1395319/#post-8781697
     
  3. Olipool

    Olipool

    Joined:
    Feb 8, 2015
    Posts:
    315
    I can't spot a duplication in AddItem(),maybe there is an error with picking up items? Like Kurt said, I would add a Debug.Log(...) at the start of AddItem to see how often it gets called for one pickup and also one Debug.Log when you find the inventory item by type and print its amount and the amount of the picked up item.
     
    Kurt-Dekker likes this.
  4. Deanford

    Deanford

    Joined:
    Oct 17, 2014
    Posts:
    93

    Thank you both for your help. I debugged the code and adding the item is calling once while the picked up amount is calling 'picked up: 2' then 'picked up 4' and so on...

    I cant seem to make it only add 1 to the item count, unless I am missing something.

    I believe it is happening here, but I am no expert coder so I am not sure lol.

    Code (CSharp):
    1. //Add to our item amount
    2.                     inventoryItem.amount += item.amount;
    My items are created with a scriptable object script called Item and in that script I have a int variable for 'amount'... I believe it is getting my item in the inventory amount (which would be 1) and then adding 1 to it again with item.amount. So the next time it will be 2 + 2?

    I'm not sure if that is the problem but that is what is standing out to me. Is there a way to add 1 to my item count without calling item.amount (so then it doesnt double?)

    Thanks again!
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    33,680
    What are these two values before you add them together?

    Ask that of EVERY value and variable all the way up the chain.

    Some value is NOT what you think it is.
     
  6. Deanford

    Deanford

    Joined:
    Oct 17, 2014
    Posts:
    93
    I set the amount in the scriptable object item to 1 and the inventoryItem is 1, so would this mean that they are adding together when clicking the first item = 2 and then when picking up the second item of the same type its 4?

    If so, how would I go about just adding one each time?
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    33,680
    If you are struggling with this question:

    You need to seriously start with some basic C# and programming in Unity tutorials.

    The answer is:

    Code (csharp):
    1. myVariable = myVariable + 1;
    That's it.
     
  8. Olipool

    Olipool

    Joined:
    Feb 8, 2015
    Posts:
    315
    Scriptable object is a good info here...
    My suspicion is that item is a scriptable object and you add that object to the inventory the first time you pick it up. Next time you add 1 to the inventory item but since it is the scriptable object itself this also adds 1 to the scriptable object and by that the scriptable object now has an amount of 2 so next time you add 2 because item.amount is 2. Hope that makes sense.Maybe keep an eye on your scriptable object assets in the inspector and see if they get increased.
     
    Deanford likes this.
  9. Olipool

    Olipool

    Joined:
    Feb 8, 2015
    Posts:
    315
    Thinking more about it, it does not matter if those are scriptable objects. They are objects and by that used by reference. So if you have an item let's say a potion and it has the amount of 1 and you add it to the inventory list, then the list has a reference to the potion and if you increase the amount it not only increases in the list but in the potion.
    So one way to deal with that would be to make the items value objects like struct but that may lead to other problems. Or you could make a copy to insert into the inventory list. Or you can make a new type for items in the inventory e.g. Inventory Item that has a field to the Item for all the static data like name and description and a field for amount which you increase. And adding an item to the inventory would be:
    Items.Add(new InventoryItem(item))
    And the constructor would assign the item to the item filed and the initial amount to the amount field and then you would increase that amount field after that when picking up a new item of that type.
     
    Deanford likes this.
  10. Deanford

    Deanford

    Joined:
    Oct 17, 2014
    Posts:
    93
    Ah, thanks for the idea. I will give it a shot and see what I can do!
     
    Olipool likes this.