文章目录
前言
这篇为自定义视图UI篇,我们将一起探索如何自定义各种UI,以及UI相关的功能。
上一篇我们掌握了库存系统的基本功能,可以说如果游戏中只有一个共同的商店一个角色那以及可以着手开发了。但如果开发一款RPG游戏那是远远不够的,需要大量的复杂的自定义UI功能,都看到这篇的同学早就是凤毛麟角了,那么不多说我们开始。
一、添加面板(人物背包)
1、创建背包
打开UI Designer上方一系统功能中的Inventory Grid,将即将创建的背包名称改为BagPanel,如下图所示指定角色和UI管理器,点击Create。
生成背包面板:
Panel Option的参数,详情-样式说明链接
Basic(基本):只是一个带有面板组件的简单直反变换 Simple(简单):面板有一个带有标题的标题
Floating(浮动):面板有一个带有标题的标题,标题有一个拖动处理程序组件,允许玩家用鼠标移动面板
Main Menu(主菜单):面板是专门为主菜单内制作的。父变换必须是主菜单面板内容变换 Gird Option的参数
Gird (格子):以单独格子显示单独的物品 List(列表):以列表的形式显示
2、主界面
创建一个UI面板,再创建MainPanel脚本挂上,这样我们点击按钮或者按键B就可以直接切换出背包;背包会显示主角身上的物品。
别忘了拖拽两个组件到MainPanel脚本。
using System.Collections;
using System.Collections.Generic;
using Opsive.UltimateInventorySystem.UI.Panels;
using UnityEngine;
using UnityEngine.UI;
public class MainPanel : MonoBehaviour
{
public Button BagBtn;
public DisplayPanel BagPanel;
// Start is called before the first frame update
void Awake()
{
BagBtn.onClick.AddListener(() => {
SwichPanel(BagPanel);
});
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.B))
{
SwichPanel(BagPanel);
}
}
/// <summary>
/// 切换界面
/// </summary>
/// <param name="displayPanel"></param>
private void SwichPanel(DisplayPanel displayPanel) {
if (!displayPanel.IsOpen)
{
displayPanel.SmartOpen();
}
else
{
displayPanel.SmartClose();
}
}
}
3、配置自定义网格
快速定位修改UI面板
网格排版
网格导航,可设为滚轮、按钮切换等
过滤器——搜索框、品类划分等
网格分类,如下图分为三类,选中的背包UI下有三个类别按钮,可自行配置图标
还记得上一章修复了不少东西,才能保证默认的生成功能能够使用吗。
下面的预制体需要改成上一章库存修改好的预制体,
二、聚焦UI时禁用人物控制
添加新面板和脚本用来判断是否聚焦UI,注意不要有其他UI组件。
将UI管理的GamePlay Panel替换为刚刚创建的。
在Inventory Canvas找到PreventSelectableDeselection脚本,将上面的bool类型设为true。测试一下,发现只对了一半,鼠标放在UI上的时候还是能移动角色;
我们找到GamePlay Panel上的DisplayPanelSelector移除掉,添加一个新的脚本UIPanelSelector挂在该UI上,具体是为了继承该类,重写里面的方法,UIPanelSelector脚本如下(自由发挥,假设鼠标在快捷栏不需要屏蔽操作的话就剔除掉该界面,或者Switch进行多种界面的定制):
using Opsive.UltimateInventorySystem.UI.Panels;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class UIPanelSelector : DisplayPanelSelector
{
public override void OnPointerClick(PointerEventData eventData)
{
base.OnPointerClick(eventData);
SetPlayer(false);
}
public override void OnPointerEnter(PointerEventData eventData)
{
base.OnPointerEnter(eventData);
SetPlayer(false);
}
public override void OnPointerExit(PointerEventData eventData)
{
base.OnPointerExit(eventData);
SetPlayer(true);
}
/// <summary>
/// 设置Player无法移动
/// </summary>
/// <param name="b"></param>
private void SetPlayer(bool b) {
GameObject.FindGameObjectWithTag("Player").GetComponent<ExampleMove>().enabled = b;
}
}
再运行,发现问题解决。
三、物品Action(物品丢弃、移动等)
互动(各种交互,包含丢弃、删除、移动(到仓库)等动作,可以李姐成委托)
进入Item Actions创建两个资源到项目内(过程不做展示)
下图为掉落和Debug调试的两个动作添加,这时运行游戏,点击物体就会使背包的物品掉落。
自定义动作:
创建自己的Item Action非常简单,只需创建一个继承Item Action并重写CanInvokeInternal和InvokeActionInternal函数的新类。
using Opsive.UltimateInventorySystem.Core.DataStructures;
using Opsive.UltimateInventorySystem.ItemActions;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class MyItemAction : ItemAction
{
/// <summary>
/// 能否调用该操作
/// </summary>
/// <param name="itemInfo">The item info.</param>
/// <param name="itemUser">The item user (can be null).</param>
/// <returns>True if it can be invoked.</returns>
protected override bool CanInvokeInternal(ItemInfo itemInfo, ItemUser itemUser)
{
Debug.Log("可以进行我的操作");
return true;
}
/// <summary>
/// 调用
/// </summary>
/// <param name="itemInfo">The item info.</param>
/// <param name="itemUser">The item user (can be null).</param>
protected override void InvokeActionInternal(ItemInfo itemInfo, ItemUser itemUser)
{
Debug.Log("这就是我的操作");
}
}
添加该方法,点击就会执行两个debug
虽说默认提供的已经够用,但该插件提供了一些特殊的接口和抽象的项目操作,在创建自定义项目操作时特别有用:
IActionWithPanel:允许您设置调用项目操作的面板。示例:移动项目操作ItemActionWithQuantityPickerPanel:从预制中生成一个QuantityPicker。
Panel,并在调用“真实”项目操作之前使用async/await获取数量。示例:数量删除项目操作。ItemViewSlotsContainerItemAction:引用项目视图插槽容器的项目操作。示例:打开其他项目视图插槽容器项目操作。
ItemActionWithAsyncFuncActionPanel<T>:生成一个异步函数操作面板,该面板可用于显示选项列表。示例:指定热键项操作
ItemObjectAction:用于引用项目对象,当项目用户无法从项目信息中识别相关项目对象时,该对象可能很有用。
代码调用-执行动作
新建脚本ManuallyCallAction,然后添加脚本到角色身上运行,按L就会生成设置好的物体。别忘了拖拽m_Inventory、PickUpItemPrefab。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Opsive.UltimateInventorySystem.Core;
using Opsive.UltimateInventorySystem.Core.DataStructures;
using Opsive.UltimateInventorySystem.Core.InventoryCollections;
using Opsive.UltimateInventorySystem.ItemActions;
public class ManuallyCallAction : MonoBehaviour
{
//角色库存
public Inventory m_Inventory;
//掉落预制体
public GameObject PickUpItemPrefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.L))
{
ManuallyCallDropItemAction();
}
}
public void ManuallyCallDropItemAction()
{
//要在代码中直接调用项目操作,您需要三样东西
//1) 项目信息 The ItemInfo
//2) 项目动作 The ItemAction
//3) 项目用户 The ItemUser
//首先拿到你想要的物品。
var result = m_Inventory.GetItemInfo(InventorySystemManager.GetItemDefinition("聚灵散"));
//如果结果没有价值,我们的库存中就没有聚灵散
if (result.HasValue == false) {
return; }
//得到苹果的ItemInfo
var appleItemInfo = result.Value;
//拿到两个聚灵散
appleItemInfo = new ItemInfo(Mathf.Max(1, appleItemInfo.Amount), appleItemInfo);
//让我们获取 ItemUser。这通常放置在与 Inventory 相同的游戏对象中,但并非总是如此
var itemUser = m_Inventory.GetComponent<ItemUser>();
//现在让我们创建 drop item 操作,您可以缓存此对象并在需要时重用它以获得更高的性能
var dropItemAction = new DropItemAction();
//您必须在使用前初始化项目操作。 这仅能一次。
dropItemAction.Initialize(false);
//设置掉落预制体
dropItemAction.PickUpItemPrefab = PickUpItemPrefab;
//可选:检查您是否被允许调用该操作(无论如何它都会在内部执行此操作)
var canInvoke = dropItemAction.CanInvoke(appleItemInfo, itemUser);
//调用动作
dropItemAction.InvokeAction(appleItemInfo, itemUser);
//对于某些操作,您甚至可以使用额外的数据
//例如 drop action 跟踪最后一个掉落的拾取对象
var droppedPickup = dropItemAction.PickUpGameObject;
}
}
不要被这些代码吓到了,一般游戏是用不到定制功能的,如果用到了那说明肯定有人懂编程,交给砖业的来就好。
三、物品功能面板
话说如果点左键就触发丢弃也太low,我们进行深度探索——点击物品弹出功能面板(如图)。
我们找到该脚本,专注红框内的4个属性,修改User Item Aciont Index为1,然后进入游戏,发现点击后弹出面板,可以单独选择,很舒服。
Use Itme On Click: 是否点击触发
User Item Aciont Index:动作索引-1为全部触发,>=0为弹出功能窗
Auto Set Item User:自动选择用户
Max Number Of Actions:最大的动作数量
四、浮动信息面板
找到Item Description点击Go Tp跳转到物品说明这一部分
创建一个默认的面板,
参数Give Description a panel代表是否创建面板(创建的UI是否带有Display Panel脚本),
参数Item Description Preset代表物品描述的面板大小。
将Panelcontent下面的子物体拖入库存格子进行面板绑定,Convert To Tooltip就是设置提示(浮动)面板,需要固定的就不去点这个就可以。
面板绑定成功!
我的这个面板背景有点物体,所以找到UI适配一下改成下图(具体看项目需求),默认居中。
五、拖拽
创建如图前两个程序,想要拖拽前两个是必要的,因为拖拽交换位置其实是两个功能组成:
拖拽程序:负责拖拽物品
OnItemViewSlotBeginDragE :在项目视图插槽开始拖动时触发。
OnItemViewSlotEndDragE:当项目视图槽完成拖动时触发。
OnItemViewSlotDragE:更新正在拖动项目视图插槽的每个帧。
通过侦听这些事件,拖动处理程序可以使用源项目视图插槽信息设置项目视图插槽光标管理器,并在拖动发生时告诉管理器鼠标的位置。
删除程序:负责处理掉落和删除物品(drop可以李姐成我们放下物品触发的动作)
通过下面第二个图片上的Item View Slot Drop Action Set里面的Action动作进行自定义
掉落条件
Item View Drop Container Can Add:检查来源和/或目的地是否可以接受交换。
Item View Drop Container Can Give:检查容器是否可以将物品交给另一个容器 Item View Drop Container Can
Move:检查是否可以将项目从一个索引移动到同一容器内的另一个索引。 Item View Drop Container Has
Name:检查源和/或目标容器的名称,看它们是否与指定的名称匹配。
Item View Drop From Item Collection:检查源项目集合是否与指定的项目集合相匹配。
Item View Drop Item Amount Condition:将源项目数量与最小值或最大值进行比较。
Item View Drop Null Item:检查源和/或目标项目是否为 Null。
Item View Drop Same Container:检查 Item View Slots 是否来自同一个 Item View Slots Container。
Item View Drop Container Can Smart Exchange:智能交换条件考虑了很多场景。
Item View Shape Drop:检查使用 ItemShapeGrid 时适合的
删除条件
Item View Drop Action To Item:从 Drop Action 调用 Item Action。
Item View Drop Container Exchange:通过从一个容器中移除并添加到另一个容器来交换容器之间的项目。
Item View Drop Container Give:将物品从源头送到目的地。 选择是否删除和/或添加项目。
Item View Drop Container 智能交换:智能交换条件考虑了很多场景。
Item View Drop Inventory Exchange:与上述类似,但不是通过 Item View Slots Container 添加/删除项目,而是直接在 Inventory 上执行此操作。
Item View Drop Inventory Give:与上面类似,但不是通过 Item View Slots Container 添加/删除项目,而是直接在 Inventory 上执行此操作。 Item View Drop Move Index 在同一个 Item View Container中移动 Item Stack 的索引。
Item View Drop Spawn Item Object:生成一个带有掉落物品的物品对象。
Item View Shape Drop:使用 ItemShapeGrid 函数放下项目,该函数考虑了 Item Shape。
默认设置基本能满足我们的项目,当然也可以创建自己的条件,只需继承“Item View Drop Condition”和“Item View Drop Action”抽象类:
[Serializable] public class ItemViewDropSameContainerCondition :
ItemViewDropCondition{
public override bool CanDrop(ItemViewDropHandler itemViewDropHandler)
{
return itemViewDropHandler.SourceContainer == itemViewDropHandler.DestinationContainer;
} }
[Serializable] public class ItemViewDropMoveIndexAction :
ItemViewDropAction {
public override void Drop(ItemViewDropHandler itemViewDropHandler)
{
itemViewDropHandler.SourceContainer.MoveItem(itemViewDropHandler.StreamData.SourceIndex,
itemViewDropHandler.StreamData.DestinationIndex);
} }
六、光标配置
举个实用的例子,拖拽物体的时候光标变换成另外的样子
项目视图槽移动光标将侦听容器上的以下事件:
OnItemViewSlotSelected:选择了项目视图槽。
OnItemViewSlotClicked:单击项目视图槽。
七、物品配置(图标、说明)
看着上面的图标空荡荡的,鼠标滑过还来回闪烁——我们是不是忘记什么设置了,没错,我图标呢?!
我不道啊!(官方没有说明)
其实是因为我们擅自瞎改图标的名称,默认是“Icon”我们整成中文“图标”,能对就怪了。
1、修复图标
修复方法很简单,打开DropHoverIconPreviewItemView和IconItemView脚本,将“Icon”改为“图标”即可。
“铁”的图标顺利显示出来了(丹药为none,未选择图标)。
2、修复描述说明
同样的找到ItemDescription脚本,修改"Description"为"描述",另外如下图错误说明也需要修改
哦,对了,为了以后生成的预制体不用修改,还得修改现有的预制体Item Description,是不是很麻烦。有个简单办法,去掉序列化就好了,哈哈,前面好像做了无用功。
当然,会编程的继承ItemDescriptionBase自己写也是可以的。自定义物品面板的方法也很简单,就是在这个脚本或者自己写的脚本上声明xx字段,在OnSetValue方法赋值,OnClear置空释放即可。
去掉后:
原来的:
完成效果:
八、角色栏
创建一个物品栏保存下来,默认名称为ItemSlotSet
创建想要的栏位,但是先别创建UI
我们这里在角色的物品集合中多加一个装备栏类型,再点击上图的Create。如图2:名字和浮动面板记得改下。
如图添加点击动作,再创建Create Action Panelhe 和 Tooltip(参考本章第三部分)。如果装备栏点击后弹出框功能想不一样就创建个新的,如果不需要该功能就不用创建,根据需求自定义。
加上提示面板,具体参考第四部分
创建出来后,在场景创建装备栏UI,再在MainPanel脚本添加并拖入按钮和面板,以及打开关闭装备栏的方法。
九、快捷栏
如图找到Hotbar,然后输入想要的物品栏数量,创建即可。
添加交互操作,创建两个程序,里面的action选之前背包选择的那个Item View Slot Drop Action Set。
下面为效果,看起来还不错。
十、物品限制(总上限、堆叠)
项目限制是一个可编写脚本的对象,通过右键创建Create -> Ultimate Inventory System -> Inventory -> Item Restriction Set.
几种限制说明
Group Item Restriction :可编程对象,本篇不做说明。详情
Item Collection Category Restriction: 限制某些物品栏只允许将特定类别的物品添加到其中。(具体见“3、管理物品集合”)
Item Collection Stack Amount Restriction : 只允许库存中一定数量的物品,限制可以容纳的物品数量。设置两个,十个格子只能放两个格子的物品;反之,不设置就是哪怕格子显示不下也在库存中无限储存。
Item Collection Stack Size Restriction:设置仓库堆叠上限,比如仓库最多能有99个相同属性的物体。
举例,限制16个格子能用,材料物品上限为99:
(1)修改库存名称为“玩家库存”
(2)在限制设置里面添加Item Collection Stack Amount Restriction和Item Collection Stack Size Restriction,填入刚刚改好的“玩家库存”,设置相应属性。
接着例子再举个例子,前面设置了玩家库存最多可以存99;我们限制要求相同材料9个为一组。
首先把“玩家库存”的类型改为MultiStackItemCollection,
然后设置默认堆叠大小为9
最终效果
注意:如果材料没有堆叠,可能是你的铁的父属性设置了可变的、唯一值;将下图的勾选取消即可。
3、管理物品集合(Item Collection Category Restriction)
修改玩家库存为ItemTransactionCollection,添加一个独立物品集合和堆叠集合(堆叠集合上限默认99,需要的自行修改)
设置如下两个条件,筛选总库存发送到新建的两个库存,方便以后管理和代码调用。
十一、仓库
仓库(storage)可以说是另一个背包,其中转移操作是利用我们之前讲过的Action动作(类似委托)来实现;仓库通常必须到某个地方或者某种一次性消耗品打开的类型,利用好的话游戏性也会大大增加。
(1)创建仓库
(2)添加仓库按钮和打开界面的事件(略)
(3)设置悬浮UI、点击面板、过滤器、搜索导航等(略)
(4)创建拖拽程序添加Action,也可以公用之前的,前提是背包功能和仓库一样。这部完成就可以拖拽了。(部分略)
(4)创建空物体添加Inventory库存脚本,拖入仓库作为仓库的库存。
效果(我只加了拖拽程序,其他的没加):
十二、商店
前置1、找到Shop Menu搜索Multi Currency找出几个预制体,替换成自己创建的预制并且修改货币属性。
改完预制,重新设置引用
前置2、找到ShopBase,修改价格属性,去掉序列化。当然像上一章在外部修改shop暴露的字段也可以。
1、创建商店
2、给商店库存添加售卖物品(略)
3、给商店添加交互库存,也就是主角
4、效果
5、要求商店删除购买的物品,并添加玩家出售的物品。
创建功能
勾选功能开关,我选择都打勾,就图个真实(互相买卖,互相伤害)
最终效果:
十三、制作界面
搜索Currency修改对应预制的货币
前置修改
1、创建制作界面
效果
总结
本篇写这么多,比我论文字数都多且查重率不高,我只能说尽力了,知识点很多,都很有用。除此之外不少东西得留到第四篇代码篇了,添加删除物品的接口、数据联动(喝血药)、物品联动(与模型联动-穿戴)、捡取东西的优化、打怪爆装备的接口、加载保存接口、宝箱随机功能。
插件和文章会在之后优化,去掉不必要的东西,在文章开头放出demo,这样做起来就很爽了,缺点如果插件更新就更不了了,毕竟自己写了些东西移动了很多组件的位置。
待优化:
1、打开界面会自动出现一个弹窗,其实自动锁定到第一个物品,方便键盘操作;给键盘和手柄玩家用的。这不是关键的,关键是那个面板的位置不正确,待优化。
2、音效部分未添加
3、联机功能(这个待定吧,目前没打算做联机所以没这需求)
4、部分快捷功能,如在仓库打开的情况下点击背包能快速移动物品;如多选功能;
5、半成品Demo,目前项目中有其他插件和框架,测试兼容,需要一定时间分离。
目前参杂很多项目预制修改的地方,等demo整理出来可以去掉,让文章更加清晰
整理部分:Item View For Ingredient缺X Item View For List(demo删除后)