Unity 高级背包系统

前言

之前实现了一个初级的背包系统,它没有对物品进行分类。

这一次新增了物品的分类存储与显示。

Asset的结构

为了对物品进行分类管理,在ScriptableObject中使用列表存储三个类型的物品,分别是武器、食物以及材料。这个列表中的元素是一个特别的类,它包含了这类物品的名字,以及存储这类物品的列表。在物品配置表中,存储的是Item类对象,在背包物品数据表中,存储的是InventoryItem对象,它们的差异是,InventoryItem类的对象多了一个数量的属性。

物品的编号

101~199 是武器

201~299 是食物

301~399 是材料

判断物品的类型,用物品的 index / 100 即可。

代码

物品配置表

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu]
public class ItemsConfigurationAdvanced : ScriptableObject
{
    [Header("物品分类列表")]
    public List<ItemList> lists;
}

[System.Serializable]
public class ItemList
{
    public string listName;
    public List<Item> items;
}

背包物品配置表

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu]
public class InventoryConfigurationAdvanced : ScriptableObject
{
    [Header("玩家持有物品的分类列表")]
    public List<InventoryItemList> lists;
}

[System.Serializable]
public class InventoryItemList
{
    public string listName;
    public List<InventoryItem> items;

    public InventoryItemList(string name)
    {
        listName = name;
        items = new List<InventoryItem>();
    }
}

背包物品类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class InventoryItem : Item
{
    public int num;

    public InventoryItem(Item item, int num) : base(item)
    {
        this.num = num;
    }
}

注:背包物品类的构造函数调用了基类的拷贝构造函数

物品类

using System;
using UnityEngine;
using UnityEngine.UI;

[Serializable]
public class Item
{
    public int id;
    public string name;
    public string description;
    public Texture2D icon;
    public GameObject model;

    public Item(Item item)
    {
        id = item.id;
        name = item.name;
        description = item.description;
        icon = item.icon;
        model = item.model;
    }
}

物品数据管理器

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 提供物品配置表与背包物品
/// </summary>
public class BackpackManagerAdvanced : Singleton<BackpackManagerAdvanced>
{
    public ItemsConfigurationAdvanced itemsConfiguration = Resources.Load<ItemsConfigurationAdvanced>("Configurations/Items Configuration Advanced");
    public InventoryConfigurationAdvanced inventoryConfiguration = Resources.Load<InventoryConfigurationAdvanced>("Configurations/Inventory Configuration Advanced");

    /// <summary>
    /// 从背包中获取指定编号的物品
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    InventoryItem Contains(int index)
    {
        if (inventoryConfiguration.lists == null)
        {
            inventoryConfiguration.lists = new List<InventoryItemList>();
        }

        if(inventoryConfiguration.lists[index / 100 - 1] == null)
        {
            inventoryConfiguration.lists[index / 100 - 1] = new InventoryItemList(itemsConfiguration.lists[index / 100 - 1].listName);
        }

        foreach (var i in inventoryConfiguration.lists[index / 100 - 1].items)
        {
            if (i.id == index)
                return i;
        }
        return null;
    }

    /// <summary>
    /// 保存指定编号的物品到背包
    /// </summary>
    /// <param name="index"></param>
    public void SaveItemToInventory(int index)
    {
        InventoryItem temp = Contains(index);

        if (temp != null)
            temp.num++;
        else
            inventoryConfiguration.lists[index / 100 - 1].items.Add(new InventoryItem(GetItem(index), 1));
    }

    /// <summary>
    /// 从物品配置表中获取指定编号的物品
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public Item GetItem(int index)
    {
        foreach (var i in itemsConfiguration.lists[index / 100 - 1].items)
        {
            if (i.id == index)
                return i;
        }
        return null;
    }
}

背包面板类

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

public class BackpackPanel : BasePanel
{
    static readonly string path = "Prefabs/UI/BackpackPanel";

    public BackpackPanel() : base(new PanelType(path)) { }

    /*-----------可配置的参数-----------*/

    public int column = 5;                  //列数
    public float itemSize = 280;            //物品格子大小
    public float interval = 20;             //格子之间的间隙
    public float viewPortHeight = 1500;      //窗口的高度

    //记录上一次显示的最值索引,以便移除看不见的格子
    int lastMinDisplayIndex;
    int lastMaxDisplayIndex;
    //滑动列表的容器
    public RectTransform content;
    //存储当前显示的格子及编号
    Dictionary<int, GameObject> curDisplayGrids = new Dictionary<int, GameObject>();

    public Item selectedItem;//当前选中的物品
    public override void OnEnter()
    {
        panelBindingTool.GetOrAddComponentInChildren<Button>("关闭").onClick.AddListener(() =>
        {
            panelStack.Pop();
        }
        );

        panelBindingTool.GetOrAddComponentInChildren<Button>("详情").onClick.AddListener(() =>
        {
            panelStack.Push(new ItemDetailPanel(selectedItem));
        }
        );

        panelBindingTool.GetOrAddComponentInChildren<Button>("武器").onClick.AddListener(() =>
        {
            listIndex = 1;
            InitThisTypeOfBackpack();
        }
        );

        panelBindingTool.GetOrAddComponentInChildren<Button>("食物").onClick.AddListener(() =>
        {
            listIndex = 2;
            InitThisTypeOfBackpack();
        }
        );

        panelBindingTool.GetOrAddComponentInChildren<Button>("材料").onClick.AddListener(() =>
        {
            listIndex = 3;
            InitThisTypeOfBackpack();
        }
        );

        InitThisTypeOfBackpack();

        OrbitCamera.Instance.work = false;
    }

    int listIndex = 1;//当前显示的物品分类 100~199 武器 200~299 食物 300~399 材料
    void InitThisTypeOfBackpack()
    {
        DestroyCurDisplayGrids();

        content = panelBindingTool.GetOrAddComponentInChildren<RectTransform>("Content");       //获取容器的组件
        content.sizeDelta = new Vector2(0, Mathf.CeilToInt(BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items.Count / column) * (itemSize + interval));      //根据物品数目计算容器的高度(向上取整)

        GameManager.Instance.listIndex = listIndex;
        GameManager.Instance.MethodField = DisplayBackpackGridAdvanced;      //将DisplayBagItem函数注入GameManager的Update中,实现实时更新

        //初始显示第一个物品
        DisplayItemBeief(BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items[0]);
        selectedItem = BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items[0];
    }

    void DestroyCurDisplayGrids()
    {
        for(int i = 0;i<curDisplayGrids.Count;i++)
        {
            GameObject.Destroy(curDisplayGrids[i]);
        }
        curDisplayGrids.Clear();
        lastMinDisplayIndex = 0;
        lastMaxDisplayIndex = 0;
    }
    //销毁正在显示的格子并初始化相关参数

    public override void OnExit()
    {
        GameManager.Instance.NonMonoUpdateMethod = null;      //取消注入
        GameManager.Instance.MethodField = null;      //取消注入
        panelManager.DestroyPanel(this.panelType);

        OrbitCamera.Instance.work = true;
    }

    public override void OnPause()
    {
        panelBindingTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = false;
    }

    public override void OnResume()
    {
        panelBindingTool.GetOrAddComponent<CanvasGroup>().blocksRaycasts = true;
    }

    public void DisplayItemBeief(Item item)
    {
        panelBindingTool.GetOrAddComponentInChildren<Text>("Item Name").text = item.name;
        panelBindingTool.GetOrAddComponentInChildren<Text>("Item Description").text = item.description;
        panelBindingTool.GetOrAddComponentInChildren<Image>("Item Image").sprite = Sprite.Create(item.icon, new Rect(0, 0, item.icon.width, item.icon.height), new Vector2(0.5f, 0.5f));
    }

    public void DisplayBackpackGridAdvanced(int listIndex)
    {
        /*-----------计算可见范围-----------*/

        int minDisplayIndex = (int)(content.anchoredPosition.y / (itemSize + interval)) * column;      //向下取整: 0,0 190,3 950,15
        int maxDisplayIndex = (int)((content.anchoredPosition.y + viewPortHeight) / (itemSize + interval)) * column + column; // 下面一栏物品,露出一点点也需要显示,所以需要多显示一行

        /*-----------越界的不显示-----------*/

        if (minDisplayIndex < 0)
            minDisplayIndex = 0;

        if (maxDisplayIndex > BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items.Count)
            maxDisplayIndex = BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items.Count;

        /*---------显示可见范围内的格子------*/

        for (int i = minDisplayIndex; i < maxDisplayIndex; i++)
        {
            if (curDisplayGrids.ContainsKey(i))                 //已显示的格子,不再重复创建
                continue;
            else
            {
                GameObject obj = PoolManager.Instance.getObject("BackpackGrid", "Prefabs/UI/BackpackGrid");       //未显示的格子,从池中获取
                obj.transform.SetParent(content);
                obj.transform.localPosition = new Vector3(i % column * (itemSize + interval), i / column * (itemSize + interval) * (-1), 0);          //将格子放到正确的位置上
                obj.GetComponent<BackpackGrid>().InitInfo(BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items[i]);                 //初始化格子信息
                obj.GetComponent<BackpackGrid>().OnClick(this, BackpackManagerAdvanced.Instance.inventoryConfiguration.lists[listIndex-1].items[i]);
                curDisplayGrids[i] = obj;                                                               //将当前显示的格子按照编号录入字典
            }
        }

        /*----------移除不可见的格子---------*/

        for (int i = lastMaxDisplayIndex; i > maxDisplayIndex; i--)
        {
            if (curDisplayGrids.ContainsKey(i))
            {
                PoolManager.Instance.recycleObject("BackpackGrid", curDisplayGrids[i]);
                curDisplayGrids.Remove(i);
            }
        }

        for (int i = lastMinDisplayIndex; i < minDisplayIndex; i++)
        {
            if (curDisplayGrids.ContainsKey(i))
            {
                PoolManager.Instance.recycleObject("BackpackGrid", curDisplayGrids[i]);
                curDisplayGrids.Remove(i);
            }
        }

        lastMaxDisplayIndex = maxDisplayIndex;
        lastMinDisplayIndex = minDisplayIndex;
    }
}

效果

猜你喜欢

转载自blog.csdn.net/weixin_43673589/article/details/124652489