实现一个简易的背包系统(相关数据定义部分):
一:数据定义部分
1:物品数据ItemData_SO:
包含了物品类型,物品名字,物品图标,图片消息描述以及物品是否可以堆叠。
//物品的数据信息
[CreateAssetMenu(fileName ="New Item",menuName ="Item/Item Data")]
public class ItemData_SO : ScriptableObject
{
public ItemType itemType; //物品类型
public string itemName; //物品名字
public Sprite itemIcon; //物品图标
[TextArea]
public string itemDescription; //物品详细描述
public bool statckable; //物品是否可以被堆叠
}
2:背包物品数据InventoryItem
包含了物品的数据以及物品的个数。
[System.Serializable]
public class InventoryItem
{
public ItemData_SO itemData;
public int itemAmount;
}
3:背包数据
包含了物品的列表,背包的容量,以及当前背包中的物品数量。
实现了AddItem(往背包中添加物品的方法):分为可堆叠和不可堆叠两种情况。
可堆叠:查找背包中是否有相同物体(根据ItemData判断),如果查找到了,直接使其的物品数量++即可。否则转入新创建的情况。
不可堆叠:遍历items,一旦找到了items里面的itemData为null的情况(指定位置上没有物品),则直接在该位置上创建新物品。
//背包的数据
[CreateAssetMenu(fileName ="New Inventory",menuName ="Inventory/Inventory Data")]
public class InventoyrData_SO : ScriptableObject
{
public List<InventoryItem> items;
public int itemCapacity; //当前背包的容量是多少
public int itemCount; //当前背包中一共有多少物品
//在当前背包中添加物品
public bool AddItem(ItemData_SO itemData)
{
if (itemCount == itemCapacity) return false;
//可堆叠的情况
if (itemData.statckable)
{
for (int i = 0; i < items.Count; i++)
{
//直接进行叠加操作
if (items[i].itemData == itemData)
{
items[i].itemAmount++;
return true;
}
}
}
//需要在背包中新添加这个物品
for (int i = 0; i < items.Count; i++)
{
if (items[i].itemData==null)
{
items[i].itemData = itemData;
items[i].itemAmount = 1;
itemCount++;
return true;
}
}
//没有添加成功,说明背包满了
return false;
}
}
二:UI显示部分
分为三层内容:Container(容器),SlotHolder(方格),ItemUI(图片物品)
1:ItemUI(物品UI):
包含其隶属于哪个背包和它所在的下标,以及itemUI的属性:ItemData物品数据,Image物品图标,amountText个数显示;
SetUpItemUI:更新方法,传入物品数据和物品个数,设置相应的UI图片显示。
public class ItemUI : MonoBehaviour
{
public InventoryData_SO Bag { get; set; } //物品所属与哪个背包
public int Index { get; set; } = -1; //物品在背包中的下标值
[HideInInspector] public ItemData_SO itemData;
[SerializeField] private Image itemIcon;
[SerializeField] private TextMeshProUGUI amountText;
private async void LoadSprite()
{
var spriteAssetReference = new AssetReference(itemData.serializeSprite.spriteAddress);
itemIcon.sprite = await spriteAssetReference.LoadAssetAsync<Sprite>().Task;
}
public void SetUpItemUI(ItemData_SO itemData, int amount)
{
if (amount == 0)
{
Bag.items[Index].itemData = null;
this.itemData = null;
itemIcon.gameObject.SetActive(false);
itemIcon.sprite = null;
return;
}
if (amount < 0) itemData = null;
if (itemData != null)
{
this.itemData = itemData;
LoadSprite();
amountText.text = amount.ToString();
itemIcon.gameObject.SetActive(true);
}
else
{
itemIcon.gameObject.SetActive(false);
}
}
#region 获取实际物品的接口(从背包数据库中获取)
public InventoryItem GetInventoryItem => Bag.items[Index];
public int GetItemAmount => Bag.items[Index].itemAmount;
//public ItemData_SO GetItemData => Bag.items[Index].itemData;
public ItemData_SO GetItemData => itemData;
#endregion
}
2:SlotHolder(存放物品UI的具体方格):
自身的属性有slotType(方格类型),itemUI(子物体图片UI)
UpdateItem:设置图片UI所属的背包(Switch选择语句),通过背包数据库找到ItemUI下标Index所指代的实际的物品,然后进行下行更新图片UI。
public class SlotHolder : MonoBehaviour
{
public SlotType slotType;
public ItemUI itemUI;
//通过SlotType获取对应的背包数据库 依次来更新其中的ItemUI
public void UpdateItem()
{
itemUI.Bag = slotType switch
{
SlotType.ConsumableBag => InventoryManager.Instance.consumableInventory,
SlotType.EquipmentBag => InventoryManager.Instance.euqipmentsInventory,
SlotType.PrimaryWeapon => InventoryManager.Instance.playerEuqipmentInventory,
SlotType.SecondaryWeapon => InventoryManager.Instance.playerEuqipmentInventory,
SlotType.Action => InventoryManager.Instance.actionInventory,
_ => null
};
//通过背包数据库 找到ItemUI下标对应的物品 对ItemUI进行更新
var item = itemUI.Bag.items[itemUI.Index];
itemUI.SetUpItemUI(item.itemData, item.itemAmount);
}
}
3:Container(存放所有SlotHolder的容器):
创建SlotHloder列表(可以在编辑器中赋值)。RefreshContainerUI:刷新操作,调用列表中所有SlotHloder的更新方法。
public class ContainerUI : MonoBehaviour
{
public SlotHolder[] slotHolders;
//刷新每个格子的内容 在获取物品的时候调用
public void RefreshContainerUI()
{
for(int i=0;i<slotHolders.Length;i++)
{
slotHolders[i].itemUI.Index = i;
slotHolders[i].UpdateItem();
}
}
}
InventoryManager:
内含所有的背包数据和背包容器,在Start时初始化状态。
public class InventoryManager : Singleton<InventoryManager>
{
[Header("背包数据")]
public InventoyrData_SO consumableInventory;
public InventoyrData_SO euqipmentsInventory;
public InventoyrData_SO playerEuqipmentInventory;
public InventoyrData_SO actionInventory;
[Header("背包容器")]
public ContainerUI consumableContainer; //消耗品背包
public ContainerUI equipmentsContainer; //装备背包
public ContainerUI actionContainer; //快捷栏上的背包
public ContainerUI playerEquipmentContainer; //人物正在装备的装备背包
private ContainerUI currentShowContainer; //当前选择的背包 (指角色面板中的背包选项,不包括人物正在穿的装备)
[Header("内容组件")]
public TextMeshProUGUI titleText;
[Header("拖拽相关")]
public Canvas dragCanvas;
public class DragData
{
public SlotHolder orignalSlot;
public RectTransform orignalParent;
}
public DragData currentDrag;
public ContainerUI CurrentShowContainer {
get => currentShowContainer;
set
{
currentShowContainer = value;
if (value == consumableContainer) SelectConsumableContainer();
else if (value == equipmentsContainer) SelectEuqipmentsContainer();
}
}
private void Start()
{
CurrentShowContainer = consumableContainer;
CurrentShowContainer.RefreshContainerUI();
equipmentsContainer.RefreshContainerUI();
actionContainer.RefreshContainerUI();
playerEquipmentContainer.RefreshContainerUI();
}
}