BridgeEquippableProcessing m_PreviousActiveSets initialised too early

Zaddo

Active member
Hi,

I get an error when I try to assign a weapon to the character. I must have something set up incorrectly in my project, but I am not sure how to fix this. Here are the details:

The problem is the CharacterInventoryBridge.Awake is being called before the InventoryItemSetManager.Initialise is called. This results in the BridgeEquippableProcess.m_PreviousActiveSets getting initialised with a CategoryCount of zero.

Then When I attempt to assign weapon to right hand. BridgeEquippableProcessing.ReEquipItems attempts to assign previousItemSet with the empty array on m_PreviousActiveSets.

This is where error is thrown:
BridgeEquippableProcessing.ReEquipItems (@ line 507): var previousItemSet = m_PreviousActiveSets.Active.Data[categoryIndex]; // m_PreviousActiveSets.Active has Category Count of Zero from when is created before. So throws an error

Start up steps occuring in wrong order:
CharacterInventoryBridge.Awake (@ line 134): m_BridgeEquippableProcessing = new BridgeEquippableProcessing(this);
=> In BridgeEquippableProcessing Constructor (@ line 109): m_PreviousActiveSets = new ActiveAndNextItemSetData(ItemSetManager.CategoryCount,ItemSetManager.SlotCount); // ItemSetManager.CategoryCount is zero at this point

InventoryItemSetManager.Initialize => Loads ItemSetRules with the categories
@ line 115: System.Array.Resize(ref m_CategoryItemSets, validCategoryCount); // Now sets m_CategoryItemSets to correct size. But too late for m_PreviousActiveSets which has zero length

The Error:



Code:
IndexOutOfRangeException: Index was outside the bounds of the array.
Opsive.UltimateCharacterController.Integrations.UltimateInventorySystem.BridgeEquippableProcessing.ReEquipItems (System.Int32 categoryIndex, System.Int32& allowedSlotsLayerMask) (at Assets/Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/BridgeEquippableProcessing.cs:507)
Opsive.UltimateCharacterController.Integrations.UltimateInventorySystem.BridgeEquippableProcessing.UpdateItemSetItems (System.Boolean equip) (at Assets/Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/BridgeEquippableProcessing.cs:463)
Opsive.UltimateCharacterController.Integrations.UltimateInventorySystem.BridgeEquippableProcessing.OnItemAddedToEquippable (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, Opsive.UltimateInventorySystem.Core.DataStructures.ItemStack addedItemStack) (at Assets/Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/BridgeEquippableProcessing.cs:196)
Opsive.UltimateCharacterController.Integrations.UltimateInventorySystem.BridgeEquippableProcessing.OnAddItemToInventory (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, Opsive.UltimateInventorySystem.Core.DataStructures.ItemStack addedItemStack) (at Assets/Opsive/UltimateCharacterController/Integrations/UltimateInventorySystem/Scripts/BridgeEquippableProcessing.cs:770)
Opsive.Shared.Events.InvokableAction`2[T1,T2].Invoke (T1 arg1, T2 arg2) (at <27da9e1afec54f2fb2a11d46a234f9df>:0)
Opsive.Shared.Events.EventHandler.ExecuteEvent[T1,T2] (System.Object obj, System.String eventName, T1 arg1, T2 arg2) (at <27da9e1afec54f2fb2a11d46a234f9df>:0)
Opsive.UltimateInventorySystem.Core.InventoryCollections.ItemCollection.NotifyAdd (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, Opsive.UltimateInventorySystem.Core.DataStructures.ItemStack addedItemStack) (at Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventoryCollections/ItemCollection.cs:430)
Opsive.UltimateInventorySystem.Core.InventoryCollections.ItemSlotCollection.SetItemAmount (Opsive.UltimateInventorySystem.Core.DataStructures.ItemInfo itemInfo, System.Int32 slotIndex, System.Boolean removePreviousItem) (at Assets/Opsive/UltimateInventorySystem/Scripts/Core/InventoryCollections/ItemSlotCollection.cs:264)
 
The Character Inventory Bridge is meant to awake before the InventoryItemSetManager does. That's normal.

I might have fixed this issue in my project but not in the released version.

Could you try that:
Code:
/// <summary>
/// Reequip the items if possible.
/// </summary>
/// <param name="categoryIndex">The category index.</param>
/// <param name="allowedSlotsLayerMask">The allowed slots layer mask.</param>
protected virtual void ReEquipItems(int categoryIndex, ref int allowedSlotsLayerMask)
{
    //An item is already equipped in the slot.
    if(ItemSetManager.ActiveItemSetIndex[categoryIndex] != -1){ return; }
    
    var previousItemSet = m_PreviousActiveSets.Active.Data[categoryIndex];

    ....

If that doesn't fix the issue for you let me know, I'll add another array bound check
 
The Character Inventory Bridge is meant to awake before the InventoryItemSetManager does. That's normal.

I might have fixed this issue in my project but not in the released version.

Could you try that:
Code:
/// <summary>
/// Reequip the items if possible.
/// </summary>
/// <param name="categoryIndex">The category index.</param>
/// <param name="allowedSlotsLayerMask">The allowed slots layer mask.</param>
protected virtual void ReEquipItems(int categoryIndex, ref int allowedSlotsLayerMask)
{
    //An item is already equipped in the slot.
    if(ItemSetManager.ActiveItemSetIndex[categoryIndex] != -1){ return; }
 
    var previousItemSet = m_PreviousActiveSets.Active.Data[categoryIndex];

    ....

If that doesn't fix the issue for you let me know, I'll add another array bound check

Hi. That matches the current release code. It is something else.

I think I have fixed it. My character was a prefab. I suspect that the InventoryItemSetManager was not saving the serializedfield m_CategoryItemSets, because it was always empty. I unpacked the Prefab and removed/added the InventoryItemSetManager, and it seems to be loading correctly. I need to do more tests. But it is late and I need to go to bed and just wanted to update you.

Also, I tried to recreate the ItemSetRules Object. I thought this might be corrupted. But it does not load properly. if you do this, the components on the character dissappear?

 
Last edited:
I'm not sure about the first problem you mentioned. As for the one you should in the video, you are right, that is a bug I never came across.

Here is the fix in the ItemSetRulesObject script:

Code:
/// <summary>
/// Initialize the collection.
/// <param name="force">Force the initialization.</param>
/// </summary>
public void Initialize(bool force)
{
    if (m_Initialized && !force) { return; }
    if (m_CategoryItemSetRules == null) {
        m_CategoryItemSetRules = new CategoryItemSetRule[0];
    }
    
    for (int i = 0; i < m_CategoryItemSetRules.Length; i++) {
        m_CategoryItemSetRules[i].Initialize(force);
    }
    
    m_Initialized = true;
}

I also made some minor fixes to the Inspector of that component.
In the ItemSetRulesObjectInspector script in the CategoryItemSetRulesReorderableList class replace the constructor with this:

Code:
/// <summary>
/// The constructor.
/// </summary>
/// <param name="title">The title.</param>
/// <param name="getObject">A function to get the object.</param>
/// <param name="setObjects">A function to set the object.</param>
public CategoryItemSetRulesReorderableList(string title, InventorySystemDatabase database,
    Func<CategoryItemSetRule[]> getObject,
    Action<CategoryItemSetRule[]> setObjects)
{
    m_InventorySystemDatabase = database;
    m_GetObject = getObject;
    m_SetObjects = setObjects;
    
    m_SelectionContainer = new VisualElement();
    m_SelectedCategoryItemSetRuleFields = new CategoryItemSetRuleField(m_InventorySystemDatabase);
    m_SelectedCategoryItemSetRuleFields.OnValueChanged += HandleSelectedItemSetRuleValueCharged;
    var array = m_GetObject.Invoke();
    m_List = array == null ? new List<CategoryItemSetRule>() : new List<CategoryItemSetRule>(array);
    m_ReorderableList = new ReorderableList(
        m_List,
        (parent, index) =>
        {
            var listElement = new CategoryItemSetRuleListElement(m_InventorySystemDatabase);
            listElement.Index = index;
            //listElement.OnValueChanged += OnCategoryItemCategorySetValueChanged;
            parent.Add(listElement);
        }, (parent, index) =>
        {
            var listElement = parent.ElementAt(0) as CategoryItemSetRuleListElement;
            listElement.Index = index;
            listElement.Refresh(m_ReorderableList.ItemsSource[index] as CategoryItemSetRule);
        }, (parent) =>
        {
            parent.Add(new Label(title));
        }, (index) => { return 25f; },
        (index) =>
        {
            //nothing
            m_SelectionContainer.Clear();
            m_SelectedCategoryItemSetRuleFields.Refresh(m_List[index]);
            m_SelectionContainer.Add(m_SelectedCategoryItemSetRuleFields);
        }, () =>
        {
            m_List.Add(new CategoryItemSetRule());
            m_SetObjects.Invoke(m_List.ToArray());
            Refresh();
            m_ReorderableList.SelectedIndex = m_List.Count - 1;
        }, (index) =>
        {
            if (index < 0 || index >= m_List.Count) { return; }
            m_List.RemoveAt(index);
            m_SetObjects.Invoke(m_List.ToArray());
            Refresh();
        }, (i1, i2) =>
        {
            var element1 = m_ReorderableList.ListItems[i1].ItemContents.ElementAt(0) as CategoryItemSetRuleListElement;
            element1.Index = i1;
            var element2 = m_ReorderableList.ListItems[i2].ItemContents.ElementAt(0) as CategoryItemSetRuleListElement;
            element2.Index = i2;
            m_SetObjects.Invoke(m_List.ToArray());
        });
    Add(m_ReorderableList);
    Add(m_SelectionContainer);
}

I hope that fixes your issue
 
Thank you very much :) That has fixed the ItemSetRulesObject.

I almost decided to start over and rebuild my inventory database and character from scratch with the new version of UIS and UCC. I was concerned that taking the database built with the last version and Character components, may not work well on the new version But, with your help it is now almost working.

My outstanding problems are the Melee Body is not defaulting on the character. And after picking up the Assualt rifle, it isn't loading nicely. Both were working ok, before the upgrade. I have gone through the training videos several times, but I must be missing some settings somewhere. I have uploaded a short video showing the main setup. Could you please take a quick look and see if you can see the problem or suggest where to look.

 
A few things have changed in the integration 2.X update.

One of those changes is having an abstraction between weapons in the inventory, weapons that are equippable and weapons that are equipped.

So first the item is added to the inventory when it is picked up. At that point the character doesn't know about the item. The reason being that you could have hundreds of items, we decided that the character shouldn't need to spawn and disable all these items on the character while they are in the main inventory.

To equip an item you must have them inside an Equippable Collection. You can "soft" equip an item by adding it to an Equippable collection without actually equipping it. This is useful to set an item in a holster for example.
When you drag the item from the Inventory Grid to the Equipment Grid you are moving the item to the Equippable Collection, so you are soft Equipping the assault Rifle, and not active equipping it.

To active Equip an item it must be in an Equippable Collection (aka it must be soft Equipped first). You can do so by scrolling with the mouse, or using an ItemAction.

So to get the functionality you want, you could automatically Equip the item when it is picked up. If you are using an InventoryItemPickup component you can select this option:
1623848895107.png

It seems you are using a custom script for pickup, so perhaps you can use this in code:
Code:
bridgeInventory.MoveEquip(itemInfo,true);
This will move the item from the Default Collection to one of the Equippable Collection and it will then active Equip it.

I hope that clears up most of your questions. There are more useful API documentation you can find on the Integration page:

As for the Body Item, I'm not sure why it is not equipping it by default. Since you set it in an Equippable Collection within the Inventory Inspector at edit time, it should be automatically Equipped.
It seems that it becomes Equippable only after you've added an Assault Rifle... Perhaps something is preventing the components to initialize in the correct order. Or something else might be off.

Try to set some Debug.Logs in the LoadDefaultLoadout function for the CharacterInventoryBridge, that's where I equip items that are in the Equippable Item Collections when the game starts. Hopefully that helps you identify why the Body Item isn't being equipped on start
 
Having that starting point for the LoadDefaultLoadout put me on the right track. It was a bug in my custom code that is called from the InventoryBase callout to m_NetworkCharacter.ItemIdentifierPickup. The code crashed out at this point and Unity was just eating the error and so it didn't show up in the console. I had to debug to find it.

Thank you very very much :)
 
Hello Sangemdoko,

I just wanted to add that I had the above problem just today, even after updating to the latest versions. I too had the "ReEquipItems" fix but not the "ItemSetRulesObject" fix. After applying that I stopped receiving the index out of bounds error, although I did have to rebuild my item set rules.

On a related subject, my character is auto equipping the first item added by default when I do not want this to happen. I have no auto equip options enabled anywhere, yet it auto equips.
This seems to happen in the "ReEquipItems" method on line 548 where it calls "EquipIfValid". I do not want it to do that, can you make it an option?

Thank you!
 
@RichardJaquish I'm not sure I understand your question about auto equipping. Could you make a new thread about it giving an example.
Is the item being auto equipped on pickup? Or is the item being auto equipped when the game starts? Is the item set in the Editor in an Equippable Item Collection at Edit time? Or are you adding this item Manually to the Equippable Item Collections at runtime?
 
Hi Sangdemoko, thank you for your reply!

I actually found a way to stop the auto equip. I created an item set rule with all slots having their category left as "None". That way it it created a set with no items equipped in any slot. I then enabled the "Default" option, causing it to auto equip the empty slot set and thus not equipping any items. Hopefully that makes sense.

Not that it matters now because I found a solution, but to answer your question, it happens when the items are taken from the "Loadout" item collection and put into the "Equippable" item collection at startup. Thus whenever I entered play mode, the character would have the first item equipped.

Thanks!
 
i'm using latest UIS 2.3.4, but this issue also bother me for a while.

my solution is: Apply prefab changes to prefab, all errors gone.

so, i guess it definitely related to serialization.
 
Top