mini: false, //迷你模式 autoplay: false, //自动播放 theme: '#FADFA3', //主题色 loop: 'all', //音频循环播放, 可选值: 'all'全部循环, 'one'单曲循环, 'none'不循环 order: 'random', //音频循环顺序, 可选值: 'list'列表循环, 'random'随机循环 preload: 'auto', //预加载,可选值: 'none', 'metadata', 'auto' volume: 0.7, //默认音量,请注意播放器会记忆用户设置,用户手动设置音量后默认音量即失效 mutex: true, //互斥,阻止多个播放器同时播放,当前播放器播放时暂停其他播放器 listFolded: false, //列表默认折叠 listMaxHeight: 90, //列表最大高度 lrcType: 3, //歌词传递方式

New Inventory System Plugin

通过 Item的SphereComponent + LineTrace结合起来 检测物体

原理:先进行SphereOverlap检测所有重叠的Item并存储到OverlappingItems,
然后再进行射线检测,因为射线检测可以穿墙,所以射线检测只检测OverlappingItems里面的物品,
如果检测到则将CurrentItem设置为检测到的值,否则设置为OverlappingItems[0]的值

创建InteractComponent,放置在PlayerController上:
屏幕截图 2025-11-12 104633
创建USceneComponent,放置在Item上,USceneComponent可以设置SphereComponent:
屏幕截图 2025-11-15 102838
创建HUD,用于设置中心圆点和PickupMessage:
屏幕截图 2025-11-12 223335
在ProjectSetting中创建ItemTraceChannel和Item的Preset,用于射线检测:
屏幕截图 2025-11-12 224921
屏幕截图 2025-11-12 224908
创建Component:
屏幕截图 2025-11-15 105218
配置InteractComponent:
屏幕截图 2025-11-15 105550
将BPC_Inv_ItemComponent添加到Item中:
屏幕截图 2025-11-15 105157
将Item的StaticMesh的CollisionPreset设置为Item:
屏幕截图 2025-11-15 105618
将BPC_Inv_InteractComponent添加到PlayerController中:
{A49777BD-4335-4C3F-A1A5-11388ECC0B66}
添加PostProcessVolume并设置Material从而达到高亮效果:
屏幕截图 2025-11-15 111655
Inv_InteractComponent.h:
屏幕截图 2025-11-15 101227
Inv_InteractComponent.cpp:
屏幕截图 2025-11-15 101256
屏幕截图 2025-11-15 101309
屏幕截图 2025-11-15 101434
屏幕截图 2025-11-15 101442
Inv_ItemComponent.h:
屏幕截图 2025-11-15 101455
Inv_ItemComponent.cpp:
屏幕截图 2025-11-15 101524
Inv_HUD.h:
屏幕截图 2025-11-15 101535
Inv_HUD.cpp:
屏幕截图 2025-11-15 101541

创建SpatialInventory,InventoryGrid,GridSlot三个Widget并通过创建InventoryComponent实现开关库存操作

创建BaseInventory用作父类:
屏幕截图 2025-11-15 170010
创建子类SpatialInventory并继承BaseInventory:
屏幕截图 2025-11-15 170201
创建UUSerWidget类的InventoryGrid:
屏幕截图 2025-11-15 173951
创建UUSerWidget类的GridSlot:
屏幕截图 2025-11-15 174411
创建InventoryComponent用于切换库存:
屏幕截图 2025-11-15 170324

InventoryComponent.h:
初始化SpatialInventory并设置ToggleInventory函数用于切换库存
屏幕截图 2025-11-15 221218
InventoryComponent.cpp:
屏幕截图 2025-11-15 221239
屏幕截图 2025-11-30 210413
InventoryGrid.h:
在InventoryGrid中创建具体行数和列数的GridSlot:
屏幕截图 2025-11-15 221305
InventoryGrid.cpp:
屏幕截图 2025-11-15 221326
GridSlot.h:
在GridSlot中存储当前的Index:
屏幕截图 2025-11-15 221337
InteractComponent.cpp:
在InteractComponent中创建ToggleInventory动作并存储InventoryComponent然后调用InventoryComponent中的ToggleInventory:
屏幕截图 2025-11-15 221404
创建三个Widget:
屏幕截图 2025-11-15 213035
设置SpatialInventory:
屏幕截图 2025-11-15 213054
设置InventoryGrid:
屏幕截图 2025-11-15 213105
设置GridSlot:
屏幕截图 2025-11-15 213138
设置InventoryComponent:
屏幕截图 2025-11-15 213214
将InventoryComponent添加到PlayerController上:
屏幕截图 2025-11-15 213232

创建UObject的抽象类用于存储数据

屏幕截图 2025-11-18 164536

创建Fast Array Serialize

屏幕截图 2025-11-18 164923
根据FastArraySerializer的步骤来建立FastArray:
{99611DE7-8862-428F-95BC-43CCD5794BA1}
将InventoryItem作为存储数据放在Entry的结构体中
在FastArray结构体中声明Entries用于存储所有Entry,
添加AddEntry和RemoveEntry的函数
添加自带的PostReplicateAdd和PreReplicatedRemove函数,用于在复制新添加/新删除的元素前调用:

{187C3683-FE3B-493E-AB0A-60CFB6CBF127}
在PostReplicateAdd和PreReplicatedRemove函数中调用InventoryComponent的委托:
{8162BC8B-122C-4FFD-A982-933A720C233B}
在InventoryComponent中创建InventoryItemChange和NoRoomInInventory的委托,并添加TryAddItem的函数:
{7D8D6D79-C3C7-46FC-B02B-902EC1655329}
创建TryAddItem函数并调用OnNoRoomInInventory的委托:
{C6F577CB-35C1-4CF9-8E03-4518E47DB672}
在InteractComponent中调用TryAddItem函数:
{67A87A59-EF4F-4089-9348-E6D4F00AB2F0}
创建InfoMessage用于显示NoRoom的Message:
{9FD85204-C36B-4EFD-961C-FBE36F97308D}
{BFFE5B90-08F7-4517-9AFD-3C8AEBC1C6CA}
创建FadeAnimation:
屏幕截图 2025-11-18 210726
在蓝图中实现ShowInfoMessage和HideInfoMessage的函数:
屏幕截图 2025-11-18 210902
将InfoMessage添加到HUD中:
屏幕截图 2025-11-18 210937
在HUD中初始化委托并设置InfoMessage:
屏幕截图 2025-11-19 143832
屏幕截图 2025-11-19 143839

创建SlotAvailability和GridAvailabilityResult来判断库存是否有空间

GridAvailabilityResult用来判断整个库存还能放下多少个该物品(TotalRoomToFill)、是否可堆叠、还有多少放不下(Remainder)并列出具体要放置的槽位SlotAvailabilities
SlotAvailability计算不同的格子要放多少个该物品,指出目标索引、是否已有物品、还能填多少(AmountToFill)
FInv_SlotAvailability 加起来的放置数量就等于GridAvailabilityResult的总的AmountToFill数量

{BCF7475A-D8A4-4A9B-8B0E-8D53F21DC72C}
在BaseInventory中创建判断库存中是否有空间:
屏幕截图 2025-11-19 172835
在SpatialInventory中继承父类函数:
屏幕截图 2025-11-19 172841

创建ItemManifest描述Item的属性

拾取物 (UInv_ItemComponent) 在蓝图中设置 FInv_ItemManifest
当要把物品放进玩家 UInv_InventoryComponent 时,会通过 FInv_FastArray::AddEntry 新建 UInv_InventoryItem,复制 Manifest 数据 并 注册成可复制子对象

在ItemComponent中创建ItemManifest并在蓝图中设置:
{79AC8419-C8A8-4965-A2FA-F707FA344AEB}
在InventoryItem中创建用来保存 FInv_ItemManifest 的实例,并添加设置该实例的函数,以及获取ItemManifest的函数:
{A833DDB9-909E-43E7-9C0E-63CFD3C7B591}
在ItemManifest中创建CreateInventoryItem的函数,创建InventoryItem 并 把当前的ItemManifest数据复制进新对象:
{DD2A49B6-989A-41DE-9CE5-FF55222EE626}
在InventoryComponent中创建FastArray并添加AddRepSubObject函数和Server函数:
屏幕截图 2025-11-19 211635
将子对象加入复制队列,确保客户端能收到该子对象
屏幕截图 2025-11-19 211706
完善FastArray中的AddEntry(UInv_ItemComponent ItemComponent)函数,通过ItemComponent中的Manifest中的CreateInventoryItem函数来新建 UInv_InventoryItem,并在该函数中复制 Manifest 数据,并注册成可复制子对象:*
屏幕截图 2025-11-19 211822

创建FGameplayTag类型的ItemType

屏幕截图 2025-11-20 132945
屏幕截图 2025-11-20 142508
屏幕截图 2025-11-20 142517
在ItemManifest中创建FGameplayTag类型的变量并限制Categories只能为Items:
屏幕截图 2025-11-20 142547
在ItemComponent中的Manifest中设置ItemType:
屏幕截图 2025-11-20 140911

创建AddItem函数绑定委托,并让其在客户端和服务器都能被调用

由于FastArray的PostReplicatedAdd和PreReplicatedRemove只会在客户端调用并调用OnInventoryItemAdded委托的Broadcast,所以需要在服务器上单独Broadcast:
屏幕截图 2025-11-20 160354
在AddEntry之后,只在服务器单独调用Broadcast:
屏幕截图 2025-11-20 160443
在InventoryComponent开头初始化InventoryList的OwnerComponent为InventoryComponent:
屏幕截图 2025-11-20 160601
在InventoryGrid中创建用于绑定的委托函数AddItem:
屏幕截图 2025-11-20 160529
屏幕截图 2025-11-20 163318

创建ItemFragment用于设置加入网格的物品的属性

添加BaseClass和ChildClass,由于Struct存在继承,所以在BaseClass中设置析构函数
创建FragmentTag用于确定当前的Fragment类型
GridFragment用于设置物品添加到网格的大小是1x1还是2x3
ImageFramgent用于设置物品添加到网格的图标:

屏幕截图 2025-11-22 213127
屏幕截图 2025-11-22 213136
屏幕截图 2025-11-22 213148
在Manifest中创建ItemFragments的实例化数组并排除父类选项:
屏幕截图 2025-11-22 213223
在Manifest中添加Fragments:
屏幕截图 2025-11-22 213635

设置HasRoomForItem函数

在Grid中创建执行具体逻辑的HasRoomForItem函数,然后在SpatialInventory中创建具体的Grid并调用该Grid的HasRoomForItem函数:
创建3个重载函数,其中以Manifest为参数的函数作为执行具体逻辑的函数,其他两个函数直接调用Manifest函数:
屏幕截图 2025-11-22 225429
在AddItem中调用HasRoomForItem函数来获取Result从而获取物品应该放在网格位置的具体信息:
屏幕截图 2025-11-22 225450
在SpatialInventory中创建具体的Grid:
屏幕截图 2025-11-22 225456
HasRoomForItem函数调用具体Grid的HasRoomForItem函数:
屏幕截图 2025-11-22 225501

Add SlottedItem To Canvas

在Manifest中创建模板函数,用于通过Tag来获取具体的Fragment(例如GridFragment,ImageFragment):
屏幕截图 2025-11-23 170527
创建SlottedItem Widget,用于将物品图标添加到Canvas:
屏幕截图 2025-11-23 120743
屏幕截图 2025-11-23 170714
屏幕截图 2025-11-23 170721
在InventoryGrid中创建SlottedItemClass和用于存储的SlottedItems:
屏幕截图 2025-11-23 170545
在AddItem函数中调用AddItemToIndices,将物品添加到需要添加的每个索引(堆叠物品分散到不同槽位,则会有多个Availability)
如果是多格物品,只会在左上角索引创建一个SlottedItem
在AddItemAtIndex函数中通过Manifest获取GridFragment和ImageFragment
通过Fragment来设置SlottedItem的位置和图标
调用AddSlottedItemToCanvas将SlottedItem添加到Canvas:

屏幕截图 2025-11-23 170652
在InventoryGrid中的HasRoomForItem函数中暂时创建Index为0的SlotAvailability用于测试:
屏幕截图 2025-11-23 170707
在蓝图中创建SlottedItem类的Widget:
屏幕截图 2025-11-23 173114
在InventoryGrid中设置SlotteItem类:
屏幕截图 2025-11-23 173123
效果:
屏幕截图 2025-11-23 173139

添加GridSlot占用Texture

在GridSlot中创建背景图片,并创建GridSlotState的枚举类:
屏幕截图 2025-11-23 223806
对于不同的State,设置不同的背景Brush:
屏幕截图 2025-11-23 223816
在AddItemToIndices中创建UpdateGridSlotsState函数用于更新Slot的状态
若为多格物品,则设置每个格子索引的State为占用:

屏幕截图 2025-11-26 130449
效果:
屏幕截图 2025-11-23 223848

创建StackCount

创建StackableFragment,用于设置物品的 单格最大堆叠数 和 捡起一个物品的堆叠数:
屏幕截图 2025-11-24 185932
创建用于StackableFragment的Tags:
屏幕截图 2025-11-24 185938
屏幕截图 2025-11-24 185942
在SlottedItem中创建Text用于显示堆叠数:
屏幕截图 2025-11-24 185949
创建更新堆叠数的函数:
屏幕截图 2025-11-24 185953
在创建SlottedItem的函数中调用UpdateStackCount函数:
屏幕截图 2025-11-24 190031
在HasRoomForItem函数中暂时硬编码创建GridAvailabilityResult:
屏幕截图 2025-11-24 190044
在蓝图中创建Stackable Fragment:
屏幕截图 2025-11-24 190701
在蓝图中添加Text_StackCount:
屏幕截图 2025-11-24 190630
效果:
{E271C4C5-DB5C-4174-870B-D16524DB3AFD}

Update GridSlot

由于一个物品可能会占用多个网格,
需要将被占用的网格标记为不可用
并且左上角格子记录堆叠数:

在GridSlot中添加StackCount、UpperLeftIndex、bAvailable:
屏幕截图 2025-11-25 195921
在UpdateGridSlotsState中设置对应的Slot:
屏幕截图 2025-11-25 195936

完善HasRoomForItem函数

完善HasRoomForItem的检查:
{F29F9504-EF84-46E9-A663-308ACF0C8BCA}
在HasRoomForItem中先判断是否存在可堆叠的同类物品,如果有,直接填充:
屏幕截图 2025-12-03 154816
Result中的InventoryItem是用于判断当前库存中是否存在相同类型的物品才会设置这个变量:
屏幕截图 2025-11-27 120648
在FastArray中添加查找第一个类型匹配的物品的函数:
屏幕截图 2025-11-27 120728
通过FindByPredicate查找:
屏幕截图 2025-11-27 121349
在TryAddItem函数中,如果在FastArray中找到相同类型的物品,则设置Result中的InventoryItem,并执行Server_AddStacksToItem,否则执行Server_AddNewItem:
屏幕截图 2025-11-27 121501

完善AddStackToItem函数

在InventoryItem中创建TotalStackCount用于记录库存中当前Item的堆叠总数:
屏幕截图 2025-11-27 212800
屏幕截图 2025-11-27 212833
在ItemComponent中创建DestroyItem函数用于销毁Item:
屏幕截图 2025-11-27 212900
在InventoryComponent中创建委托,用于当StackChange时,改变库存Widget:
屏幕截图 2025-11-27 212933
在添加StacksToItem之前调用委托:
屏幕截图 2025-11-27 213022
更新TotalStackCount,如果有Remainder,则更新剩余物品的StackableFragment,否则销毁Item:
屏幕截图 2025-11-27 213036
绑定委托,遍历SlotAvailabilities,如果当前格子有物品,则直接更新SlottedItem的StackCount,否则添加新的SlottedItem:
屏幕截图 2025-11-27 213105
将物品的Replicates设置为True:
屏幕截图 2025-11-27 180525

设置拖拽、合并

在GridSlot中添加鼠标进入和离开事件,用于鼠标移动到对应格子时,格子会高亮:
屏幕截图 2025-12-02 113601
屏幕截图 2025-12-02 113620
在SlottedItem中添加鼠标按下,进入和离开事件,用于拖拽SlottedItem,创建委托并在InventoryGrid中设置委托:
屏幕截图 2025-12-02 115301
鼠标按下时调用检测拖拽,在拖拽时创建用于视觉效果的SlottedItem:
屏幕截图 2025-12-02 121023
InventoryGrid中创建用于委托的函数:
屏幕截图 2025-12-02 223123
拖拽委托被触发时,将bIsDragging设置为true,用于在Tick中实现拖拽:
取消拖拽委托被触发时,将判断是否可以放置或交换:
屏幕截图 2025-12-03 154359
交换物品:
屏幕截图 2025-12-03 154423
移除Inventory中的SlottedItem:
屏幕截图 2025-12-03 154430
在Tick中执行拖拽逻辑,先通过鼠标所在象限和物品尺寸计算物品左上角起始坐标,然后判断是否可以放置或交换:
屏幕截图 2025-12-03 154502
屏幕截图 2025-12-03 154515
判断是否可以放置或交换:
屏幕截图 2025-12-03 154639
屏幕截图 2025-12-03 154659
通过鼠标所在象限和物品尺寸计算物品左上角起始坐标:
屏幕截图 2025-12-03 154729
计算鼠标在哪个格子并且计算在当前格子的左半边和上半边:
屏幕截图 2025-12-03 154743

创建PopUpMenu和SplitMenu

创建PopUpMenu的Widget:
屏幕截图 2025-12-12 174237
绑定Consume和Drop的函数:
屏幕截图 2025-12-12 174249
创建Split的Widget:
屏幕截图 2025-12-12 174307
绑定Split和Cancel的函数:
屏幕截图 2025-12-12 174317
再SlottedItem中创建RightButtonDown的函数,用于判断是否在SlottedItem上按下右键:
屏幕截图 2025-12-12 174334
屏幕截图 2025-12-12 174401
在InventoryComponent中创建DropItem和ConsumeItem的服务器函数,并创建SpawnDroppedItem函数:
屏幕截图 2025-12-12 174428
在服务器更新Consume和Drop后的库存变化:
屏幕截图 2025-12-12 174450
在Manifest中声明PickupActorClass用于Drop到世界:
屏幕截图 2025-12-12 174513
在ItemFragment中创建Consume的Fragment:
屏幕截图 2025-12-12 174533
屏幕截图 2025-12-12 174552
创建ConsumableFragment的Tag:
屏幕截图 2025-12-12 174557
屏幕截图 2025-12-12 174603
在SpatialInventory中创建CanvasPanel,创建关闭PopUpMenu和SplitMenu的函数:
屏幕截图 2025-12-12 174624
屏幕截图 2025-12-12 174638
在Grid中获取Consumable的Tag用于判断物品是否可以Consume,创建PopUpMenu和SplitMenu:
屏幕截图 2025-12-12 174723
屏幕截图 2025-12-12 174820
绑定SlottedItem的RightButtonDown,创建PopUpMenu并判断是否是Consumable
屏幕截图 2025-12-12 174912
按键按下时,调用Server函数,并在本地更新网格占用:
屏幕截图 2025-12-12 174942
放置物品时,先判断是否可以Split:
屏幕截图 2025-12-12 175142
如果Shift按下,则可以Split:
屏幕截图 2025-12-13 171134
Split和Cancel的按钮按下:
屏幕截图 2025-12-13 155917
将ItemComponent设置为可复制的:
屏幕截图 2025-12-12 175220
创建PopUpMenu:
屏幕截图 2025-12-12 180450
创建SplitMenu:
屏幕截图 2025-12-12 180456
设置Grid:
屏幕截图 2025-12-12 180530
为可以Consume的物品添加具体的Consumable Fragment ———— Heal Consumable Fragment:
屏幕截图 2025-12-12 180608
设置ItemComponent为可复制的:
屏幕截图 2025-12-12 180639

添加ItemDescription

创建ItemDescription Wiget类:
屏幕截图 2025-12-13 212750
屏幕截图 2025-12-14 104556
在SlottedItem中创建鼠标进入和离开事件:
屏幕截图 2025-12-14 104613
在鼠标事件中调用BaseInventory的函数:
屏幕截图 2025-12-14 104705
在BaseInventory中创建虚函数:
屏幕截图 2025-12-14 104719
在SpatialInvventory中实现虚函数:
屏幕截图 2025-12-14 104846
在SpatialInventory中的Canvas中创建ItemDescription,并根据鼠标位置和Canvas的大小来设置Widget的位置,确保Widget不会超过边界:
屏幕截图 2025-12-14 110351
当Hover时,延迟出现,当UnHover时,隐藏:
屏幕截图 2025-12-14 110357

完善ItemDescription

复合模式:
1.CompositeBase(复合基类)
·定义所有组件的公共接口
·声明 DoWork() 等操作
2.Composite(复合类)
·包含子复合类Composite 或 叶子节点Leaf(Children)
·DoWork() 遍历子组件并调用它们的 DoWork()
3.Leaf(叶子节点)
·实现基础操作,不包含子组件
·DoWork() 执行实际工作
屏幕截图 2025-12-14 112701



创建所有的复合Widget的类:
屏幕截图 2025-12-19 101501
屏幕截图 2025-12-19 101531
屏幕截图 2025-12-19 101555
在CompositeBase中创建关闭和显示Widget的函数,并创建ApplyFunction函数:
屏幕截图 2025-12-20 134209
屏幕截图 2025-12-20 134213
在Composite中创建Children数组,并继承Base中所有的函数:
屏幕截图 2025-12-20 134225
通过WidgetTree来设置Children,ApplyFunction和Collapse让所有的Children调用对应的函数:
屏幕截图 2025-12-20 134230
在Leaf中创建对应的FragmentTag,以及ApplyFunction:
屏幕截图 2025-12-20 134236
真正调用Function的位置:
屏幕截图 2025-12-20 134241
将ItemDescription改为继承自Composite:
屏幕截图 2025-12-20 134253



在创建ItemDescription后,调用AssimilateInventoryFragments函数:通过遍历所有专门为Similate设置的类型的Fragments,查找是否有Fragment与Composite上的Fragment匹配,如果有,则将Fragment的数据应用到对应的Composite上,从而在Description上显示对应的Leaf
创建Leaf_Image:
屏幕截图 2025-12-20 191450
添加Image和SizeBox:
屏幕截图 2025-12-20 202729
屏幕截图 2025-12-20 202733
添加专门用于Assimilate的Fragment,并让ImageFragment继承自AssimilateFragment:
屏幕截图 2025-12-20 202756
Assimilate函数:将Fragment的数据应用到对应的UI上,父函数判断是否与标签匹配并显示Widget:
屏幕截图 2025-12-20 202843
在Manifest中创建将Fragment数据应用到对应的Composite的函数,以及获取所有指定类型的Fragments的函数:
屏幕截图 2025-12-20 202909
获取所有指定类型的Fragments的函数:
屏幕截图 2025-12-20 202932
遍历所有FInv_AssimilateFragment类型的Fragment,每个Fragment调用Assimilate,找到匹配的Leaf并应用数据:
屏幕截图 2025-12-20 202951
在SpatialInventory创建ItemDescription时调用AssimilateInventoryFragments函数:
屏幕截图 2025-12-20 203051
在蓝图中创建Leaf_Icon并继承自Leaf_Image:
屏幕截图 2025-12-20 192817
设置Leaf_Icon的FragmentTag:
屏幕截图 2025-12-20 212816
在ItemDescription中添加WBP_Leaf_Icon:
屏幕截图 2025-12-20 203239
效果:
{B612A51C-BF9E-40E5-940F-D812E0073550}



创建Leaf_Text并在蓝图中继承自Leaf_Text创建ItemName的Widget
创建Leaf_Text:
屏幕截图 2025-12-20 213142
屏幕截图 2025-12-20 214953
在NativePreConstruct中设置Text的字体大小:
屏幕截图 2025-12-20 215015
创建新的TextFragment并继承自AssimilateFragment:
屏幕截图 2025-12-20 215034
配置Assimilate函数:
屏幕截图 2025-12-20 215053
添加ItemNameFragment的FragmentTag:
屏幕截图 2025-12-20 215109
屏幕截图 2025-12-20 215103
在蓝图中创建Leaf_Text的Widget类:
屏幕截图 2025-12-20 214008
设置ItemName的FragmentTag:
屏幕截图 2025-12-20 215303
添加到ItemDescription上:
屏幕截图 2025-12-20 215334
在物品的Manifest中添加TextFragment,并将FragmentTag设置为ItemNameFragment:
屏幕截图 2025-12-20 215351
效果:
屏幕截图 2025-12-20 215600



创建Label + Number:
创建Leaf类的LabeledNumber:
屏幕截图 2025-12-25 143631
创建Tag:
屏幕截图 2025-12-25 190223
屏幕截图 2025-12-25 190228
在Leaf中添加两个Text,一个用于显示Label,一个用于显示Value:
屏幕截图 2025-12-25 190248
屏幕截图 2025-12-25 190254
创建LabeledNumber的Fragment,并继承Assimilate和InitializeFragment函数,添加随机数让Value的值随机:
{B2CAB40B-D255-401D-862A-1FF23DAE2098}
初始化Fragment函数中让Value随机化:
屏幕截图 2025-12-25 190642
在Manifest中创建InventoryItem时,初始化所有的Fragments:
屏幕截图 2025-12-25 190658
在蓝图中创建LabeledNumber:
屏幕截图 2025-12-25 155632
屏幕截图 2025-12-25 190854
屏幕截图 2025-12-25 190904
屏幕截图 2025-12-25 190924



添加Modifiers,使Consumable实现Description显示的Value的值 和 真正增加的Value的值一样:
Consumable调用所有的Modifier的函数,Modifier继承自LabeledNumberFragment,用来具体设置Label的值并重写OnConsume函数从而让两个Value相同:

添加Modifier父类继承自LabeledNumber并添加OnConsume虚函数,然后再添加子类并重写各自的OnConsume函数
修改ConsumableFragment的父类为AssimilateFragment,并添加ConsumeModifiers的数组,在函数中调用每个Modifier的函数:

屏幕截图 2025-12-26 153728
在所有的Assimilate函数中添加判断,因为父类判断的只是父类自己的Fragment,子类还需要再次判断:
屏幕截图 2025-12-26 153831
在InitializeFragment函数中随机化Value,在ConsumableFragment的函数中分别调用所有的Modifier函数:
屏幕截图 2025-12-26 154147
在Description中添加3个Label并配置不同的标签:
屏幕截图 2025-12-26 154331
为Item设置ConsumableFragment:
屏幕截图 2025-12-26 154358
效果:
屏幕截图 2025-12-26 160843
屏幕截图 2025-12-26 160848

添加装备槽位

创建EquippedGridSlot并继承自GridSlot:
屏幕截图 2025-12-27 140057
创建EquippedSlottedItem并继承自SlottedItem:
屏幕截图 2025-12-30 202819
创建EquipmentComponent专门用于装备物品:
屏幕截图 2026-01-01 155924
在SlottedItem中存储当前Item所处的InventoryGird或EquippedGridSlot:
屏幕截图 2026-01-02 125710
在创建视觉反馈时设置:
屏幕截图 2026-01-02 125731
在SpatialInventory中创建EquippedGridSlot,
创建绑定函数,将SlottedItem的拖拽触发统一放到SpatialInventory中来配置:

屏幕截图 2026-01-02 125813
遍历所有创建的EquippedGridSlot并将其存储在EquippedGridSlots中,
绑定函数将分别调用所有槽位对应的绑定函数:

屏幕截图 2026-01-02 125844
在InventoryGrid中创建绑定函数,用于在SpatialInventory中调用:
屏幕截图 2026-01-02 125923
判断是否是EquippedSlottedItem,如果是则取消装备并清空EquippedGridSlot上的物品,然后将物品添加到InventoryGrid中:
屏幕截图 2026-01-02 133357
创建拖拽事件:
屏幕截图 2026-01-02 143723
获取SpatialInventory:
屏幕截图 2026-01-02 143956
在Tick中通过拖拽的鼠标位置来判断是否需要高亮:
屏幕截图 2026-01-02 144013
将物品拖拽到EquippedGridSlot上后,判断当前物品的标签是否匹配,并判断装备槽中是否已经存在物品:
屏幕截图 2026-01-02 144056
创建EquippedSlottedItem到EquippedGridSlot:
屏幕截图 2026-01-02 144114
创建EquipmentFragment和EquipModifier:
屏幕截图 2026-01-02 144210
屏幕截图 2026-01-02 144254
在InventoryComponent中创建OnItemEquipped和OnItemUnEquipped的委托:
屏幕截图 2026-01-02 144353
在Multicast中调用委托:
屏幕截图 2026-01-02 144451
在EquipmentComponent中绑定委托:
屏幕截图 2026-01-02 144511
在EquipmentComponent中调用EquipmentFragment的函数:
屏幕截图 2026-01-02 144640
在蓝图中创建:
屏幕截图 2025-12-27 145848
屏幕截图 2025-12-30 222129
屏幕截图 2026-01-01 162056
将EquipmentComponent添加到PlayerController中:
屏幕截图 2026-01-01 162126
在SpatialInventory中添加不同类型的EquippedGridSlot:
{3ADB4975-FCFE-4FC3-8B84-045518581DC8}
在物品中创建Equipment Fragment:
屏幕截图 2026-01-02 162846
效果:
{8214F42C-0B79-4C3E-89D6-B76C15B5B4A9}

添加EquipActor:真正装备的物品

创建Actor类:
屏幕截图 2026-01-02 170506
在EquipActor中添加用于辨识的ItemType:
屏幕截图 2026-01-02 200442
启用复制:
屏幕截图 2026-01-02 200446
在EquipmentComponent中Spawn和Destroy生成的EquipActor:
屏幕截图 2026-01-02 201750
初始化Character时,由于可能BeginPlay时Character还没准备好,所以需要添加添加OnPossessedPawnChanged回调:
屏幕截图 2026-01-02 201858
在Spawn函数中,调用EquipmentFragment中的生成函数,并设置EquipActor的ItemType用于辨识,最后添加到EquipActors数组中,
在Destroy函数中,通过数组的FindByPredicate函数查找标签匹配的EquipActor,并调用Destroy函数:

屏幕截图 2026-01-02 201923
在OnItemEquipped和OnItemUnEquipped函数中分别调用:
屏幕截图 2026-01-02 201934
在EquipmentFragment中配置AttachToPoint和EquipActorClass并创建生成EquipActor的函数:
{1F04B166-E1B0-47E6-8352-97DAB4C56558}
通过AttachMesh来生成EquipActor,并绑定到AttachToPoint:
{998D8977-66D6-4905-9A72-2F6D9313CC53}
创建EquipActor蓝图类:
屏幕截图 2026-01-02 174526
屏幕截图 2026-01-02 174634
为EquipmentFragment配置AttachToPoint和EquipActorClass:
屏幕截图 2026-01-02 202243
效果:
{4826B061-4B1A-44B6-BB46-A2B8E7C73966}

添加ProxyMesh和CharacterDisplay

创建ProxyMesh:
屏幕截图 2026-01-03 115047
在EquipmentComponent中添加判断石否是ProxyMesh:
屏幕截图 2026-01-03 163550
如果是ProxyMesh则不调用OnEquip函数,并且将EquipActor设置为不复制:
屏幕截图 2026-01-03 163646
在ProxyMesh中添加EquipmentComponent和ProxyMesh:
屏幕截图 2026-01-03 163717
通过不断的查找来初始化ProxyMesh和EquipmentComponent:
屏幕截图 2026-01-03 170450
创建CharacterDisplay的Widget:
屏幕截图 2026-01-03 170528
通过查找ProxyMesh来获取Mesh,当鼠标拖拽时,旋转角色:
屏幕截图 2026-01-03 170756
在SpatialInventory中添加CharacterDisplay的Widget,并添加鼠标松开函数:
屏幕截图 2026-01-03 171406
当鼠标在CharacterDisplay外面松开时,将CharacterDisplay的bIsDragging设置为false:
屏幕截图 2026-01-03 171418
在蓝图中创建ProxyMesh,并将其拖到世界中:
{03494E9B-0416-4707-95D5-340BBC58E356}
在蓝图中创建CharacterDisplay:
屏幕截图 2025-08-18 195919
创建Render Target:
屏幕截图 2025-08-18 200217
屏幕截图 2025-08-18 200248
配置大小:
屏幕截图 2025-08-18 200319
创建Material并将Texture改为Render Target:
屏幕截图 2025-08-18 200639
为Material设置背景透明:
屏幕截图 2025-08-18 215848
将CharacterDisplay的Image设置为刚刚的材质:
屏幕截图 2025-08-18 200802
在Spatial Widget中添加CharacterDisplay Widget:
屏幕截图 2025-08-18 201245
在ProxyMesh中添加SpringArm和SceneCaptureComponent2D,并将Texture设置为Render Target,并隐藏Atmosphere:
屏幕截图 2026-01-03 160658
将FiledOfView改为60:
屏幕截图 2026-01-03 154249
将ProxyMesh拖到世界中即可,如果修改了ProxyMesh,则需要将世界中的ProxyMesh删除后重新添加到世界:
只有在打包后才会在多人游戏中正确运行 或者 取消Run Under One Process进行测试:
屏幕截图 2025-08-19 110646
效果:
屏幕截图 2025-08-18 222608

添加AttachComponent,用于附加到角色Mesh上

将衣服直接附加到角色Mesh上,让衣服可以跟随角色的Animation动起来:
在EquipmentFragment中添加ClothingMeshAsset用于存储用于附加的衣服网格,当bAttachToMesh为True时,显示ClothingMeshAsset,隐藏AttachToPoint和EquipActorClass:
屏幕截图 2026-01-04 153910
在Component中添加Map用于存储Component:
屏幕截图 2026-01-04 154226
如果AttachToMesh,则创建SkeletalMeshComponent并附加到Mesh,
否则使用EquipActor:

屏幕截图 2026-01-04 154249
屏幕截图 2026-01-04 154302
在EquipmentFragment中有两种选择:
屏幕截图 2026-01-04 154320
屏幕截图 2026-01-04 154325

Fix Bug: 拾取物品时,若还有剩余,物品剩余数量却没有减少

添加获取Manifest Mutable类型的函数,确保Manifest可以被修改:
屏幕截图 2026-01-03 104656
屏幕截图 2026-01-03 105206

Bug:在设置另一个ItemDescription时,没有提前清除上一个的ItemDescription

获取所有的子类:
屏幕截图 2025-08-19 142928
重写SetVisibility函数,当设置Description的Visibility时,先将所有子类先关闭:
屏幕截图 2025-08-19 142938
调换两个函数位置,先销毁,后显现:
屏幕截图 2025-08-19 143013

Bug:

需要初始化剩余物品,否则提前返回后剩余物品为0:
屏幕截图 2026-01-18 144511

Rotate Item

旋转物品:
1.当物品被捡起后,横着无法放下时,尝试旋转物品
2.当按下R键时,旋转物品

先在SlotAvailability中添加bIsRotated标志来判断是否被旋转了:
屏幕截图 2026-01-10 210929
在HasRoomForItem中尝试旋转物品:
屏幕截图 2026-01-10 211930
如果物品被旋转了,则更改SlottedItem的GridDimensions,物品的原先尺寸存储在GridFragment中的GridDimensions,
所以只需要判断这两个GridDimensions是否相同即可知道物品是否被旋转:

屏幕截图 2026-01-10 210837
如果需要旋转,则只需要旋转Widget即可:
屏幕截图 2026-01-10 211157
当R键按下时,调用InventoryGrid中的RotateDraggedItem函数,并添加判断,是否在拖拽时旋转:
屏幕截图 2026-01-10 211039

背包网格

添加BackpackGrid:
屏幕截图 2026-01-18 143434
在Spatial中初始化,并绑定事件用于同一管理:
屏幕截图 2026-01-18 143819
在Spatial中创建绑定函数
拖拽检测函数分别判断是否是BaseGrid,BackpackGrid,并处理EquippedGridSlots:

屏幕截图 2026-01-18 143833
拖拽取消函数,分别判断不同事件,并分为同一网格中移动,和不同网格中跨网格移动:
屏幕截图 2026-01-18 143854
获取鼠标位置,判断鼠标在哪个网格内:
屏幕截图 2026-01-18 143929
总的HasRoomForItem,优先填充BaseGrid,然后填充BackpackGrid:
添加Item时,通过总的判断结果来分别对BaseGrid和BackpackGrid进行填充:

屏幕截图 2026-01-18 143941
改变Item的Statck,分配Grid:
从总的Result中分离出Backpack专有的Result:

屏幕截图 2026-01-18 143954
屏幕截图 2026-01-18 144015
在开始时只初始化BaseGrid:
屏幕截图 2026-01-18 144118
添加判断,需要保证HoveredItem的InventoryGrid就是当前的Grid,否则不同网格之间的会导致问题:
屏幕截图 2026-01-18 144143
不能将装备栏中的背包拖到它自己中,背包里还有物品就不能放置在网格中:
屏幕截图 2026-01-18 144229
分割物品需要判断是否在不同网格之间分割:
屏幕截图 2026-01-18 144254
屏幕截图 2026-01-18 144309
处理不同网格之间的跨网格移动:
屏幕截图 2026-01-18 144419
初始化BackpackGrid,先保存当前背包中所有的存储物品,之后清除所有格子和UI,重新创建格子并将旧物品按位置放回去:
屏幕截图 2026-01-18 145856
屏幕截图 2026-01-18 145918
更换背包时,高亮闪烁背包变大或变小的格子:
屏幕截图 2026-01-18 145938
屏幕截图 2026-01-18 145951

posted @ 2025-11-15 10:25  pone1  阅读(4)  评论(0)    收藏  举报