Well, I made an attempt, but fell short in a few places. Going by this and this guide, I tried to create a Factory that has ReturnSlots() and GetSlots() and operate the queue from there. The main class would be abstract and let different ProductCreators override, but I wasn’t able to figure out how to declare the main class as abstract, since Unity then wouldn’t let me attach it. In the end, I think this ended up being a short spawning class more than anything else.
Video of performance, works pretty well until you go to high slot values:
Changes to the Inventory.cs:
Summary
public int GetSize()
{
// If inventory size has changed, remake the inventory array
if(slots.Length != inventorySize)
{
print(slots.Length + " slots with inv size " + inventorySize);
InventoryItem[] recalcSlots = new InventoryItem[inventorySize];
int upperBound = Math.Min(slots.Length, inventorySize);
for (int i = 0; i < upperBound; i++)
{
recalcSlots[i] = slots[i];
}
slots = recalcSlots;
}
return slots.Length;
}
New UIFactory script, not sure which namespace this should belong to and which object it should live on in the scene:
Summary
public class UIFactory : MonoBehaviour
{
[SerializeField] InventorySlotUI inventoryItemPrefab = null;
public Queue<InventorySlotUI> SlotsInQueue = new Queue<InventorySlotUI>();
public void ReturnSlots(GameObject objSlotUI)
{
var slotUI = objSlotUI.GetComponent<InventorySlotUI>();
if (slotUI != null)
{
objSlotUI.transform.SetParent(transform, true);
objSlotUI.SetActive(false);
SlotsInQueue.Enqueue(slotUI);
}
else
{
Destroy(objSlotUI);
}
}
public InventorySlotUI GetSlotUI()
{
if (SlotsInQueue.Count > 0)
{
InventorySlotUI slotUI = SlotsInQueue.Dequeue();
slotUI.gameObject.SetActive(true);
return slotUI;
}
InventorySlotUI newSlotUI = Instantiate(inventoryItemPrefab, transform);
return newSlotUI;
}
}
InventoryUI.cs changes:
Summary
// CONFIG DATA
// [SerializeField] InventorySlotUI InventoryItemPrefab = null;
[SerializeField] UIFactory factory = null;
private void Redraw()
{
// Stepping backwards to make sure none are missed...
int children = transform.childCount;
for (int i = children - 1; i > -1; i--)
{
factory.ReturnSlots(transform.GetChild(i).gameObject);
}
// wasn't able to find a better way to get the slots in the correct order for some reason
for (int i = playerInventory.GetSize() - 1; i > -1; i--)
{
var itemUI = factory.GetSlotUI();
itemUI.transform.SetParent(transform, true);
itemUI.transform.SetAsFirstSibling();
itemUI.Setup(playerInventory, i);
}
}
The inventoryUI.cs ended up causing the biggest challenge. When using foreach(Transform child in transform), it was skipping children when detaching the gameobjects back to the factory. And then to get the slots to mostly be in the right order, I had to step backward through the playerInventory, as well. Somewhere along the line, I created a duping bug when you swap and change inventory sizes, not sure what causes that.
This was interesting to try out, even though I don’t think I made a true factory pattern. I’ll definitely be on the lookout for good tutorials of where to use this when I have object types that share a lot of attributes. For now, I think my game will have a fixed inventory size.
Edit: I see where I went wrong with the abstract classes. I don’t need any overlap with MonoBehaviour in the Factory base class or the subclasses of it. Now the question is where those classes and subclasses should go.
Edit 2: Got it working as an abstract class within InventoryUI.cs, but now I’m running into a couple of different issues with inheriting.
- If the abstract class does not inherit MonoBehaviour, Instantiate() is not available. So, the class must (?) inherit from MonoBehaviour.
- If it does inherit, then it’s not able to get created with the “new” keyword while on InventoryUI.cs. The effect of this is that it doesn’t have a transform of its own and the DragItem.cs Awake functions don’t cache properly, which makes the dragging not layer correctly.
This seems closer than my last attempt in terms of the format, but I’m obviously still missing some important pieces to get it functioning as intended.
Summary
namespace GameDevTV.UI.Inventories
{
public class InventoryUI : MonoBehaviour
{
// CONFIG DATA
[SerializeField] InventorySlotUI InventoryItemPrefab = null;
[SerializeField] UIFactory factory = null;
// CACHE
Inventory playerInventory;
// LIFECYCLE METHODS
private void Awake()
{
playerInventory = Inventory.GetPlayerInventory();
playerInventory.inventoryUpdated += Redraw;
factory = new InvUIFactory(InventoryItemPrefab);
}
private void Start()
{
Redraw();
}
// PRIVATE
private void Redraw()
{
int children = transform.childCount;
for (int i = children - 1; i > -1; i--)
{
factory.ReturnSlots(transform.GetChild(i).gameObject);
}
for (int i = playerInventory.GetSize() - 1; i > -1; i--)
{
var itemUI = factory.GetSlotUI();
itemUI.transform.SetParent(transform, true);
itemUI.transform.SetAsFirstSibling();
itemUI.transform.localScale = Vector3.one;
itemUI.Setup(playerInventory, i);
}
}
}
public abstract class UIFactory : MonoBehaviour
{
public abstract void ReturnSlots(GameObject obj);
public abstract InventorySlotUI GetSlotUI();
}
public class InvUIFactory : UIFactory
{
[SerializeField] InventorySlotUI inventoryItemPrefab = null;
public Queue<InventorySlotUI> SlotsInQueue = new Queue<InventorySlotUI>();
public InvUIFactory(InventorySlotUI prefab)
{
inventoryItemPrefab = prefab;
}
public override void ReturnSlots(GameObject objSlotUI)
{
var slotUI = objSlotUI.GetComponent<InventorySlotUI>();
if (slotUI != null)
{
// this factory has no transform
// objSlotUI.transform.SetParent(transform, true);
objSlotUI.SetActive(false);
SlotsInQueue.Enqueue(slotUI);
}
else
{
Destroy(objSlotUI);
}
}
public override InventorySlotUI GetSlotUI()
{
if (SlotsInQueue.Count > 0)
{
InventorySlotUI slotUI = SlotsInQueue.Dequeue();
slotUI.gameObject.SetActive(true);
return slotUI;
}
InventorySlotUI newSlotUI = Instantiate(inventoryItemPrefab);
return newSlotUI;
}
}
}