包含世界物品的拾取,UI物品的拖拽,交换(后续扩充额外内容:使用,掉落等等)
1:物品拾取 ItemPickUp:为每个世界上的物品添加此脚本和Trigger碰撞体
根据物体类型选择加入到哪个背包,附带刷新背包+刷新物品+删除世界物品操作。
public class ItemPickUp : MonoBehaviour
{
public ItemData_SO itemData;
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
//往背包中加入物品 + 刷新背包 + 删除物品 (判断背包是否已满)
bool add = false;
switch (itemData.itemType)
{
case ItemType.Consumable:
add = InventoryManager.Instance.consumableInventory.AddItem(itemData);
InventoryManager.Instance.consumableContainer.RefreshContainerUI();
break;
case ItemType.PrimaryWeapon:
case ItemType.SecondaryWeapon:
add = InventoryManager.Instance.euqipmentsInventory.AddItem(itemData);
InventoryManager.Instance.equipmentsContainer.RefreshContainerUI();
break;
}
if (add) Destroy(gameObject);
}
}
}
2:物品的拖拽,交换功能
相关内容定义:在InventoryManager中定义拖拽相关功能
每次拖动其实是拖动方格SlotHolder下的图片ItemUI,我们将要拖动的ItemUI单独放在一个DragCanvas下(实现在其他UI的上面突出显示)。
dragOriginalSlot定义了拖拽的原始信息:被拖拽物品的原始方格。
[Header("拖拽相关")]
public Canvas dragCanvas;
public SlotHolder dragOriginalSlot;
DragItem类:挂载在ItemUI上
要点:使用三个接口 IBeginDragHandler, IDragHandler, IEndDragHandler实现拖拽功能。
类中数据和Awake初始化
private ItemUI currentItemUI;
private SlotHolder currentSlot;
private SlotHolder targetSlot;
private void Awake()
{
currentItemUI = GetComponent<ItemUI>();
currentSlot = GetComponentInParent<SlotHolder>();
}
1:IBeginDragHandler:开始拖拽,设置原始方格,设置父物体到 Drag Canvas上。
public void OnBeginDrag(PointerEventData eventData)
{
//记录原始数据
InventoryManager.Instance.dragOriginalSlot = currentSlot;
transform.SetParent(InventoryManager.Instance.dragCanvas.transform);
}
2:IDragHandler:拖拽中途,设置transform的位置为鼠标的位置即可。
public void OnDrag(PointerEventData eventData)
{
//跟随鼠标位置移动
transform.position = eventData.position;
}
3:IEndDragHandler:结束拖拽,首先检测下方是否是UI物体,再通过CheckSlot()函数判断鼠标位置是否指向了一个SlotHolder。如果都通过了的话,设置targetSlot(注意,如果对应方格上没有物品,则Slot在最上方,否则ItemUI在最上方),接着检查targetSlot的类型,如果类型匹配,则进行交换Swap(交换的是内含的InventoryItem)操作。完成之后进行更新两个方格,并且将当前DragItem重新回到原始方格,并且设置anchor(防止没有准确放回原位)。
Check函数:遍历所有指定背包中的所有方格Slot即可。
具体的检测方法使用到了RectTransformUtility.RectangleContainsScreenPoint 方法,可以判断指定的位置是否在某一RectTransform内。
#region 检查当前鼠标位置是否位于一个SlotHoler之内 (用于实现拖拽和交换)
public bool CheckSlot(Vector3 position)
{
bool mouseInSlot = false;
mouseInSlot |= CheckInSpecifiedInventory(actionContainer, position);
mouseInSlot |= CheckInSpecifiedInventory(playerEquipmentContainer, position);
if (currentShowContainer.gameObject.activeInHierarchy)
mouseInSlot |= CheckInSpecifiedInventory(currentShowContainer, position);
return mouseInSlot;
}
private bool CheckInSpecifiedInventory(ContainerUI specifiedInventory,Vector3 position)
{
for(int i=0;i< specifiedInventory.slotHolders.Length;i++)
{
RectTransform t = specifiedInventory.slotHolders[i].transform as RectTransform;
if (RectTransformUtility.RectangleContainsScreenPoint(t, position))
return true;
}
return false;
}
#endregion
public void OnEndDrag(PointerEventData eventData)
{
//放下物品 交换数据
//判断鼠标下方是否指向的是UI物体
if(EventSystem.current.IsPointerOverGameObject())
{
if(InventoryManager.Instance.CheckSlot(eventData.position))
{
if (eventData.pointerEnter.gameObject.GetComponent<SlotHolder>())
targetSlot = eventData.pointerEnter.gameObject.GetComponent<SlotHolder>();
else
targetSlot = eventData.pointerEnter.gameObject.GetComponentInParent<SlotHolder>();
//根据目标格子的类型进行交换
switch (targetSlot.slotType)
{
case SlotType.ConsumableBag:
if (currentItemUI.itemData.itemType == ItemType.Consumable) SwapItem();
break;
case SlotType.EquipmentBag:
if (currentItemUI.itemData.itemType != ItemType.Consumable) SwapItem();
break;
case SlotType.PrimaryWeapon:
if(currentItemUI.itemData.itemType== ItemType.PrimaryWeapon) SwapItem();
break;
case SlotType.SecondaryWeapon:
if (currentItemUI.itemData.itemType == ItemType.SecondaryWeapon) SwapItem();
break;
case SlotType.Action:
if (currentItemUI.itemData.itemType == ItemType.Consumable) SwapItem();
break;
}
currentSlot.UpdateItem();
targetSlot.UpdateItem();
}
}
transform.SetParent(InventoryManager.Instance.dragOriginalSlot.transform);
RectTransform t = transform as RectTransform;
t.offsetMax = Vector2.zero;
t.offsetMin = Vector2.zero;
}
4:Swap:Swap交换的是背包数据库InventorItem数据(要处理自我交换的情况),通过Slot下的ItemUI下的背包数据库以及下标获取对应的实际物品,对于可堆叠和不可堆叠的情况分开处理。
private void SwapItem()
{
//对自我交换进行处理
if (targetSlot == currentSlot) return;
//得到背包中实际的物品
InventoryItem targetItem = targetSlot.itemUI.GetItemByItemUI();
InventoryItem tmpItem = currentSlot.itemUI.GetItemByItemUI();
//处理可叠加的情况 (同一种物品 + 物品可堆叠 )
if(targetItem.itemData==tmpItem.itemData && targetItem.itemData.statckable)
{
targetItem.itemAmount += tmpItem.itemAmount;
tmpItem.itemData = null; tmpItem.itemAmount = 0;
}
//不可叠加 进行交换
else
{
targetSlot.itemUI.Bag.items[targetSlot.itemUI.Index] = tmpItem;
currentSlot.itemUI.Bag.items[currentSlot.itemUI.Index] = targetItem;
}
}