【Unity实战】从零手戳一个库存背包系统

文章目录

  • 前言
  • 素材
  • 开始
    • 一、绘制背包UI
    • 二、背包开启关闭
    • 三、初始化背包网格
    • 四、 添加物品
    • 五、 拖拽交换功能物品
    • 六、 物品拆分
    • 七、 物品堆叠
    • 八、 拖拽还原
    • 九、 引入字典存储数据
    • 十、 拾取物品
    • 十一、 丢弃物品
  • 最终效果
  • 源码
  • 完结

前言

库存背包系统是大多数游戏的关键部分,几乎在每种类型的游戏都可能会用到,今天我将带你从零实现一个能够进行拖放的库存拆分、堆叠和丢弃的背包系统,你可以将它轻松的移植到你的游戏中。

老粉丝应该知道我之前有做过一个背包系统,当时做的比较简单,很多功能都没有实现,所以这次算一个重置版,当然你有兴趣可以看看之前实现的背包系统:https://blog.csdn.net/qq_36303853/article/details/129962414

先来看看实现的最终效果
【Unity实战】从零手戳一个库存背包系统_第1张图片

素材

【Unity实战】从零手戳一个库存背包系统_第2张图片




【Unity实战】从零手戳一个库存背包系统_第3张图片

开始

一、绘制背包UI

【Unity实战】从零手戳一个库存背包系统_第4张图片
物品列表网格添加Grid Layout Group组件控制物品格子排序
【Unity实战】从零手戳一个库存背包系统_第5张图片
效果
【Unity实战】从零手戳一个库存背包系统_第6张图片

二、背包开启关闭

物品槽信息类,用于存储每个物品槽的信息

// 物品槽信息类,用于存储每个物品槽的信息
[System.Serializable]
public class ItemSlotInfo
{
    public Item item;// 物品对象
    public string name;// 物品名称
    public int stacks;// 堆叠数量
    public ItemSlotInfo(Item newItem, int newstacks)
    {
        item = newItem;
        stacks = newstacks;
    }
}

库存系统

using System.Collections.Generic;
using UnityEngine;

public class Inventory : MonoBehaviour
{
    [SerializeReference] public List<ItemSlotInfo> items = new List<ItemSlotInfo>();// 物品列表
    [Space]
    [Header("库存菜单组件")]
    public GameObject inventoryMenu;// 背包菜单
    public GameObject itemPanel;// 物品列表容器
    public GameObject itemPanelGrid;// 物品列表网格布局

    [Space]
    public int inventorySize = 24;// 背包容量大小
    void Start()
    {
        // 初始化物品列表并赋值为 null
        for (int i = 0; i < inventorySize; i++)
        {
            items.Add(new ItemSlotInfo(null, 0));
        }
    }

    void Update()
    {
        // 按下 Tab 键显示或隐藏背包界面
        if (Input.GetKeyDown(KeyCode.Tab))
        {
            if (inventoryMenu.activeSelf)
            {
                inventoryMenu.SetActive(false);
                Cursor.lockState = CursorLockMode.Locked;// 隐藏光标并锁定在屏幕中心
            }
            else
            {
                inventoryMenu.SetActive(true);
                Cursor.lockState = CursorLockMode.Confined;// 显示光标并限制在屏幕内部移动
            }
        }
    }
}

挂载Inventory 脚本在canvas,配置数据
【Unity实战】从零手戳一个库存背包系统_第7张图片

运行效果

三、初始化背包网格

新建物品容器脚本

using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class ItemPanel : MonoBehaviour
{
    public Inventory inventory;// 背包脚本引用
    public ItemSlotInfo itemSlot;// 物品槽信息
    public Image itemImage;// 物品图像组件
    public TextMeshProUGUI stacksText;// 堆叠数量文本组件
}

挂载代码
【Unity实战】从零手戳一个库存背包系统_第8张图片

新建物品抽象类,所有物品类型都需要继承此类

using UnityEngine;
//物品抽象类,所有物品类型都需要继承此类
[System.Serializable]
public abstract class Item
{
    public abstract string GiveName();// 获取物品名称
    public virtual int MaxStacks()// 获取每个物品槽的最大堆叠数量
    {
        return 30;// 默认为 30
    }
    public virtual Sprite GiveItemImage()// 获取物品图片
    {
        return Resources.Load<Sprite>("UI/Item Images/No Item Image Icon");// 默认图片
    }
}

Inventory新增刷新背包功能,每次打开背包时调用RefreshInventory();

private List<ItemPanel> existingPanels = new List<ItemPanel>();//物品容器列表

//刷新背包
public void RefreshInventory()
{
    //物品容器列表
    existingPanels = itemPanelGrid.GetComponentsInChildren<ItemPanel>().ToList();
    //如果物品列表容器不足,创建物品面板
    if (existingPanels.Count < inventorySize)
    {
        int amountToCreate = inventorySize - existingPanels.Count;
        for (int i = 0; i < amountToCreate; i++)
        {
            GameObject newPanel = Instantiate(itemPanel, itemPanelGrid.transform);
            existingPanels.Add(newPanel.GetComponent<ItemPanel>());
        }
    }
    int index = 0;
    foreach (ItemSlotInfo i in items)
    {
        //给物品列表元素命名
        i.name = "" + (index + 1);
        if (i.item != null) i.name += ":" + i.item.GiveName();
        else i.name += ":-";
        //更新物品面板
        ItemPanel panel = existingPanels[index];
        panel.name = i.name + " Panel";
        if (panel != null)
        {
            panel.inventory = this;
            panel.itemSlot = i;
            if (i.item != null)
            {
                panel.itemImage.gameObject.SetActive(true);  // 显示物品图标
                panel.itemImage.sprite = i.item.GiveItemImage();  // 设置物品图标的精灵
                panel.stacksText.gameObject.SetActive(true);  // 显示物品叠加数量
                panel.stacksText.text = "" + i.stacks;  // 设置物品叠加数量的文本
            }
            else
            {
                panel.itemImage.gameObject.SetActive(false);  // 隐藏物品图标
                panel.stacksText.gameObject.SetActive(false);  // 隐藏物品叠加数量
            }
        }
        index++;
    }
}

效果

四、 添加物品

Inventory实现添加和清除物品功能

//添加物品
public int AddItem(Item item, int amount)
{
    // 检查已有物品槽中是否有空余位置
    foreach (ItemSlotInfo i in items)
    {
        if (i.item != null)
        {
            if (i.item.GiveName() == item.GiveName())
            {
                if (amount > i.item.MaxStacks() - i.stacks)
                {
                    amount -= i.item.MaxStacks() - i.stacks;
                    i.stacks = i.item.MaxStacks();
                }

                else
                {
                    i.stacks += amount;
                    //如果背包菜单处于激活状态,刷新背包显示
                    if (inventoryMenu.activeSelf) RefreshInventory();
                    return 0;
                }

            }
        }

    }

    //将剩余的物品放入空的物品槽中
    foreach (ItemSlotInfo i in items)
    {
        if (i.item == null)
        {
            if (amount > item.MaxStacks())
            {
                i.item = item;
                i.stacks = item.MaxStacks();
                amount -= item.MaxStacks();
            }
            else
            {
                i.item = item;
                i.stacks = amount;
                //如果背包菜单处于激活状态,刷新背包显示
                if (inventoryMenu.activeSelf) RefreshInventory();
                return 0;
            }

        }

    }
    Debug.Log("库存中没有空间:" + item.GiveName());
    //如果背包菜单处于激活状态,刷新背包显示
    if (inventoryMenu.activeSelf) RefreshInventory();
    return amount;
}

//清空指定物品槽中的物品和叠加数量
public void ClearSlot(ItemSlotInfo slot)
{
    slot.item = null;
    slot.stacks = 0;
}

新增第一个物品,木头脚本,继承Item基类,重新对应参数

using UnityEngine;
public class WoodItem : Item
{
    public override string GiveName()// 获取物品名称
    {
        return "Wood";
    }
    public override int MaxStacks()// 获取每个物品槽的最大堆叠数量
    {
        return 10;
    }
    public override Sprite GiveItemImage()// 获取物品图片
    {
        return Resources.Load<Sprite>("UI/Item Images/Wood Icon");
    }
}

添加40个木头进库测试

public class Inventory : MonoBehaviour
{
    void Start()
    {
        // 。。。
        AddItem(new WoodItem(), 40);
    }
}

效果
【Unity实战】从零手戳一个库存背包系统_第9张图片

五、 拖拽交换功能物品

先实现指针跟随鼠标,新增脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class Mouse : MonoBehaviour
{
    public GameObject mouseItemUI; // 鼠标上的物品UI
    public Image mouseCursor; // 鼠标光标
    public ItemSlotInfo itemSlot; // 物品槽信息
    public Image itemImage; // 物品图像
    public TextMeshProUGUI stacksText; // 叠加数量文本

    void Update()
    {
        // 将鼠标指针位置设置为当前鼠标位置
        transform.position = Input.mousePosition;
        
        // 如果鼠标被锁定
        if (Cursor.lockState == CursorLockMode.Locked)
        {
            mouseCursor.enabled = false; // 隐藏鼠标光标
            mouseItemUI.SetActive(false); // 隐藏鼠标上的物品UI
        }
        else
        {
            mouseCursor.enabled = true; // 显示鼠标光标
            
            // 如果物品槽中有物品
            if (itemSlot.item != null)
            {
                mouseItemUI.SetActive(true); // 显示鼠标上的物品UI
            }
            else
            {
                mouseItemUI.SetActive(false); // 隐藏鼠标上的物品UI
            }
        }
    }
    public void SetUI()
    {
        stacksText.text = "" + itemSlot.stacks; // 设置叠加数量文本
        itemImage.sprite = itemSlot.item.GiveItemImage(); // 设置物品图像
    }
    public void EmptySlot()
    {
        itemSlot = new ItemSlotInfo(null, 0);// 清空物品槽
    }
}

新增拖拽,跟随鼠标显示的UI
【Unity实战】从零手戳一个库存背包系统_第10张图片
效果,就和一个格子效果一致
【Unity实战】从零手戳一个库存背包系统_第11张图片
给Mouse挂载mouse脚本,并添加Canvas Group组件,将这个组件的blocksRaycasts属性设置为false,表示在我们刚开始拖拽的整个过程当中,鼠标不会再去把这个UI物品当作一个阻挡物来看待,包括他的子物体的所有的UI对象
【Unity实战】从零手戳一个库存背包系统_第12张图片
ps:当然,选择去除各个子组件Image里的射线投射目标,也能实现相同的效果
【Unity实战】从零手戳一个库存背包系统_第13张图片

记得默认先隐藏mouse下的物品列表容器
【Unity实战】从零手戳一个库存背包系统_第14张图片
修改ItemPanel脚本实现物品拖拽和交换

using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.EventSystems;

public class ItemPanel : MonoBehaviour, IPointerEnterHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler, IDropHandler
{
    public Inventory inventory; // 背包脚本引用
    public ItemSlotInfo itemSlot; // 物品槽信息
    public Image itemImage; // 物品图像组件
    public TextMeshProUGUI stacksText; // 堆叠数量文本组件
    private bool click; // 当前是否点击
    private Mouse mouse; // 鼠标

    // 当鼠标进入时调用的方法
    public void OnPointerEnter(PointerEventData eventData)
    {
        eventData.pointerPress = this.gameObject;
    }

    // 当鼠标按下时调用的方法
    public void OnPointerDown(PointerEventData eventData)
    {
        click = true;
    }

    // 当鼠标抬起时调用的方法
    public void OnPointerUp(PointerEventData eventData)
    {
        if (click)
        {
            OnClick();
            click = false;
        }
    }

    // 在拖拽结束时调用
    public void OnDrop(PointerEventData eventData)
    {
        OnClick();
        click = false;
    }
    // 在拖拽过程中连续调用
    public void OnDrag(PointerEventData eventData)
    {
        if (click)
        {
            OnClick();
            click = false;
            OnDrop(eventData);
        }
    }

    // 将物品拾取到鼠标槽位
    public void PickupItem()
    {
        mouse.itemSlot = itemSlot;
        mouse.SetUI();
    }

    // 物品面板淡出效果
    public void Fadeout()
    {
        itemImage.CrossFadeAlpha(0.3f, 0.05f, true);//0.05 秒itemImage透明度渐变到0.3
    }

    // 将物品放下到当前物品面板上
    public void DropItem()
    {
        itemSlot.item = mouse.itemSlot.item;
        itemSlot.stacks = mouse.itemSlot.stacks;
        inventory.ClearSlot(mouse.itemSlot);
    }

    // 交换两个物品槽位的物品
    public void SwapItem(ItemSlotInfo slotA, ItemSlotInfo slotB)
    {
        // 暂存槽位A的物品信息
        ItemSlotInfo tempItem = new ItemSlotInfo(slotA.item, slotA.stacks);
        // 将槽位B的物品信息赋值给槽位A
        slotA.item = slotB.item;
        slotA.stacks = slotB.stacks;
        // 将暂存的物品信息赋值给槽位B
        slotB.item = tempItem.item;
        slotB.stacks = tempItem.stacks;
    }

    // 当物品面板被点击时调用的方法
    void OnClick()
    {
        if (inventory != null)
        {
            mouse = inventory.mouse;
            // 如果鼠标槽位为空,将物品拾取到鼠标槽位
            if (mouse.itemSlot.item == null)
            {
                if (itemSlot.item != null)
                {
                    PickupItem();
                    Fadeout();
                }
            }
            else
            {
                // 点击的是原始槽位
                if (itemSlot == mouse.itemSlot)
                {
                    inventory.RefreshInventory();
                }
                // 点击的是空槽位
                else if (itemSlot.item == null)
                {
                    DropItem();
                    inventory.RefreshInventory();
                }
                // 点击的是不同物品类型的已占用槽位
                else if (itemSlot.item.GiveName() != mouse.itemSlot.item.GiveName())
                {
                    SwapItem(itemSlot, mouse.itemSlot);
                    inventory.RefreshInventory();
                }
            }
        }
    }
}

修改Inventory,初始化清空鼠标物品槽

public Mouse mouse;

void Update()
{
    // 按下 Tab 键显示或隐藏背包界面
    if (Input.GetKeyDown(KeyCode.Tab))
    {
        if (inventoryMenu.activeSelf)
        {
            //。。。
            mouse.EmptySlot();
        }
        else
        {
            //。。。
        }
    }
}
//刷新背包
public void RefreshInventory()
{
	//。。。
	mouse.EmptySlot();
}

为了方便交换测试,我们新增一个石头的物品脚本

using UnityEngine;
public class StoneItem : Item
{
    public override string GiveName()// 获取物品名称
    {
        return "Stone";
    }
    public override int MaxStacks()// 获取每个物品槽的最大堆叠数量
    {
        return 5;
    }
    public override Sprite GiveItemImage()// 获取物品图片
    {
        return Resources.Load<Sprite>("UI/Item Images/Stone Icon");
    }
}

在Inventory中生成

void Start()
{
    //。。。
    AddItem(new WoodItem(), 40);
    AddItem(new StoneItem(), 40);
}

运行效果
【Unity实战】从零手戳一个库存背包系统_第15张图片

修复问题,基本拖到和交换功能是做好了,但是你会发现物品放下时物品透明的并没有还原,那是因为前面我们拖动时调用了Fadeout方法,实现了物品面板淡出效果,透明度变为了0.3,我们放置物品时没有还原物品面板透明度

在Inventory新增代码

//刷新背包
public void RefreshInventory()
{
	//。。。
	foreach (ItemSlotInfo i in items)
    {
    	//。。。
		if (panel != null)
        {
        	//。。。
			if (i.item != null)
            {
				//。。。
				panel.itemImage.CrossFadeAlpha(1, 0.05f, true);//0.05 秒itemImage透明度渐变到1完全不透明
			}
			//。。。
		}
	}
}

效果
【Unity实战】从零手戳一个库存背包系统_第16张图片

六、 物品拆分

实现滚轮拆分物品,shift对半分物品效果

修改Mouse代码,实现滚轮拆分物品

public ItemPanel sourceItemPanel; // 源物品面板对象
public int splitSize; // 拆分数量

void Update()
{
    // ...

    if (itemSlot.item != null) // 如果物品槽中有物品
    {
        if (Input.GetAxis("Mouse ScrollWheel") > 0 && splitSize < itemSlot.stacks)
        {
            // 当鼠标向上滚动并且拆分数量小于物品槽剩余堆叠数量时
            splitSize++; // 增加拆分数量
        }
        
        if (Input.GetAxis("Mouse ScrollWheel") < 0 && splitSize > 1)
        {
            // 当鼠标向下滚动并且拆分数量大于1时
            splitSize--; // 减少拆分数量
        }

        stacksText.text = "" + splitSize; // 在UI中显示拆分数量
        
        if (splitSize == itemSlot.stacks)// 如果拆分数量等于物品的堆叠数量
        { 
            // 将源物品面板的堆叠数量文本组件设置为不可见
            sourceItemPanel.stacksText.gameObject.SetActive(false);  
        }
        else
        {
            sourceItemPanel.stacksText.gameObject.SetActive(true);
            // 在文本组件中显示物品的剩余堆叠数量
            sourceItemPanel.stacksText.text = "" + (itemSlot.stacks - splitSize);
        }
    }
}   

public void SetUI()
{
    // stacksText.text = "" + itemSlot.stacks; // 设置叠加数量文本
    stacksText.text = "" + splitSize;// 在UI中显示拆分数量
    itemImage.sprite = itemSlot.item.GiveItemImage();
} 

修改ItemPanel,实现shift对半分物品效果

// 将物品拾取到鼠标槽位
public void PickupItem()
{
    mouse.itemSlot = itemSlot;
    mouse.sourceItemPanel = this;
    if (Input.GetKey(KeyCode.LeftShift) && itemSlot.stacks > 1)
    {
        mouse.splitSize = itemSlot.stacks / 2;
    }
    else
    {
        mouse.splitSize = itemSlot.stacks;
    }
    mouse.SetUI();
}
// 将物品放下到当前物品面板上
public void DropItem()
{
    itemSlot.item = mouse.itemSlot.item;
    if (mouse.splitSize < mouse.itemSlot.stacks)
    {
        itemSlot.stacks = mouse.splitSize;
        mouse.itemSlot.stacks -= mouse.splitSize;
        mouse.EmptySlot();
    }
    else
    {
        itemSlot.stacks = mouse.itemSlot.stacks;
        inventory.ClearSlot(mouse.itemSlot);
    }
}

效果

七、 物品堆叠

修改ItemPanel代码,新增物品堆叠方法

//物品堆叠
public void StackItem(ItemSlotInfo source, ItemSlotInfo destination, int amount)
{
    // 计算目标物品槽中可用的堆叠空间
    int slotsAvailable = destination.item.MaxStacks() - destination.stacks;
    
    // 如果目标物品槽没有可用的堆叠空间,则直接返回
    if (slotsAvailable == 0) return;

    if (amount > slotsAvailable)
    {
        // 堆叠数量超过可用空间时,从源物品槽中减去可用空间
        source.stacks -= slotsAvailable;
        // 目标物品槽的堆叠数量设置为最大堆叠数
        destination.stacks = destination.item.MaxStacks();
    }
    if (amount <= slotsAvailable)
    {
        // 堆叠数量小于可用空间时,将堆叠数量加到目标物品槽中
        destination.stacks += amount;
        // 如果源物品槽中剩余的堆叠数量等于堆叠数量(即所有物品都被堆叠完),则清空源物品槽
        if (source.stacks == amount) inventory.ClearSlot(source);
        // 否则,从源物品槽中减去堆叠数量
        else source.stacks -= amount;  
    }
}

// 当物品面板被点击时调用的方法
void OnClick()
{
	//。。。
	
	// 点击的是同物品类型的已占用槽位
    else if (itemSlot.stacks < itemSlot.item.MaxStacks()){
        StackItem(mouse.itemSlot, itemSlot, mouse.splitSize);
        inventory.RefreshInventory();
    }
}

运行效果

八、 拖拽还原

实现拖拽物品时按鼠标右键还原物品
修改Inventory代码

void Update()
{
	//。。。
	
    //拖拽物品时按鼠标右键还原物品
    if (Input.GetKeyDown(KeyCode.Mouse1) && mouse.itemSlot.item != null)
    {
        RefreshInventory();
    }
}

效果

九、 引入字典存储数据

创建一个用于存储所有物品的字典allItemsDictionary

allItemsDictionary 字典是为了方便根据物品名称来查找对应的物品对象。通常情况下,当你需要添加某个物品时,首先需要根据物品名称从字典中获取对应的物品对象,然后再将其添加到 items 列表中。

使用字典的好处是可以通过物品名称快速索引到对应的物品对象,而不需要遍历整个 items 列表。这在处理大量物品的时候可以提高性能和效率。

修改Inventory代码

// 创建一个用于存储所有物品的字典,其中键为物品名称,值为对应的物品对象
Dictionary<string, Item> allItemsDictionary = new Dictionary<string, Item>();

void Start()
{
    // 初始化物品列表并赋值为 null
    for (int i = 0; i < inventorySize; i++)
    {
        items.Add(new ItemSlotInfo(null, 0));
    }

    // 获取所有可用的物品,并将它们添加到物品字典中
    List<Item> allItems = GetAllItems().ToList();
    string itemsInDictionary = "字典条目:";
    foreach (Item i in allItems)
    {
        if (!allItemsDictionary.ContainsKey(i.GiveName()))
        {

            allItemsDictionary.Add(i.GiveName(), i);
            itemsInDictionary += "," + i.GiveName();
        }
        else
        {
            // 如果字典中已存在同名的物品,则输出调试信息
            Debug.Log("" + i + "已存在于与之共享名称的字典中 " + allItemsDictionary[i.GiveName()]);
        }

    }

    itemsInDictionary += ".";
    Debug.Log(itemsInDictionary);

    // 添加一些初始物品
    AddItem("Wood", 40);
    AddItem("Stone", 40);
}

// 获取所有可用的物品
IEnumerable<Item> GetAllItems()
{
    List<Item> allItems = new List<Item>();

    // 获取当前应用程序域中的所有程序集
    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

    // 遍历每个程序集
    foreach (Assembly assembly in assemblies)
    {
        // 获取程序集中定义的所有类型
        Type[] types = assembly.GetTypes();

        // 遍历每个类型
        foreach (Type type in types)
        {
            // 检查类型是否是 Item 类的子类
            if (type.IsSubclassOf(typeof(Item)))
            {
                // 创建该类型的实例,并将其转换为 Item 对象
                Item item = Activator.CreateInstance(type) as Item;

                // 将物品添加到列表中
                allItems.Add(item);
            }
        }
    }

    return allItems;
}   

//添加物品
public int AddItem(string itemName, int amount)
{
    //查找要添加的项目
    Item item = null;
    allItemsDictionary.TryGetValue(itemName, out item);
    //如果未找到任何项,则退出方法
    if (item == null)
    {
        Debug.Log("无法在字典中找到要添加到库存中的物品");
        return amount;
    }
	
	//。。。
}

十、 拾取物品

新增可拾取物品脚本

using UnityEngine;
using TMPro;

//物品拾取脚本
public class ItemPickup : MonoBehaviour
{
    public string itemToDrop;   // 需要拾取的物品名称
    public int amount = 1;      // 物品数量,默认为1
    private TextMeshPro text;   //物品文本
    Inventory playerInventory;  //玩家的背包组件

    void Start()
    {
        text = transform.GetComponentInChildren<TextMeshPro>();
        text.text = amount.ToString();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.C))
        {
            PickUpItem();
        }
    }

    // 当碰撞体进入触发器时调用
    private void OnTriggerStay2D(Collider2D other)
    {
        if (other.tag == "Player")
        {
            // 获取玩家的背包组件
            playerInventory = other.GetComponent<Inventory>();
        }
    }

    //当碰撞体离开触发器时调用
    private void OnTriggerExit2D(Collider2D other){
        if (other.tag == "Player")
        {
            // 获取玩家的背包组件
            playerInventory = null;
        }
    }

    // 拾取物品的方法
    public void PickUpItem()
    {
        // 如果玩家背包组件存在,则拾取物品
        if (playerInventory == null) return;

        // 将物品添加到背包,并返回剩余的物品数量
        amount = playerInventory.AddItem(itemToDrop, amount);

        // 如果数量小于1,销毁该拾取物品的游戏对象
        if (amount < 1)
        {
            Destroy(this.gameObject);
        }
        else
        {
            //更新text
            text.text = amount.ToString();
        }
    }
}

创建一个Player主角和可拾取物品
【Unity实战】从零手戳一个库存背包系统_第17张图片

【Unity实战】从零手戳一个库存背包系统_第18张图片
给Player添加Player标签,并把Inventory脚本移到Player下
【Unity实战】从零手戳一个库存背包系统_第19张图片
可拾取物品添加脚本,配置参数
【Unity实战】从零手戳一个库存背包系统_第20张图片

效果

十一、 丢弃物品

绘制丢弃物品预制体
【Unity实战】从零手戳一个库存背包系统_第21张图片
挂载ItemPickup脚本
【Unity实战】从零手戳一个库存背包系统_第22张图片

修改Inventory代码

public GameObject dropObject;//丢弃物品预制体

//丢弃物品
public void DropItem(string itemName)
{
    Item item = null;
    // 从字典中查找物品
    allItemsDictionary.TryGetValue(itemName, out item);
    if (item == null)
    {
        Debug.Log("在字典中找不到要添加到掉落的物品");
        return;
    }

    // 在当前位置实例化一个掉落物体
    GameObject droppedItem = Instantiate(dropObject, transform.position, Quaternion.identity);

    //修改图片
    droppedItem.GetComponent<SpriteRenderer>().sprite = item.GiveItemImage();

    ItemPickup ip = droppedItem.GetComponent<ItemPickup>();

    if (ip != null)
    {
        // 设置掉落物品的属性
        ip.itemToDrop = itemName;
        ip.amount = mouse.splitSize;
        mouse.itemSlot.stacks -= mouse.splitSize;//更新物品槽中该物品的剩余数量,及减去将要丢弃的物品数量
    }

    if (mouse.itemSlot.stacks < 1) ClearSlot(mouse.itemSlot);// 清空物品槽
    
    mouse.EmptySlot();// 清空鼠标上的物品
    RefreshInventory();// 刷新背包显示
}

Update中调用

void Update()
{
    //控制丢弃物品 EventSystem.current.IsPointerOverGameObject():该条件判断鼠标当前是否位于UI元素之上
    if (Input.GetKeyDown(KeyCode.Mouse0) && mouse.itemSlot.item != null && !EventSystem.current.IsPointerOverGameObject())
    {
        DropItem(mouse.itemSlot.item.GiveName());
    }
}

效果

最终效果

【Unity实战】从零手戳一个库存背包系统_第23张图片

源码

整理好后,我会放上来

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。点赞越多,更新越快哦!当然,如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

你可能感兴趣的:(#,unity实战,unity,游戏引擎,游戏)