Unity游戏开发:背包系统的实现
Posted float_freedom
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity游戏开发:背包系统的实现相关的知识,希望对你有一定的参考价值。
背包是游戏中经常使用的一个组件,它负责管理玩家在游戏中所获得的道具。一个完整的背包系统应当具有将物品放置进背包、对背包内物品进行管理和使用背包内物品等功能。而往往一个背包系统的逻辑关系较为复杂,如果把所有功能都放在一个脚本中实现会使代码显得十分冗杂且缺乏逻辑。所以在背包系统的设计过程中,我们常将其分解为数据、逻辑和UI三部分分别来进行完成。
一、UI设计
以Cotton Puzzle中的背包设计为例,我们需要有物品展示栏、物品切换按键和物品提示信息等部分。
在Canvas中创建ItemHolder,在ItemHolder中创建LeftButton和RightButton控制物品的左右切换、Slot来控制物品的显示以及ItemToolTip来显示物品的提示信息。
二、UI
创建InventoryUI脚本并挂在ItemHolder上以对整个背包UI进行管理。
首先,确定InventoryUI可以控制的组件并定义当前所显示的物品序号,由于ItemToolTip的显示与否和Slot直接相关联,所以可以通过控制Slot间接控制ItemToolTip,在InventoryUI中便不再单独对其进行控制。
//InventoryUI.cs
public Button leftButton;
public Button rightButton;
public SlotUI slotUI;
public int currentItemIndex;//当前物品序号
2.1 Slot中的物品显示
//InventoryUI.cs
/// <summary>
/// 更新Slot中物品图片
/// </summary>
/// <param name="itemDetails"></param>
/// <param name="index"></param>
private void OnUpdateUIEvent(ItemDetails itemDetails, int index)
if (itemDetails == null)//当前物品为空
slotUI.SetEmpty();
currentItemIndex = -1;
leftButton.interactable = false;
rightButton.interactable=false;
else
currentItemIndex = index;
slotUI.SetItem(itemDetails);
if (index > 0)
leftButton.interactable = true;
if (currentItemIndex == -1)
leftButton.interactable = false;
rightButton.interactable = false;
定义UpdateUIEvent事件来实时管理Slot中显示的物品(通过调用SlotUI中实现的SetEmpty()和SetItem(itemDetails)方法实现)和左右按钮的可使用情况。
//InventoryUI.cs
private void OnEnable()
EventHandler.UpdateUIEvent += OnUpdateUIEvent;
private void OnDisable()
EventHandler.UpdateUIEvent -= OnUpdateUIEvent;
2.2 物品切换
//InventoryUI.cs
/// <summary>
/// 左右按钮使用事件
/// </summary>
/// <param name="amount"></param>
public void SwitchItem(int amount)
var index = currentItemIndex + amount;
if(index< currentItemIndex)
leftButton.interactable = false;
rightButton.interactable = true;
else if(index> currentItemIndex)
leftButton.interactable = true;
rightButton.interactable = false;
else
leftButton.interactable = true;
rightButton.interactable = true;
EventHandler.CallChangeItemEvent(index);
2.3 SlotUI的实现
在SlotUI中需要实现将Slot图片设置为指定物品(包含物品为空的情况)、鼠标点击反馈、鼠标移入移出反馈(这里主要要实现当鼠标移入显示物品文字描述信息,移出取消显示的功能)。
//SlotUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class SlotUI : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
public Image itemImage;
public ItemDetails currentItem;
public ItemToolTip toolTip;
private bool isClicked;
/// <summary>
/// 设置Slot图片为指定item
/// </summary>
/// <param name="itemDetails"></param>
public void SetItem(ItemDetails itemDetails)
currentItem = itemDetails;
this.gameObject.SetActive(true);
itemImage.sprite = itemDetails.itemSprite;
itemImage.SetNativeSize();
/// <summary>
/// 设置Slot图片为空
/// </summary>
public void SetEmpty()
this.gameObject.SetActive(false);
public void OnPointerClick(PointerEventData eventData)
isClicked = !isClicked;
EventHandler.CallItemSelectedEvent(currentItem, isClicked);
public void OnPointerEnter(PointerEventData eventData)
if (this.gameObject.activeInHierarchy)
toolTip.gameObject.SetActive(true);
toolTip.UpdateItemName(currentItem.itemName);
public void OnPointerExit(PointerEventData eventData)
toolTip.gameObject.SetActive(false);
2.4 物品描述信息的展示
首先我们需要定义一个变量来记录物品的信息。这里我们在ItemToolTip下创建一个Text来记录文本信息。
//ItemToolTip.cs
public Text itemNameText;
接下来定义方法根据目前Slot所展示的物品名字来修改Text的内容。
//ItemToolTip.cs
public void UpdateItemName(ItemName itemName)
itemNameText.text = itemName switch
ItemName.Key => "信箱钥匙",
ItemName.Ticket => "一张船票",
_ => ""
;
为了方便使用我们将物品名写成了枚举变量,定义在Enums脚本中,方便在其他脚本中也可以使用。
//Enums.cs
/// <summary>
/// 物品名
/// </summary>
public enum ItemName
None,
Key,
Ticket,
三、数据
首先为了我们能够在unity中更加直观地对数据进行修改,利用以下代码在unity的菜单下创建数据子菜单,并序列化对象。每个ItemDetails包含物品名(itemName)和物品贴图(itemSprite)两个属性。
//ItemDataList.cs
[CreateAssetMenu(fileName ="ItemDataList",menuName = "Inventory/ItemDataList")]
/// <summary>
/// 对象序列化
/// </summary>
[System.Serializable]
public class ItemDetails
public ItemName itemName;
public Sprite itemSprite;
在unity中可以直接新建一个ItemDataList的数据对象,创建方式为:右键 -> Create ->Inventory -> ItemDataList
定义GetItemDetails根据物品名获取ItemDetails 对象。
//ItemDataList.cs
public List<ItemDetails> itemDetailsList = new List<ItemDetails>();
public ItemDetails GetItemDetails(ItemName itemName)
return itemDetailsList.Find(i => i.itemName.Equals(itemName));
四、逻辑
对于场景中的物体,在鼠标点击后需要能够被添加到背包内并在原有位置消失。可通过ItemClicked()函数来实现。
//Item.cs
public ItemName itemName;
public void ItemClicked()
//隐藏鼠标点击物体
this.gameObject.SetActive(false);
//将物品添加到背包
InventoryManager.Instance.AddItem(itemName);
最后,需要定义一个脚本InventoryManager对背包系统的UI、数据进行一个统一的管理。
//InventoryManager.cs
[SerializeField] private List<ItemName> itemList = new List<ItemName>();
public ItemDataList itemData;//通过物品名在物品列表中查找相应物品
(1)在新加载游戏的时候需要清空物品列表
private void OnStartNewGameEvent(int gameWeek)
itemList.Clear();
(2)当物品被使用时需要将物品移除背包并更新UI
private void OnItemUsedEvent(ItemName itemName)
var index = GetItemIndex(itemName);
itemList.RemoveAt(index);
if (itemList.Count == 0)
EventHandler.CallUpdateUIEvent(null, -1);
(3)当场景加载之后,需要将目前背包中有的物品进行更新
private void OnAfterSceneLoadedEvent()
if (itemList.Count == 0)
EventHandler.CallUpdateUIEvent(null, -1);
else
for (int i = 0; i < itemList.Count; i++)
EventHandler.CallUpdateUIEvent(itemData.GetItemDetails(itemList[i]), i);
(4)当使用Button在背包中左右切换物品时,需要实时更改物品的图片并判断是否越界
private void OnChangeItemEvent(int index)
if (index >= 0 && index < itemList.Count)
ItemDetails item = itemData.GetItemDetails(itemList[index]);
EventHandler.CallUpdateUIEvent(item, index);
else
Debug.Log("越界!");
注册这些事件:
private void OnEnable()
EventHandler.ChangeItemEvent += OnChangeItemEvent;
EventHandler.AfterSceneLoadedEvent += OnAfterSceneLoadedEvent;
EventHandler.ItemUsedEvent += OnItemUsedEvent;
EventHandler.StartNewGameEvent += OnStartNewGameEvent;
private void OnDisable()
EventHandler.ChangeItemEvent -= OnChangeItemEvent;
EventHandler.AfterSceneLoadedEvent -= OnAfterSceneLoadedEvent;
EventHandler.ItemUsedEvent -= OnItemUsedEvent;
EventHandler.StartNewGameEvent -= OnStartNewGameEvent;
另外有一些工具函数需要实现:
public void AddItem(ItemName itemName)
if(!itemList.Contains(itemName))//列表中不包含物品则添加到列表中
itemList.Add(itemName);
//UI显示
EventHandler.CallUpdateUIEvent(itemData.GetItemDetails(itemName), itemList.Count - 1);
/// <summary>
/// 获取物品在列表中的序号
/// </summary>
/// <param name="itemName"></param>
/// <returns></returns>
private int GetItemIndex(ItemName itemName)
for(int i=0;i<itemList.Count;i++)
if(itemName.Equals(itemList[i]))
return i;
return -1;
Unity UGUI有趣应用 -------------------- 背包系统(上)之简易单页背包系统及检索功能的实现
背包系统,无论是游戏还是应用,都是常常见到的功能,其作用及重要性不用我多说,玩过游戏的朋友都应该明白。
在Unity中实现一个简易的背包系统其实并不是太过复杂的事。本文要实现的是一个带检索功能的背包系统。先看一下我们要完成的效果 。由于上传的gif图不能大于5M,所以录制的质量比较一般。大家先将就看一下吧
那现在,我们就开始动手了~~
一. 拼UI
由于本文着重讲述的是背包系统的运作,所以,背包里面的元素获取就简洁地做成这个样子
事实上,一般游戏内获取道具的途径是敌人掉落,完成任务,商城购买等,而这里简化了这一步,右边的加号代表向背包里添加一个该元素。好了,现在开始实际制作。
1.1 在 Resources 文件夹下创建两个子文件夹 Prefab 和 Sprite ,用于存放资源
,
1.2 创建一个Canvas;
创建一个空物体,命名为Options;
创建一个 Image,用来显示道具,命名为 X1-装备
在上一步创建的 Image 下创建一个子 Image。
然后给这两张 Image 赋值
这里需要说明的是,命名为 X1-装备 是为了更方便的说明背包如何运作,事实上一般游戏的道具都是有着名字,种类等属性,而这些属性应该储存在 配置表 里面。本文不会涉及到配置表的操作,所以这里就简洁处理。还有这些UI资源都是随意的,读者自行选择自己的图片资源。
1.3 进行第②中的操作,实现如下效果
1.4 制作背包
在Canvas下创建一个空物体,命名为BG,赋上一张背景图,调整尺寸大小
在BG下创建一个Panel,调整其尺寸大小。并添加 Content Size Fitter 和 Grid Layout Group 组件,修改其中参数
1.5 检索UI
在BG下创建一个空物体,命名为 Toggles ,并在其下创建3个子Toggle,分别命名为 Med,Eqi,Goods。
再创建一个 InputField。如图
1.6 单个道具Item
创建一张Image, 命名为Cell。在Cell下创建一个子Text。调整两者大小。做成prefab,放到prefab文件夹中
至此,UI 拼接就完成了。只缺逻辑了~~~
二. UI逻辑
2.1 物品类
首先创建一个C#脚本,命名为 CellItem,代表物品类。一个道具(物品),通常都会有 名字,所属种类,数量,价格等属性,而这里的话,只取前三种属性,完整代码:
2.2 数据管理类
当我们的角色获得了一个道具之后,理应有一个管理系统将新获得的道具信息添加进去。所以创建一个C#脚本,命名为DataManager 。
在本文中实现的背包系统中,只管理了三种道具:药品,装备,物品(消耗品)。对应着前文所描述的 UI拼接 。所以在 DataManager 中创建3个 Dictionary ,用来存储道具信息。
以每个道具的名字为 key。同时为了方便管理,我把DateManager做成了单例模式。不熟悉单例模式的朋友可以参考一下这篇博文 https://www.cnblogs.com/liaoguipeng/p/5130144.html
DataManager完整代码:
2.3 UI管理类
这个类是整个背包的重点,用于管理第一步中创建的所以UI。
创建一个C#脚本,命名为UIManager。在场景中创建一个空物体,命名为GameManager,并把UIManager挂载上去。
为了获取第一步中所创建的UI,先定义与之相匹配的变量
其中,toggle 和 InputField 对应之前所制作的 toggle 和输入框;
中间三个 bool 变量表示检索时是否选择的toggle代表的种类;
buttons 代表左边6个添加按钮; lattice 用来得到之前制作的 Cell 的prefab;
content 代表前文的Panel;
whole则会记录所以的道具信息;
先给这些变量赋值 。我会先给出每个UI所对应的响应方法,方法体中会牵涉其它核心的方法,这个稍后会讲。
而3个bool值变量对应着三个方法。这三个方法作用于检索
3个Toggle也对应了3个响应方法
6个button对应着同一个方法,不过参数不同,参数为每个button所对应的名字,如“X1-装备”,“X2-药品”等
接下来就是比较重要的方法:
一. 修改键对值
当添加一个道具进来时,对其进行管理。应该先检索当前的 Dictionary 中是否有这个道具(key),如果有,则这个道具的数量属性 + 1;如果没有,则添加一个键对值。
二. 创建道具单元
在默认显示所有物品的情况下,只要遍历 Dictionary whole就可以创建出所有的道具。而在加入了检索功能后,则需要多进行一个判断,提取出符合检索标准的键对值,这一步我们用 Linq 可以快速获取。然后创建所有道具。
创建单元时需要把道具相应的图片显示出来,需要注意的是,我把我的资源放在了Resources/Sprite下,读者的资源路径与我不一致时,仅修改一行代码即可
三. 刷新背包
当我们添加一个道具或者按种类检索时,都应该刷新背包。而刷新的思路也很简单:先把 Panel 下的所以 CellItem 删除,然后根据新的 Dictionary 创建新的一批道具单元。
删除 方法
所以刷新函数如下
四. 按名字搜索
按名字搜索对应着前文创建的 InputField。思路也是比较简单的,如果字典中有这个道具key,就创建出来
完整代码:
有个问题是,当按名字搜索时,添加道具会执行相应的代码。事实上,当游戏中处于检索时一般是不会触发添加物体这种事件的。
三. 总结
总体来说,本文实现的这个背包系统不算复杂,也有人会问,本文一直在使用 Dictionary whole。而在 DataManager 中定义的三个字典除了存储了信息之外,并没有用到别的地方去,似乎是有点多余。
事实上,本文只是着重介绍整个背包系统的原理才使用了whole,可以更为简洁地说明。读者可以思考一下,如果我有着 10个种类的道具,每种道具都需要一页表来显示。如果我使用whole,那么对whole筛选10遍,重复操作多,浪费资源,而如果我有10个于种类对应的字典则可以直接使用,无需筛选。所以,分类保存是很有必要的。
在下一篇中,我会对这个背包系统进行优化和升级
- 采用读取配置表来录入道具信息
- 从单页背包升级分页背包
- 更为完善的检索系统
码字不易,希望这篇文章能对各位读者有所帮助O(∩_∩)O哈哈~
以上是关于Unity游戏开发:背包系统的实现的主要内容,如果未能解决你的问题,请参考以下文章
Unity UGUI有趣应用 -------------------- 背包系统(上)之简易单页背包系统及检索功能的实现