前言
之前实现了一个初级的背包系统,它没有对物品进行分类。
这一次新增了物品的分类存储与显示。
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;
}
}
效果