Step 1: Any place in Inventory.cs that calls InventoryUpdated should also call InventorySlotUpdated(slot) (or i, or whatever index of the slot that just changed is).
Step 2: Add the following property to IInventorySource
bool IsPhysical => true;
By including the autoimplementation of the property (true), we ensure that we don’t need to actually override this in Inventory, because when there is a default implementation in an Interface, there is no need to override it to to fulfill the contract.
However, in PickupFinder, we want to override this
public overrided bool IsPhysical => false;
Now the Bank and the Inventory will be Physical, the PickupCollector won’t be.
This is because we want two different behaviours to happen based on the location…
The rest of this is handled in InventoryUI and InventorySlotUI. I pulled the ObjectPooling out, because it really won’t be saving much clock for the additional complexity.
InventoryUI.cs
using UnityEngine;
using GameDevTV.Inventories;
using TMPro;
namespace GameDevTV.UI.Inventories
{
/// <summary>
/// To be placed on the root of the inventory UI. Handles spawning all the
/// inventory slot prefabs.
/// </summary>
public class InventoryUI : MonoBehaviour
{
// CONFIG DATA
[SerializeField] InventorySlotUI InventoryItemPrefab = null;
[SerializeField] bool isPlayerInventory = true;
[SerializeField] TextMeshProUGUI Title;
[SerializeField] private Transform physicalPool;
// CACHE
IInventorySource selectedInventory;
// LIFECYCLE METHODS
private void Awake()
{
if (isPlayerInventory)
{
selectedInventory = Inventory.GetPlayerInventory();
}
}
private void Start()
{
if(isPlayerInventory)
{
Redraw();
}
}
// PRIVATE
private void OnEnable()
{
Redraw();
}
private void Redraw()
{
if (selectedInventory==null) return;
foreach (Transform child in transform)
{
Destroy(child.gameObject);
}
for (int i = 0; i < selectedInventory.GetSize(); i++)
{
var itemUI = Instantiate(InventoryItemPrefab, transform);
itemUI.transform.SetParent(transform);
itemUI.Setup(selectedInventory, i);
}
}
public bool Setup(GameObject user)
{
if (selectedInventory is { IsPhysical: false })
{
selectedInventory.inventoryUpdated -= Redraw;
}
if (user.TryGetComponent(out selectedInventory))
{
if (!selectedInventory.IsPhysical)
{
selectedInventory.inventoryUpdated += Redraw;
}
Title.text = selectedInventory.GetDisplayName();
Redraw();
return true;
}
return false;
}
}
}
In Awake(), we’re first checking to see if we’re the Player’s inventory and setting the selectedInventory appropriately. We’re NOT subscribing to InventoryUpdated.
In Start(), we Redraw if we’re the Player Inventory, to get everything set up.
In Setup, if we’re NOT a physical inventory, then we subscribe to the inventoryUpdated, if we are a physical Inventory (bank), we don’t. Then, since we’re just setting up, we force a full Redraw, because either way, when we first open the other window, we want to Redraw.
So we’ve now set up updating the PickupFinder inventory when changes are made. Because it is dynamic, simply refreshing slots is a bad idea and will cause problems, so updating every time something in PickupFinder changes is the way to go.
Next, we head into InventorySlotUI.cs. I’ll just include the changed and added methods, because my version of the script does not contain the Top Ramen code that is the click handling.
public void Setup(IInventorySource inventory, int index)
{
this.inventory = inventory;
this.index = index;
Redraw();
if (inventory.IsPhysical)
{
inventory.GetGameObject().GetComponent<Inventory>().OnInventorySlotUpdated +=
Inventory_OnInventorySlotUpdated;
}
}
void OnDestroy()
{
if (inventory is { IsPhysical: true })
{
inventory.GetGameObject().GetComponent<Inventory>().OnInventorySlotUpdated -=
Inventory_OnInventorySlotUpdated;
}
}
private void Inventory_OnInventorySlotUpdated(int slot)
{
if (slot == index) Redraw();
}
private void Redraw()
{
icon.SetItem(inventory.GetItemInSlot(index), inventory.GetNumberInSlot(index));
}
So in this case, we’re checking to see if we ARE physical. In the event that we are, then we subscribe to the InventorySlotUpdated method. We factored out the Icon change to be a Redraw method, and our handler checks to see if the slot matches and Redraws if appropriate.
OnDestroy() is added, and the Inventory is checked to see if it is physical. If it is, we unsubscribe from the event.