Blink

纸上得来终觉浅,绝知此事要躬行

Unity:实现背包系统(二)

背包管理器

/****************************************************
 *  文件名称:BagManager.cs
 *  作    者:Blink
 *  邮    箱:1614283998@qq.com
 *  创建日期:2019/11/11 16:51:05
 *  功    能:背包管理器
 ****************************************************/

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

public class BagManager
{
    #region 单例
    private static BagManager mInstance = null;
    public static BagManager Instance
    {
        get
        {
            if (mInstance == null)
                mInstance = new BagManager();
            return mInstance;
        }
    }
    private BagManager() { }
    #endregion

    // 刷新UI的回调
    public Action<BagGrid, bool> OnRefreshGridUI = null;
    // 背包已满回调
    public Action OnFilled = null;
    private Func<int, BaseItem> OnGetItem = null;

    // 背包格子对象数组
    private BagGrid[] mBagGrids = null;
    public BagGrid[] BagGrids
    {
        get
        {
            return mBagGrids;
        }
    }

    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="bagGridCount">背包格子数量</param>
    /// <param name="datas">背包数据</param>
    public virtual void Init(int bagGridCount, Func<int, BaseItem> getItemHandle, List<BagData> datas = null)
    {
        OnGetItem = getItemHandle;

        // 初始化背包格子数组
        mBagGrids = new BagGrid[bagGridCount];
        for (int i = 0; i < mBagGrids.Length; i++)
        {
            mBagGrids[i] = new BagGrid(i);
        }

        // 初始化背包物品数据
        if (datas != null)
        {
            foreach (BagData data in datas)
            {
                if (data == null) continue;
                BaseItem item = GetItemById(data.ID);
                if (item != null)
                {
                    mBagGrids[data.Index].SetItem(GetItemById(data.ID), data.Amount);
                }
            }
        }
    }

    /// <summary>
    /// 通过物品ID添加物品到背包
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <param name="amount">数量</param>
    public virtual void AddItem(int id, int amount = 1)
    {
        BaseItem item = GetItemById(id);
        AddItem(item, amount);
    }

    /// <summary>
    /// 通过物品对象添加物品
    /// </summary>
    /// <param name="item">需要添加的物品对象</param>
    /// <param name="amount">数量</param>
    public virtual void AddItem(BaseItem item, int amount = 1)
    {
        if (item == null)
        {
            throw new System.Exception("Bag AddItem Exception:需要添加到背包的Item为null!");
        }
        else
        {
            BagGrid grid = null;
            while (amount > 0)
            {
                grid = FindUsableGrid(item.ID);
                if (grid != null)
                {
                    int remainCapacity = grid.IsEmpty ? item.Capacity : (grid.Item.Capacity - grid.Amount);
                    int tmp = amount;
                    if (tmp > remainCapacity)
                        tmp = remainCapacity;
                    if (grid.IsEmpty)
                    {
                        grid.SetItem(item, tmp);
                        //TODO 刷新UI显示,新的物品
                        OnRefreshGridUI(grid, true);
                    }
                    else
                    {
                        grid.AddAmount(tmp);
                        //TODO 刷新UI显示,刷新数量
                        OnRefreshGridUI(grid, false);
                    }
                    // 剩余没存的物品个数,如果<=0则已经存储完
                    amount -= remainCapacity;
                }
                else
                {
                    //TODO 背包已满
                    break;
                }
            }
        }
    }

    /// <summary>
    /// 根据格子索引减少物品数量
    /// </summary>
    /// <param name="index">格子索引</param>
    /// <param name="amount">物品数量</param>
    public virtual void SubItemByIndex(int index, int amount = 1)
    {
        BagGrid grid = FindGridByIndex(index);
        if (grid == null) return;
        grid.SubAmount(amount);
        OnRefreshGridUI(grid, false);
    }

    /// <summary>
    /// 通过物品ID减少物品数量
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <param name="amount">amount</param>
    public virtual void SubItemById(int id, int amount = 1)
    {
        while (amount > 0)
        {
            BagGrid grid = FindGridById(id);
            if (grid == null) break;
            // 如果格子中的物品数量大于需要减少的数量
            if (grid.Amount >= amount)
            {
                grid.SubAmount(amount);
                amount = 0;
            }
            else
            {
                amount -= grid.Amount;
                grid.SubAmount(grid.Amount);
            }

            ////TODO 刷新UI
            //if (grid.IsEmpty)
            //{
            //    // 清空格子UI显示
            //    OnRefreshGridUI(grid, false);
            //}
            //else
            //{
            //    // 刷新数量显示
            //    OnRefreshGridUI(grid, false);
            //}
            OnRefreshGridUI(grid, false);
        }
    }

    /// <summary>
    /// 判断物品数量是否充足
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <param name="amount">需进行比较的物品个数</param>
    /// <returns></returns>
    public bool IsEnough(int id, int amount)
    {
        return GetItemCount(id) >= amount;
    }

    /// <summary>
    /// 获取背包中对应ID物品的总个数
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <returns></returns>
    public int GetItemCount(int id)
    {
        int count = 0;
        if (mBagGrids != null)
        {
            for (int i = 0; i < mBagGrids.Length; i++)
            {
                if (mBagGrids[i] == null || mBagGrids[i].IsEmpty) continue;
                if (mBagGrids[i].Item.ID == id)
                    count += mBagGrids[i].Amount;
            }
        }
        return count;
    }

    /// <summary>
    /// 获取背包中物品存储数据列表(存档用)
    /// </summary>
    /// <returns></returns>
    public List<BagData> GetBagDatas()
    {
        if (mBagGrids == null) return null;
        List<BagData> list = new List<BagData>();
        for (int i = 0; i < mBagGrids.Length; i++)
        {
            if (mBagGrids[i] == null || mBagGrids[i].IsEmpty) continue;
            list.Add(new BagData
            {
                ID = mBagGrids[i].Item.ID,
                Index = mBagGrids[i].Index,
                Amount = mBagGrids[i].Amount
            });
        }
        return list;
    }

    /// <summary>
    ///  通过物品ID获取物品
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <returns></returns>
    public BaseItem GetItemById(int id)
    {
        if (OnGetItem != null)
        {
            return OnGetItem(id);
        }
        return null;
    }

    /// <summary>
    /// 根据格子索引查找格子
    /// </summary>
    /// <param name="index">格子索引</param>
    /// <returns>查找到的格子</returns>
    public BagGrid FindGridByIndex(int index)
    {
        if (mBagGrids == null || index < 0 || index >= mBagGrids.Length)
            return null;
        return mBagGrids[index];
    }

    /// <summary>
    /// 根据物品ID查找格子(返回第一个查找到的格子)
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <returns>查找到格子</returns>
    public BagGrid FindGridById(int id)
    {
        if (mBagGrids == null) return null;
        for (int i = 0; i < mBagGrids.Length; i++)
        {
            if (mBagGrids[i] == null || mBagGrids[i].IsEmpty) continue;
            if (mBagGrids[i].Item.ID == id)
                return mBagGrids[i];
        }
        return null;
    }

    /// <summary>
    /// 查找可用的格子,返回格子索引,没有找到返回-1
    /// </summary>
    /// <param name="id">物品ID</param>
    /// <returns>查找到的格子</returns>
    protected virtual BagGrid FindUsableGrid(int id)
    {
        BagGrid grid = null;
        for (int i = 0; i < mBagGrids.Length; i++)
        {
            if (mBagGrids[i] == null)
            {
                continue;
            }
            // 空格子
            else if (mBagGrids[i].IsEmpty)
            {
                if (grid == null)
                {
                    grid = mBagGrids[i];
                }
            }
            // 未放满
            else if (mBagGrids[i].Item.ID == id && mBagGrids[i].Amount < mBagGrids[i].Item.Capacity)
            {
                grid = mBagGrids[i];
                break;
            }
        }
        return grid;
    }

    #region 测试
    public override string ToString()
    {
        string info = "";
        foreach (var g in mBagGrids)
        {
            if (!g.IsEmpty)
            {
                info += (g.ToString() + "\n");
            }
        }
        return info;
    }
    //END
    #endregion
}

测试

  1. 创建背包UI界面
    《Unity:实现背包系统(二)》

  2. 创建背包格子UI脚本
    脚本实现了IPointerDownHandler接口,使格子可以对点击事件进行相应操作

/****************************************************
 *  文件名称:UIGrid.cs
 *  作    者:Blink
 *  邮    箱:1614283998@qq.com
 *  创建日期:2019/11/11 18:35:10
 *  功    能:Noting
 ****************************************************/

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

public class UIGrid : MonoBehaviour,IPointerDownHandler
{
    public int index;
    public BagPanel panel;
    public Text txtAmount;
    public Image imgIcon;

    // 初始化,传入格子索引和背包面板对象
    public void Init(int index,BagPanel panel)
    {
        this.index = index;
        this.panel = panel;
        txtAmount = transform.Find("txtAmount").GetComponent<Text>();
        imgIcon = transform.Find("Sprite").GetComponent<Image>();
        Clear();
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        // 鼠标左键按下,显示物品信息
        if(eventData.button == PointerEventData.InputButton.Left)
        {
            panel.ShowInfo(index);
        }
        // 不是鼠标右键按下就减少数量
        else
        {
            BagManager.Instance.SubItemByIndex(index);
        }
    }

    // 清空格子显示
    public void Clear()
    {
        imgIcon.gameObject.SetActive(false);
        imgIcon.sprite = null;
        txtAmount.text = "";
        txtAmount.gameObject.SetActive(false);
    }

    // 刷新显示
    public void RefreshUI(Sprite sprite,int amount)
    {
        if(imgIcon.gameObject.activeSelf == false)
        {
            imgIcon.sprite = sprite;
            imgIcon.gameObject.SetActive(true);
        }
        RefreshUI(amount);
    }

    // 刷新显示
    public void RefreshUI(int amount)
    {
        if(amount <= 1)
        {
            txtAmount.gameObject.SetActive(false);
        }
        else
        {
            if (txtAmount.gameObject.activeSelf == false)
                txtAmount.gameObject.SetActive(true);
            txtAmount.text = "x" + amount;
        }
    }
}
  1. 创建背包UI脚本,并挂载到BagPanel上
/****************************************************
 *  文件名称:BagPanel.cs
 *  作    者:Blink
 *  邮    箱:1614283998@qq.com
 *  创建日期:2019/11/11 18:35:42
 *  功    能:Noting
 ****************************************************/

using UnityEngine;
using UnityEngine.UI;

public class BagPanel : MonoBehaviour
{
    public UIGrid[] uiGrids;
    public GameObject mInfo;

    private void Start()
    {
        // 注册背包管理器中的刷新UI回调
        BagManager.Instance.OnRefreshGridUI += OnRefreshGrid;

        // 获取所有的格子UI
        Transform content = transform.Find("Content");
        uiGrids = new UIGrid[content.childCount];
        for (int i = 0; i < content.childCount; i++)
        {
            // 给每个格子挂载上UIGrid脚本,并初始化
            uiGrids[i] = content.GetChild(i).gameObject.AddComponent<UIGrid>();
            uiGrids[i].Init(i, this);
        }

        // 刷新所有格子的显示
        var tmp = BagManager.Instance.BagGrids;
        foreach (var item in tmp)
        {
            if(item!=null && !item.IsEmpty)
            {
                OnRefreshGrid(item, true);
            }
        }
    }

    // 显示物品信息,传入点击的格子所有
    public void ShowInfo(int index = -1)
    {
        if (index != -1)
        {
            BagGrid grid = BagManager.Instance.FindGridByIndex(index);
            if (!grid.IsEmpty)
            {
                BaseItem item = grid.Item;
                string info = string.Format("名称:{0}\n容量:{1}\n说明:{2}", item.Name, item.Capacity, item.Des);
                mInfo.GetComponentInChildren<Text>().text = info;
                mInfo.SetActive(true);
            }
            else
            {
                mInfo.SetActive(false);
            }
        }
    }

    // 刷新格子显示,传入格子数据对象,isNew表示该格子是否为新添加物品(原本没有物品)
    public void OnRefreshGrid(BagGrid grid, bool isNew)
    {
        foreach (var item in uiGrids)
        {
            if(item.index == grid.Index)
            {
                if (grid.IsEmpty)
                {
                    item.Clear();
                    if (mInfo.gameObject.activeSelf)
                    {
                        mInfo.gameObject.SetActive(false);
                    }
                }
                else
                {
                    if (isNew)
                    {
                        Sprite sprite = Resources.Load<Sprite>(grid.Item.Icon);
                        item.RefreshUI(sprite, grid.Amount);
                    }
                    else
                    {
                        item.RefreshUI(grid.Amount);
                    }
                }
                break;
            }
        }
    }
}
  1. 创建物品数据配置表(这里使用的是Json格式)
[
    {
        "id":1001,
        "capacity":3,
        "name":"刀",
        "icon":"Sprites/1001",
        "type":1,
        "des":"这是一件好东西"
    },
  {
    "id": 1002,
    "capacity": 3,
    "name": "枪",
    "icon": "Sprites/1002",
    "type": 1,
    "des": "这是一件好东西"
  },
  {
    "id": 1003,
    "capacity": 3,
    "name": "剑",
    "icon": "Sprites/1003",
    "type": 1,
    "des": "这是一件好东西"
  },
  {
    "id": 1004,
    "capacity": 3,
    "name": "戟",
    "icon": "Sprites/1004",
    "type": 1,
    "des": "这是一件好东西"
  },
  {
    "id": 1005,
    "capacity": 3,
    "name": "斧",
    "icon": "Sprites/1005",
    "type": 1,
    "des": "这是一件好东西"
  },
  {
    "id": 1006,
    "capacity": 3,
    "name": "钺",
    "icon": "Sprites/1006",
    "type": 1,
    "des": "这是一件好东西"
  }
]
  1. 创建测试脚本,用来作为启动器,将其挂载到一个空物品上
/****************************************************
 *  文件名称:Test.cs
 *  作    者:Blink
 *  邮    箱:1614283998@qq.com
 *  创建日期:2019/11/11 22:38:21
 *  功    能:Noting
 ****************************************************/

using LitJson;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class Test : MonoBehaviour
{
    // 背包格子数量
    public int gridCount = 25;
    // 物品字典
    private Dictionary<int, BaseItem> mItemDic = null;

    private void Awake()
    {
        // 读取物品配置文件
        LoadItemCfg();

        // 加载存档数据
        List<BagData> dataList = Load();

        // 初始化背包管理器
        BagManager.Instance.Init(gridCount, GetItem, dataList);
    }


    private void Update()
    {
        // 按Q添加物品
        if (Input.GetKeyDown(KeyCode.Q))
        {
            int id = UnityEngine.Random.Range(1001, 1007);
            BagManager.Instance.AddItem(id);
            Debug.Log("添加物品,ID:" + id);
        }

        // 按A输出背包信息
        if (Input.GetKeyDown(KeyCode.A))
        {
            Debug.Log(this.ToString());
        }

        // 按W减少物品
        if (Input.GetKeyDown(KeyCode.W))
        {
            BagManager.Instance.SubItemByIndex(0);
            Debug.Log("减少物品");
        }

        // 按E键存档
        if (Input.GetKeyDown(KeyCode.E))
        {
            // 存档
            Save();
        }
    }

    private void OnGUI()
    {
        GUIStyle style = new GUIStyle();
        style.fontSize = 30;
        style.normal.textColor = Color.yellow;
        GUILayout.Label("Q键添加物品",style);
        GUILayout.Label("鼠标左键点击物品显示详情", style);
        GUILayout.Label("鼠标右键出售物品", style);
    }

    // 读档
    private List<BagData> Load()
    {
        string path = Application.streamingAssetsPath + "/bag.json";
        if (File.Exists(path))
        {
            StreamReader reader = new StreamReader(path);
            string json = reader.ReadToEnd();
            reader.Close();
            List<BagData> list = JsonMapper.ToObject<List<BagData>>(json);
            return list;
        }
        else
        {
            return null;
        }
    }

    // 存档
    private void Save()
    {
        List<BagData> list = BagManager.Instance.GetBagDatas();
        string json = LitJson.JsonMapper.ToJson(list);
        string path = Application.streamingAssetsPath + "/bag.json";
        StreamWriter writer = new StreamWriter(path);
        writer.Write(json);
        writer.Close();
    }

    // 读取配置文件
    private void LoadItemCfg()
    {
        mItemDic = new Dictionary<int, BaseItem>();

        string json = Resources.Load<TextAsset>("Configs/item").text;
        LitJson.JsonData configs = LitJson.JsonMapper.ToObject(json);
        foreach (LitJson.JsonData data in configs)
        {
            int id = int.Parse(data["id"].ToString());
            int capacity = int.Parse(data["capacity"].ToString());
            string name = data["name"].ToString();
            string icon = data["icon"].ToString();
            string des = data["des"].ToString();
            BaseItem item = new BaseItem(id, name, capacity, icon, des);
            mItemDic.Add(id, item);
        }
    }

    // 获取物品数据
    private BaseItem GetItem(int id)
    {
        if (mItemDic.ContainsKey(id))
            return mItemDic[id];
        return null;
    }
}

效果

  • 按E键存档后存档文件格式(这里使用的是Json)
[
    {
        "Index": 0,
        "ID": 1001,
        "Amount": 2
    },
    {
        "Index": 1,
        "ID": 1003,
        "Amount": 1
    },
    {
        "Index": 2,
        "ID": 1002,
        "Amount": 2
    }
]
  • 读取存档效果
    《Unity:实现背包系统(二)》
点赞
  1. 争渡说道:

    兄弟,什么时候出一篇关于unity的文件资源等的管理介绍,最近刚开始学习unity,对资源的管理比较混乱

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注