OK,和之前项目组的同事领导恰过晚饭(可惜木有喝酒,写的不给劲),开始写第二期,这一期实现的是背包系统的核心,背包本身!
思路
首先还是对整体的数据结构进行分析
上一期说明了背包是由一个个插槽Slot组成的,而插槽中持有物品实例,但是这就带来一个问题,插槽如何和外界沟通?Slot作为structure没法在蓝图中使用指针,而如果直接拷贝,又过于庞大和消耗
参照GAS中的做法,我们通过一个中间的数据结构SlotHandle作为钥匙去访问背包中的Slot
有了钥匙和储物柜,那么接下来还需要实现将物品放入储物柜,取出储物柜,通过钥匙获取储物柜的物品等功能,换句话说就是增删查改,相信各位都是老司机了,一定比我写的要好。
有了整体的思路,代码的实现不过是时间的消耗而已,接下来就开始实现。
代码实现
InventoryItemSlot
插槽Slot
ItemSlotFilter和SlotTags可以忽略,这是之后实现的功能,用于定义插槽的类别以及插槽存放物品的要求。
插槽重要的数据为:
- ItemInstance指针,指向物品实例。
- SlotId为插槽的编号,一个背包内每个插槽的Id都是唯一的。
- Owner为持有插槽的Inventory Component的指针,即持有插槽的背包。
InventoryItemSlotArray
存放插槽的数组
Arc Inventory和GAS中处理Spec结构体都采用了类似的写法,应该是为了网络同步,我对此了解的还不够深入,就不发表看法了,照着学就完事
InventoryItemSlotHandle
插槽的钥匙
重要的数据为SlotId,钥匙和它对应的柜子Id应该是一致的。这是用于根据Handle寻找对应的Slot的索引!
构造函数
equal和not equal的实现。目前其它的部分都可以忽略,核心的是判断SlotId是否一致!
InventoryComponent
首先给背包添加的数据为:插槽数组和钥匙数组
插槽Id生成器,背包每生成一次插槽,计数器+1,确保所有插槽的Id都不相同
下一步需要创建几个工具函数
IsValidItemSlot
用于判断SlotHandle-插槽钥匙是否有效
GetItemSlot
输入SlotHandle,获取对应的Slot。根据钥匙,找到对应的柜子。
方法也很简单,遍历柜子,一个个对一遍Id,如果编号一样,就说明找到了对应的柜子
创建和移除插槽
PostInventoryUpdate函数会在当背包系统插槽发生变化调用,比如新增或移除插槽
PopulateSlotReferenceArray用于对插槽重新排序
CreateInventorySlot
忽视SlotTags和Filter,目前没有作用
创建插槽的代码很简单,就算创建插槽的结构体然后添加到插槽数组当中,然后计数器+1,确保所有插槽的Id都不相同,然后调用背包更新函数
RemoveInventorySlot
移除插槽
PostInventoryUpdate
首先清空存放钥匙的数组,然后重新生成
PopulateSlotReferenceArray
按照当前的插槽重新生成钥匙数组
增删查改
接下来实现的传统艺能,增删查改。有一些代码我就不贴了,大致理解思路自己实现即可。
增加物品
LootItem函数的作用是找到背包中第一个空的插槽,调用PlaceItemIntoSlot将物品实例放入插槽
AcceptsItem函数的功能为判断指定的插槽Item Instance指针是否为空,如果为空则返回true,说明可以放入新的物品
PlaceItemIntoSlot函数的作用为,将物品放入指定的插槽当中
首先通过钥匙获取对应的Slot,然后通过代码Slot.ItemInstance=Item将物品存入Slot中
OnInventoryUpdate和OnItemSlotUpdate为背包内物品发生变化的消息委托,在这一期可以忽略
移除物品
移除指定插槽的物品
移除所有物品
查找物品
GetAllSlotHandles返回当前背包所有的插槽钥匙
GetItemInstanceInSlot获取指定插槽内存放的物品
蓝图测试
随便找一个actor,添加刚才实现的Inventory Component
然后在beginplay中添加以下代码。
- 创建物品instance
- 将instance通过LootItem函数放入背包中,因为当前背包都为空,所以会放到第一个插槽内
- 获取当前背包所有的插槽钥匙,然后获取第一个插槽的钥匙
- 获取第一个插槽内的物品
- 可以看到物品实例成功的放入插槽内了
这一块只是简单的实现了往背包内塞入物品的功能,下一期会正式的说明如何生成一件装备,并且让他成功的修改角色的属性。