Search Unity

  1. The 2022.1 beta is now available for testing. To find out what's new, have a look at our 2022.1 beta blog post.
    Dismiss Notice

Help Wanted I'm having a difficult trying to use the GameFoundationSdk.inventory.FindItem method

Discussion in 'Game Foundation' started by Matb3, Nov 23, 2020.

  1. Matb3


    Nov 23, 2020
    Hello, i'm using version 0.7 of GameFoundation, and i'm trying to use the FindItem method, but it always return a null value, even if i had used the "CreateItem" method in previous calls. Here's my code:
    Code (CSharp):
    1. public StackableInventoryItem InstantiateItem(CollectableItem cItem)
    2.     {
    3.         StackableInventoryItem item;
    4.         InventoryItemDefinition itemDefinition = GameFoundationSdk.catalog.Find<InventoryItemDefinition>(cItem.itemName);
    5.         item = GameFoundationSdk.inventory.FindItem(itemDefinition.displayName) as StackableInventoryItem;
    6.         if (item == null)
    7.         {
    8.             item = GameFoundationSdk.inventory.CreateItem(itemDefinition) as StackableInventoryItem;
    9.             item.SetQuantity(cItem.quantity);
    10.         }
    11.         InventoryItem item2 = GameFoundationSdk.inventory.FindItem(itemDefinition.displayName);
    12.         if (item2 != null)
    13.         {
    14.             Debug.Log("Item2:" + item2.definition.displayName);
    15.         }
    16.         InventoryItem item3 = GameFoundationSdk.inventory.FindItem(itemDefinition.key);
    17.         if (item3 != null)
    18.         {
    19.             Debug.Log("Item3:" + item3.definition.displayName);
    20.         }
    21.         return item;
    22.     }
    The CollectableItem class is an object containg the name and the quantity for "drop" pourposes. The is equals to the key that i had set to my item in the Game Foundation Inventory window. The "item2" and "item3" are tries to get a return using the key and name, confirming that is not a problem with the "as StackableInventoryItem" on item, but all of them are null.

    @Edit: I found that the version 0.8 is out, tried it, but still having the same results
  2. tony_c-unity3d


    Unity Technologies

    Jul 18, 2019
    Hi @Matb3 ,

    Thank you so much for your interest in Game Foundation and for taking the time to reach out when you encountered issues. I've looked over your initial test code and found a couple of misunderstandings. This is just a first pass at explaining some of the issues you encountered--I encourage you to reach out if you have any more questions or if this explanation is confusing in any way.

    To start off, I'm assuming that you actually want to instantiate a new item each time your method, 'InstantiateItem' is called. If that's not the case and would like to find an existing item if it already exists, please let me know--the test code could be rewritten to find existing items by definition first and only instantiate if needed.

    Also, you were using a custom class named CollectableItem which wasn't included. I replaced the class by passing the 2 fields you were accessing, which also makes it more explicit and shows exactly what the method needs to function. Importantly, you would never want to use the displayName to find catalog items, but rather the definition keys so that's what I'm passing instead. The definition key can be found in the Unity Game Foundation Inventory window (Window > Game Foundation > Inventory Item) and is the 2nd field down under 'General', right below the 'Display Name' field.

    I've broken down the transformation of your test code into a working solution in steps (below), but please note that the first step doesn't actually solve your problem, just replaces your CollectableItem class with 2 parameters to permit the final solution (STEP #3):

    STEP #1 (still fails): replacing CollectableItem (unknown class) with 2 parameters:

    Code (csharp):
    2.     public StackableInventoryItem InstantiateItem(string definitionKey, long quantity)
    3.     {
    4.         StackableInventoryItem item;
    5.         InventoryItemDefinition itemDefinition = GameFoundationSdk.catalog.Find<InventoryItemDefinition>(definitionKey);
    6.         item = GameFoundationSdk.inventory.FindItem(itemDefinition.displayName) as StackableInventoryItem;
    7.         if (item == null)
    8.         {
    9.             item = GameFoundationSdk.inventory.CreateItem(itemDefinition) as StackableInventoryItem;
    10.             item.SetQuantity(quantity);
    11.         }
    12.         InventoryItem item2 = GameFoundationSdk.inventory.FindItem(itemDefinition.displayName);
    13.         if (item2 != null)
    14.         {
    15.             Debug.Log("Item2:" + item2.definition.displayName);
    16.         }
    17.         InventoryItem item3 = GameFoundationSdk.inventory.FindItem(itemDefinition.key);
    18.         if (item3 != null)
    19.         {
    20.             Debug.Log("Item3:" + item3.definition.displayName);
    21.         }
    22.         return item;
    23.     }
    Now that that's complete, I've addressed the underlying issue which is that, once you find the item definition, you can use that to immediately instantiate an item. The null check is only needed to ensure the item definition existed; once you have it, you're ready to instantiate and return the item which will then be ready for use.

    STEP #2: instatiate the stackable inventory item from the stackable inventory item definition:

    Code (csharp):
    2.     public StackableInventoryItem InstantiateItem2(string definitionKey, long quantity)
    3.     {
    4.         StackableInventoryItem item;
    6.     #if OLD_VERSION
    7.         StackableInventoryItemDefinition itemDefinition = GameFoundationSdk.catalog.Find<InventoryItemDefinition>(definitionKey) as StackableInventoryItemDefinition;
    8.     #else
    9.         // implementation note: suggest passing StackableInventoryItemDefinition to retrieve the correct type directly so you won't need to cast it
    10.         StackableInventoryItemDefinition itemDefinition = GameFoundationSdk.catalog.Find<StackableInventoryItemDefinition>(definitionKey);
    11.     #endif
    13.     // notes:
    14.     // 1) FindItem doesn't use the displayName, but you could pass it the itemDefinition directly to FindItems to update a list of all items using that definition
    15.     // 2) if you do intend to instantiate a new item (as the method name implies), you don't need to find the item at all; just create one from the definition (above).
    16.     // item = GameFoundationSdk.inventory.FindItem(itemDefinition.displayName) as StackableInventoryItem;
    17.     // if (item == null)
    18.     // { ... }
    20.     #if OLD_VERSION
    21.         item = GameFoundationSdk.inventory.CreateItem(itemDefinition);
    22.         item.SetQuantity(quantity);
    23.     #else
    24.         // implementation note: the above 2 lines could be simplified to:
    25.         item = GameFoundationSdk.inventory.CreateItem(itemDefinition, quantity);
    26.     #endif
    27.         return item;
    28.     }
    Now, with a minor cleanup of above code (which can be tested with or without OLD_VERSION defined), the following is the final version you could use to instantiate items using a StackableInventoryItemDefinition key:

    STEP #3: final, cleaned version:

    Code (csharp):
    2.     public StackableInventoryItem InstantiateItem3(string definitionKey, long quantity)
    3.     {
    4.         var itemDefinition = GameFoundationSdk.catalog.Find<StackableInventoryItemDefinition>(definitionKey);
    5.         if (itemDefinition is null)
    6.         {
    7.             Debug.LogError($"Unable to find StackableInventoryItemDefinition for {definitionKey}.");
    8.             return null;
    9.         }
    11.         // since itemDefinition is a StackableInventoryItemDefinition, CreateItem will return a StackableInventoryItem
    12.         var item = GameFoundationSdk.inventory.CreateItem(itemDefinition, quantity);
    13.         return item;
    14.     }
    For more information and some sample code, please check out our Stackable Inventory item sample code (02a_StackableInventoryItem). If you do, you could also add the following test code to see your InstantiateItem code working (just add this to the existing Start() method and see the results in the log):

    TEST CODE (add to Start()):

    Code (csharp):
    2. var i2 = InstantiateItem2("healthPotion", 7);
    3. var i3 = InstantiateItem3("healthPotion", 17);
    4. Debug.Log($"i2.displayName:{i2.definition.displayName} qty:{i2.quantity}");
    5. Debug.Log($"i3.displayName:{i3.definition.displayName} qty:{i3.quantity}");

    Code (csharp):
    2. "i2.displayName:Health Potion qty:7"
    3. "i3.displayName:Health Potion qty:17"

    Hope this helps! If you do want more details or a sample of finding existing inventory items (i.e. if they had been previously instantiated using this method), please let me know. Best of luck!
    Last edited: Nov 23, 2020
    Orlando_AGS and Matb3 like this.
  3. Matb3


    Nov 23, 2020
    I think i should had explain and/or rename my method, sorry

    What i'm actually trying to do is search in the inventory if the player already collected the item (the pourpose of the FindItem). If he hasn't, them i create a new item and return this, if he has, i just increase the quantity (i forgot to put an else for the first if), not creating a new item, because this will make a list with, for example "health potion: 1, health potion: 1, health potion: 1" and not and unique return of "health potion: 3"

    Anyway, thanks for this answer, there are some cleanups that i didn't know because this "StackableItem" are new
    Last edited: Nov 23, 2020
  4. tony_c-unity3d


    Unity Technologies

    Jul 18, 2019
    Hi @Matb3,

    Oh, sorry for the confusion. I took another stab at solving your problem and came up with a couple of options. The first is definitely not best practice, but, it shows the most unified solution to your problem, you could do the following:

    Code (csharp):
    2. // static class member: temporary item list to avoid excess allocations
    3. // note: this could be inside the method below, but then it would allocate a new list every time you wanted to increment the stackable item
    4. static List<InventoryItem> inventoryItemList = new List<InventoryItem>();
    6. // find and increase quantity of specified stackable item by stackable item definition key.  creates new item if none exist.
    7. public StackableInventoryItem IncreaseQuantity(string definitionKey, long inc)
    8. {
    9.     // find the stackable inventory item definition
    10.     // note: this could be moved to Start() and the definition saved off as a member variable to avoid searching every time.
    11.     StackableInventoryItemDefinition itemDefinition = GameFoundationSdk.catalog.Find<StackableInventoryItemDefinition>(definitionKey);
    12.     if (itemDefinition is null)
    13.     {
    14.         Debug.LogError($"Unable to find StackableInventoryItemDefinition for {definitionKey}.");
    15.         return null;
    16.     }
    18.     // find the stackable item based off that definition
    19.     StackableInventoryItem item;
    20.     if (GameFoundationSdk.inventory.FindItems(itemDefinition, inventoryItemList) > 0)
    21.     {
    22.         item = inventoryItemList[0] as StackableInventoryItem;
    23.         item.SetQuantity(item.quantity + inc);
    24.     }
    25.     // if no stackable item already exists, create one now
    26.     else
    27.     {
    28.         item = GameFoundationSdk.inventory.CreateItem(itemDefinition, inc);
    29.     }
    31.     return item;
    32. }
    This solution suffers from the following problems:

    1) It has to re-find the definition each time. To avoid this, you could search the catalog only in Start() then save off the definition.
    2) It has to re-find the stackable item every time. This could be avoided if you save off the item once created for later use (see below). The preferred solution would be:

    Code (csharp):
    2. // definition key to use for all IncStackQty calls
    3. const string m_StackableItemDefinitionKey = "healthPotion";
    5. // save off the actual stackable item (not definition) for later gets and/or increments
    6. StackableInventoryItem m_Stack = null;
    8. // return the current quantity.
    9. // note: this tolerates stackable item having never been created
    10. long GetStackQty()
    11. {
    12.     return m_Stack is null ? 0 : m_Stack.quantity;
    13. }
    15. // increments the quantity of the stackable item.  note: this creates a new one, if needed
    16. long IncStackQty(long inc)
    17. {
    18.     // if the stackable item has never been created then create one now
    19.     if (m_Stack is null)
    20.     {
    21.         var def = GameFoundationSdk.catalog.Find<StackableInventoryItemDefinition>(m_StackableItemDefinitionKey);
    22.         if (def is null)
    23.         {
    24.             Debug.LogError($"Unable to find stackable inventory item definition.");
    25.             return 0;
    26.         }
    28.         m_Stack = GameFoundationSdk.inventory.CreateItem(def, inc);
    29.         return inc;
    30.     }
    32.     // increment the quantity of the existing stackable item
    33.     var newQuantity = m_Stack.quantity + inc;
    34.     m_Stack.SetQuantity(newQuantity);
    36.     // return the new quantity of stackable items
    37.     return newQuantity;
    38. }
    Orlando_AGS and Matb3 like this.
  5. Matb3


    Nov 23, 2020
    The first solution is more similar to what i was trying to do, i just thought that the FindItem method would spare me to have to use a list and get the position 0 of that list, but it doesn't seem work like that.

    Thank you for the answers!
    erika_d and tony_c-unity3d like this.
  6. tony_c-unity3d


    Unity Technologies

    Jul 18, 2019
    Of course! Yes, to find items by definition, it has to return a list since there could be more than one. Best of luck with your project!
    Orlando_AGS, erika_d and Matb3 like this.