野外生存游戏【1. 库存系统】

UE版本:5.2.1

本章实现效果预览

image-20230901173439420

注:本文是为多人游戏设计的,所以在某些部分会比较繁琐,可根据个人需求自行调整。另外,获取到项目工程文件之后,需做此设置才能正常运行

image-20230901183325581

系统框架

基于第一人称游戏创建项目

image-20230901171602709

项目文件结构如下

image-20230901171657143

  • _Main文件夹用于存放所有开发文件
    • Blueprints用于存放蓝图
      • DataTables用于存放数据表
      • Enums用于存放枚举
      • GameModes用于存放游戏模式
      • Interfaces用于存放接口文件
      • Inventory用于存放库存系统相关蓝图
      • Items用于存放物品
      • Structures用于存放结构体
    • Widgets用于存放视图组件
      • InventoryWidgets用于存放库存相关的视图文件
      • Textures用于存放纹理资源文件

物品存放

UI

先来创建UI界面,实现效果如下

image-20230901172250079

需要创建的 Widgets 有这些(另外两个用于实现拖放逻辑)

image-20230901172350199

UI 逻辑如下(这里没有详细截图,只是放个大概,可以自行查看项目文件或者根据喜好自行调整)

image-20230901172544925

image-20230901172743455

image-20230901172753923

物品定义

首先创建如下枚举,用于定义物品的类型、属性、稀有度等信息

image-20230901172920688

image-20230901172939383

image-20230901172947855

image-20230901172955743

image-20230901173004519

接着创建一个结构体,用于定义物品

image-20230901173115595

其中 BP_ItemMaster 是一个基类,先直接定义在 Item 文件夹下即可,暂时用不到

image-20230901173204323

定义好之后再创建一个数据表,新增一个木头物品

image-20230901173256964

到这里,物品相关的定义就完成了,接下来开始编写逻辑实现物品添加

新增物品

期望效果是按下1键,游戏就能从数据表中读取第一条物品并添加到库存中。

实现逻辑是:当用户按下1的时候,先读取物品信息,然后找到库存中为空的索引,将它存到一个固定长度的items数组里,接着更新UI即可

先创建一个自己的 GameMode 和 PlayerController

image-20230901173547024

然后在 Inventory 文件夹下创建这两个 ActorComponent,其中 BPC_PlayerInventory 继承自 BPC_ItemsContainer

image-20230901173643263

打开 BPC_ItemsContainer ,创建一个自定义事件,用于新增物品

image-20230901174318546

函数的具体实现逻辑如下,先找到一个空的槽位,然后更新UIimage-20230901174441714

image-20230901174451547

Find Empty Slot 实现逻辑如下,遍历 Items,通过 ItemID 是否为0来判断槽位是否为空

image-20230901174531122

Update UI 的实现逻辑如下,通过接口找到 Player Controller 的引用,然后通过 Player Controller 去调用 widget 来更新 UI(这里之所以要用接口传来传去搞得这么复杂,是为了要在多人游戏中区分客户端,可以根据自己的需求适当简化)

image-20230901174637933

其中用到的接口为 BPI_SurvivalCharacter

image-20230901174848906

image-20230901175052926

再创建一个 BPI_SurvivalGamePC 接口,挂在 Player Controller 上,就基本打通了获取引用这个过程

image-20230901175324380

image-20230901175413635

继续在 PlayerController 里面编写逻辑,前文说了要通过 PlayerController 来控制 Widget,所以这里先在开始运行的时候获取到 Widget 的引用

image-20230901175646231

接着在这里面创建两个事件,用于调用 Widget 来更新和重置槽位

image-20230901175847247

具体 Widget 里面的实现逻辑如下

image-20230901180044231

最后把 BPC_PlayerInventory 挂载到 BP_FirstPersonCharacter 上就好了

image-20230901180510419

OK,那么到这里,整个逻辑就打通了。运行游戏,按下Tab键会显示库存界面,按1就会新增物品

image-20230901180225965

拖拽物品

这里想做到的是,打开库存栏之后,我们拖拽物品可以移动到不同的槽位

实现逻辑如下:

1、点击物品,记录下当前选中的槽位索引和物品信息

2、拖拽物品,创建一个可拖拽的widget,并将信息复制上去

3、放在物品,找到鼠标落点处的槽位索引,将信息放进去,然后删除之前槽位的数据

image-20230901173439420

首先打开 W_InventorySlot,重载这三个函数,分别对应了上面提到的三步

image-20230901180921388

下面这个函数就检测了拖拽这个操作,W_InventorySlot 本身的 ItemInfo 变量就记录了物品信息,随后创建可拖拽的actor和对应的widget

image-20230901180939583

分别是这两个

image-20230901181347737

image-20230901181421142

image-20230901181433320

继续重载Drop函数,表示已经拖到位置,准备放下了

image-20230901180957622

其中 BP_FirstPersonCharacter 里面的 OnSlotDropOnServer 函数逻辑如下

image-20230901181744494

它调用了 BPC_PlayerInventory 里面的函数,但是 BPC_PlayerInventory 又是继承自 BPC_ItemsContainer 的,所以我们先打开 BPC_ItemsContainer, 编写 OnslotDrop 函数

image-20230901181801056

但是它的 Handle Slot Drop 函数是空的,因为要留在 BPC_PlayerInventory 里面实现

image-20230901181819734

所以我们回到 BPC_PlayerInventory ,重载 Handle Slot Drop 函数

image-20230901181954890

这个函数找到了初始的 container, 并调用了核心的 transferItem函数

image-20230901182147976

image-20230901182158729

其中 Add Item to Index 等子函数逻辑如下

image-20230901182234078

image-20230901182243862

image-20230901182302379

注意还有 Remove Item at Index 也被 BPC_PlayerInventory 重载了

image-20230901182832116

OK,到此为止,拖拽的逻辑就基本完成了