Refactoring the Click Handler In InventorySlotUI
First up, we're going to make a small improvement to the click handling, taking advantage of information passed into the PointerEventData, specifically the LastUIClicked.
If you haven't done so, make sure that the InventorySlotUI implements the IPointerClickHandler interface.
OnPointerClick
// This is static, so that any time an InventorySlotUI is clicked, this
// value will be updated.
private static InventorySlotUI LastUIClicked;
public void OnPointerClick(PointerEventData eventData)
{
if (LastUIClicked != this)
{
LastUIClicked = this;
Invoke(nameof(TimesUp), .5f); //Fine tune this value to taste
Debug.Log($"{index} was clicked once.");
}
else
{
HandleDoubleClick();
}
}
private void TimesUp()
{
if (LastUIClicked == this) LastUIClicked = null;
}
So this new OnPointerClickMethod first checks to see if the LastUIClicked is this UI. If it’s not, which will be the case whenever we click on a new InventorySlotUI, then the static LastUIClicked is set to this, and we invoke a timer TimesUp().
Otherwise, we call HandleDoubleClicked.
TimesUp simply checks to see if we’re the LastUIClicked, and if we are, it clears the LastUIClicked.
HandleDoubleClick
Our HandleDoubleClicked() method has to deal with three distinct states:
* We are the Player Inventory and the OtherInventory is open
* We are the Player Inventory and the OtherInventory is closed
* We are the OtherInventory
private void HandleDoubleClick()
{
TimesUp();//Prevents triple click from starting another HandleDoubleClick
InventoryItem item = inventory.GetItemInSlot(index);
int number = inventory.GetNumberInSlot(index);
if (item == null || number<1 ) return; //Nothing to do
if (inventory.gameObject.CompareTag("Player"))
{
var otherInventoryUI =
FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory); //will return the other Inventory or null
//Check to see if it's not null (should never happen), if it's Active, and if the otherInventory's inventory is valid.
if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory!=null)
{
Inventory otherInventory = otherInventoryUI.SelectedInventory;
TransferToOtherInventory(otherInventory, item, number);
}
else if(item is EquipableItem equipableItem && inventory.TryGetComponent(out Equipment equipment))
{
EquipItem(equipableItem, equipment, number);
}
}
else //if(!inventory.gameObject.CompareTag("Player") means we're the other inventory.
{
TransferToOtherInventory(Inventory.GetPlayerInventory(), item, number);
}
}
We start by calling TimesUp(). This simply clears the LastItemClicked to prevent triple clicks from calling this method again. (You could theoretically click four times really really fast, but if a player does that, they might just deserve to lose the item or some other weirdness).
Next, we get the item and number (rather than constantly calling for it) and make sure that we actually have an item in the first place. If we don’t have an item, then we don’t want to do anything, just exit.
Next we check to see if we’re the player. If we are the player, then we have two possible states:
- The otherInventoryUI is open
- The otherInventoryUI is not open.
So we find the otherInventoryUI, and make sure that it’s active and that a valid inventory is linked to it.
To facilitate this, we have to add two properties to InventoryUI.cs
public bool IsOtherInventory => !isPlayerInventory;
public Inventory SelectedInventory => selectedInventory;
This exposes the isPlayerInventory and selectedInventory so that our search can work, and we can get a link to the inventory that the otherInventory is pointing to.
Note that we’re using a Linq expression FirstOrDefault()
, so you’ll need to add
using System.Linq;
to your usings clause in InventorySlotUI.cs
If we have a valid OtherInventoryUI and otherInventory, then we try to transfer the item to the other inventory. I’ll show that method shortly.
If OtherInventoryUI and otherInventory are not valid, then we see if we can Equip the item. I’ll show that method shortly as well.
Finally, if we’re not the Player Inventory then we must be the OtherInventory, so we attempt a Transfer from this InventorySlot to the Player’s Inventory.
TransferToOtherInventory
private void TransferToOtherInventory(Inventory otherInventory, InventoryItem item, int number)
{
if (otherInventory.HasSpaceFor(item))
{
otherInventory.AddToFirstEmptySlot(inventory.GetItemInSlot(index),
inventory.GetNumberInSlot(index));
inventory.RemoveItem(item, number);
Setup(inventory, item);
}
}
This is relatively straightforward. I made this a separate method to both reduce the clutter in HandleDoubleClick, and to make a method that would work for EITHER inventory => otherInventory or the other way round.
First, we check to make sure we can transfer the inventory in the first place with otherInventory.HasSpaceFor. If it does, then it’s a simple matter of adding the item to the other inventory and removing the item from our inventory.
The reason this works for either the player’s inventory or the other inventory is how we pass the parameters. If we’re the player’s inventory, then we pass the otherInventory to the method. In this case, the global inventory points to the player’s inventory. But, if we’re the otherInventory, then we pass the player’s inventory to the method, because inventory points to the other inventory.
EquipItem
private void EquipItem(EquipableItem equipableItem, Equipment equipment, int number)
{
if (equipableItem.CanEquip(equipableItem.GetAllowedEquipLocation(), equipment))
{
EquipableItem otherEquipableItem = equipment.GetItemInSlot(equipableItem.GetAllowedEquipLocation());
equipment.RemoveItem(equipableItem.GetAllowedEquipLocation());
inventory.RemoveFromSlot(index, number);
equipment.AddItem(equipableItem.GetAllowedEquipLocation(), equipableItem);
if (otherEquipableItem != null)
{
inventory.AddItemToSlot(index, otherEquipableItem, 1);
}
Setup(inventory, index);
}
}
This method is only called if we’re the player’s inventory, and the other inventory is not active, and this is an equipable item.
In this case, we make sure that the item is equipable (if you haven’t taken Shops and Abilities, you should remove this check!). If it is, then we get whatever is in the equipment slot, add our equipableItem to the Equipment slot, and if there was an item already in the equipment, we put in this spot. Simple exchange.
And that’s it, click to transfer as well as click to equip.
OOPS
Quick fix, in my initial code, I didn't refresh the UI, so if you double clicked, it LOOKED like the item was still in the origina location as well as the transferred one. Of course, if you tried to drag that item, you'd get a null reference error, and if you tried to double click, nothing would happen, because all that's really going on is the icon didn't get cleared.
The simple fix for this (which is already applied if you haven't done the code yet) is to add
Setup(inventory, index);
to TransferToOtherInventory() and EquipItem() after the item has been removed from the inventory.