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, //歌词传递方式

Inventory System Plugin

新输入增强系统

在cs文件中添加Module:
屏幕截图 2025-07-28 124056
添加IMX和Action以及对应的回调函数:
屏幕截图 2025-07-28 124030
先在BeginPlay中将输入添加到子系统中:
屏幕截图 2025-07-28 124041
创建输入:
屏幕截图 2025-07-28 121145
屏幕截图 2025-07-28 121219
屏幕截图 2025-07-28 121242
将Action添加到IMC中并选择按键:
屏幕截图 2025-07-28 121322
在PlayerController中进行赋值:
屏幕截图 2025-07-28 121453

LineTrace for Pickups

创建自定义Trace Channel,并初始化为Ignore All:
屏幕截图 2025-07-28 153927
创建自定义Collision Preset给PickupItem,并将ItemTrace设置为Block:
屏幕截图 2025-07-28 154136
添加Trace函数:
屏幕截图 2025-07-28 175804
根据屏幕中心点获取世界位置,然后进行Line Trace,并判断上一个物品是否和当前物品相同:
屏幕截图 2025-07-28 175825
屏幕截图 2025-07-28 175833
将Item的Collision Presets设置为Item:
屏幕截图 2025-07-28 154400
将Item Trace Channel设置为ItemTrace:
屏幕截图 2025-07-28 175938

添加PickUpMessage

在HUDWidget中创建Show和Hide Message的方法,并在蓝图中实现:
屏幕截图 2025-07-28 202708
屏幕截图 2025-07-28 202845
在Trace中调用对应函数:
屏幕截图 2025-07-28 202731
创建ActorComponent类作为Item的Component,并初始化Item的属性:
屏幕截图 2025-07-28 202744
创建一个以UserWidget为父类的PickupMessageWidget:
屏幕截图 2025-07-28 202900
将PickupMessage拖到HUD中:
image
屏幕截图 2025-07-28 202912
在ItemComponent中添加Blueprintable,以便创建蓝图类:
image
屏幕截图 2025-07-28 202919
在Item中添加刚刚创建的BP类ItemComponent:
屏幕截图 2025-07-28 202926
效果:
屏幕截图 2025-07-28 202942

添加高光效果

创建高光材质:
屏幕截图 2025-07-28 203846
配置材质:
屏幕截图 2025-07-28 204228
创建StaticMeshComponent用于设置高光:
屏幕截图 2025-07-28 204557
屏幕截图 2025-07-28 204642
创建Interface用于在其他地方也可以使用高光效果:
屏幕截图 2025-07-28 205023
屏幕截图 2025-07-28 205054
在Interface中创建 使用高光 和 关闭高光 的函数:
屏幕截图 2025-07-28 212056
在MeshComponent中实现该函数:
屏幕截图 2025-07-28 212158
屏幕截图 2025-07-28 212207
在Trace中获取 实现了指定接口的组件,即MeshComponent,并调用接口中的Highlight方法:
屏幕截图 2025-07-28 212242
创建MeshComponent的蓝图类
屏幕截图 2025-07-28 211346
屏幕截图 2025-07-28 211410
设置MeshComponent:
屏幕截图 2025-07-28 211755
在Item中添加MeshComponent:
屏幕截图 2025-07-28 211538

添加库存系统

添加InventoryComponent类:
屏幕截图 2025-07-29 105525
添加一个InventoryWidget的基类:
屏幕截图 2025-07-29 112035
创建子类SpatialInventoryWidget:
屏幕截图 2025-07-29 113002
屏幕截图 2025-07-29 113103
在Component中添加 初始化库存 和 打开关闭库存 函数:
屏幕截图 2025-07-29 133625
屏幕截图 2025-07-29 133653
在PlayerController中添加InputAction:
屏幕截图 2025-07-29 133710
从蓝图中查找InventoryComponent并初始化,绑定Input Action:
屏幕截图 2025-07-29 133730
创建蓝图类的SpatialInventoryWidget:
屏幕截图 2025-07-29 120227
屏幕截图 2025-07-29 120315
屏幕截图 2025-07-29 121156
在蓝图中创建InventoryComponent:
屏幕截图 2025-07-29 120033
屏幕截图 2025-07-29 120125
将MenuClass设置为WBP_SpatialInventoryWidget:
屏幕截图 2025-07-29 121057
在PlayerController中添加InventoryComponent:
屏幕截图 2025-07-29 121239
绑定Input Action:
屏幕截图 2025-07-29 125222
屏幕截图 2025-07-29 125230
在PlayerController中配置对应Action:
屏幕截图 2025-07-29 125251

添加不同类型的Grid以及Switcher Button

添加GridType.h:
屏幕截图 2025-07-29 180605
添加Grid类:
屏幕截图 2025-07-29 154333
初始化对应Gird的Category:
屏幕截图 2025-07-29 180646
创建3个不同类型的Grid、Button以及Switcher,点击对应的Button时,禁用该Button并切换Grid:
屏幕截图 2025-07-29 180502
创建Grid蓝图类:
屏幕截图 2025-07-29 165056
暂时添加一个Text用于测试:
屏幕截图 2025-07-29 174542
不同的Category对应不同的数字:
屏幕截图 2025-07-29 174551
在InventoryWidget中创建3个Button,1个Switcher包含3个Grid,并设置3个Grid的Item Category:
屏幕截图 2025-07-29 174632
效果:
屏幕截图 2025-07-29 174124
屏幕截图 2025-07-29 174128
屏幕截图 2025-07-29 174131

添加Grid Slot

添加GridSlot Widget类:
屏幕截图 2025-07-29 191959
添加Slot的Index以及 设置 和 获取Index函数:
屏幕截图 2025-07-29 222955
添加Utils工具函数类,专门存放工具函数:
屏幕截图 2025-07-29 202852
存放计算当前格子的索引值的工具函数:
image
在Grid中添加Grid Slot并添加Slot的行数、列数、大小,绑定CanvasPanel用于在上面添加Slot:
屏幕截图 2025-07-29 225600
修改:
上面的行数和列数写反了:
image
上面的i和j写反了:
image
创建Slot蓝图类:
屏幕截图 2025-07-29 205445
屏幕截图 2025-07-29 205458
删除Inventory中的SwicherBackGround:
屏幕截图 2025-07-29 210627
屏幕截图 2025-07-29 210633
在Grid中添加BackGround并添加Canvas用于存储Slot:
屏幕截图 2025-07-29 211152
在Grid中配置Slot对应的参数:
屏幕截图 2025-07-29 222820
配置Slot:
屏幕截图 2025-07-29 222708
还需要在Inventory中分别对3个Grid配置Grid Class:
屏幕截图 2025-07-29 212146
效果:
屏幕截图 2025-07-29 222847

Add Fast Array

创建FastArray文件:
屏幕截图 2025-07-30 110856
屏幕截图 2025-07-30 110923
创建InventoryItem:
屏幕截图 2025-07-30 111906
添加NetCore Model:
屏幕截图 2025-07-30 112712
设置FastArray:
屏幕截图 2025-07-30 150336
屏幕截图 2025-07-30 150350
屏幕截图 2025-07-30 150420
添加委托:
屏幕截图 2025-07-30 150429
屏幕截图 2025-07-30 150545

Add No Room in Inventory

声明一个No Room的委托以及一个TryAddItem的函数,并在里面进行NoRoom Broadcast用于测试::
屏幕截图 2025-07-30 184013
添加消息Widget类:
屏幕截图 2025-07-30 172924
添加Text,并在蓝图中添加Show和Hide Message函数定义,通过SetMessage函数让Text在LifeTime后消失:
屏幕截图 2025-07-30 184415
在HUD中创建并绑定InfoMessage,创建回调函数OnNoRoom并绑定委托:
屏幕截图 2025-07-30 184509
在PlayerController中F键按下后,TryAddItem:
屏幕截图 2025-07-30 184850
在蓝图中创建WBP_InfoMessage Widget:
屏幕截图 2025-07-30 181430
为Text创建Fade Animation:
屏幕截图 2025-07-30 181913
设置Show和Hide Message函数:
屏幕截图 2025-07-30 182036
在HUD中添加WBP_InfoMessage:
屏幕截图 2025-07-30 182214
效果:
屏幕截图 2025-07-30 183833

Check Room for Item:

创建两个结构体用于存储数据:
屏幕截图 2025-07-30 205528
在父类BaseInventory中声明是否有Room的函数,并返回空的SlotAvailabilityResult:
屏幕截图 2025-07-30 205641
在子类SpatialIventory中定义该函数:
屏幕截图 2025-07-30 205753
在TryAddItem中使用该函数判断是否有空间:
屏幕截图 2025-07-30 205905

Item Manifest

创建ItemManifest类,用于存储Item的所有信息:
屏幕截图 2025-07-31 115503
屏幕截图 2025-07-31 115523
添加StructUtils Model:
屏幕截图 2025-07-31 124531
屏幕截图 2025-07-31 164102
屏幕截图 2025-07-31 164130
屏幕截图 2025-07-31 164211
屏幕截图 2025-07-31 164353
屏幕截图 2025-07-31 164455
在ItemComponent中设置Manifest中的Category:
屏幕截图 2025-07-31 162514

Item Type Tags

添加Tag类:
屏幕截图 2025-07-31 165313
添加GamePlayTags Model:
屏幕截图 2025-07-31 165519
在.h中声明,在.cpp中定义Tag:
屏幕截图 2025-07-31 172111
在Manifest中添加ItemType:
屏幕截图 2025-07-31 172532
在ItemComponent中设置具体的Item Type:
屏幕截图 2025-07-31 171903

On Item Added

添加获取ItemManifest的函数:
屏幕截图 2025-07-31 184244
添加AddItem回调函数,若MatchesCategory则AddItem:
屏幕截图 2025-07-31 184538
如果是服务器,则直接Broadcast:
屏幕截图 2025-07-31 184636
设置FastArray的Component:
屏幕截图 2025-07-31 184648

Item Fragment

创建ItemFragment类:
屏幕截图 2025-08-01 122304
屏幕截图 2025-08-01 122418
创建FragmentTags:
屏幕截图 2025-08-01 153832
屏幕截图 2025-08-01 153849
创建父类结构体Fragment,子类结构体GridFragment继承父类结构体:
屏幕截图 2025-08-01 160808
在Manifest中创建Fragments变量:
屏幕截图 2025-08-01 160823
定义Fragment Tags:
屏幕截图 2025-08-01 160836
在蓝图中设置Fragments:
屏幕截图 2025-08-01 155431
添加子结构体ImageFragment:
屏幕截图 2025-08-01 164213
屏幕截图 2025-08-01 164227
屏幕截图 2025-08-01 163638

Has Room For Item

在Grid中添加HasRoomForItem函数并添加重载函数:
屏幕截图 2025-08-01 173328
在SpatialInventory的HasRoomForItem函数中使用switch分别调用对应Grid的HasRoomForItem函数:
屏幕截图 2025-08-01 173429

Add Item to Indices

屏幕截图 2025-08-01 201216
屏幕截图 2025-08-01 201240
屏幕截图 2025-08-01 201333

Create Widget to add to the grid

创建Widget类:
屏幕截图 2025-08-02 105150
绑定Image Icon:
屏幕截图 2025-08-02 143122
添加Widget到AddItem中:
屏幕截图 2025-08-02 143033

Add Slotted Items to Canvas

创建工具函数,通过Index获取Position:
屏幕截图 2025-08-02 160744
将Slotted Items添加到Canvas中并设置大小和位置:
屏幕截图 2025-08-02 160858
创建SlottedItem Widget蓝图类:
屏幕截图 2025-08-02 153157
屏幕截图 2025-08-02 153211
添加Image_Icon:
屏幕截图 2025-08-02 153323
在Grid中设置Slotted Item Class:
屏幕截图 2025-08-02 153338
在测试之前,需要先删除原先的Item,因为细节面板不会刷新,修改细节面板需要在对应物品的Instance中修改
效果:
屏幕截图 2025-08-02 160525

Grid Slot Texture

在Slot中添加GridSlotState,并设置不同的Brush对应不同的State:
屏幕截图 2025-08-03 122042
创建遍历二维网格的模板函数:
屏幕截图 2025-08-03 122135
为每个Slot设置Texture:
屏幕截图 2025-08-03 122151
在Slot中配置FSlateBrush:
屏幕截图 2025-08-03 111300
效果:
屏幕截图 2025-08-03 120006

Add Stack Count

创建一个新的子结构Stackable Fragment用于设置 物品的最大存储容量 和 物品的数量:
屏幕截图 2025-08-03 152025
添加Tag:
屏幕截图 2025-08-03 152041
在SlottedItem中添加Text用于显示StackCount:
屏幕截图 2025-08-03 152125
用于测试的数据:
屏幕截图 2025-08-03 152155
在创建SlottedItem时UpdateStackCount:
屏幕截图 2025-08-03 152215
在蓝图中设置:
屏幕截图 2025-08-03 153213
添加Text:
屏幕截图 2025-08-03 151844
效果:
屏幕截图 2025-08-03 151857

Update Grid Slot

屏幕截图 2025-08-03 163233
屏幕截图 2025-08-03 163247

Add Item in the inventory

获取FragmentOfType:
屏幕截图 2025-08-04 205559
判断是否可以堆叠:
屏幕截图 2025-08-04 205654
屏幕截图 2025-08-04 221843
屏幕截图 2025-08-04 221938
屏幕截图 2025-08-04 221947
屏幕截图 2025-08-04 222008
查找第一个与指定类型匹配的物品:
屏幕截图 2025-08-04 205425
屏幕截图 2025-08-04 222353

添加当前物品的堆叠总数

屏幕截图 2025-08-05 115415
屏幕截图 2025-08-05 115431

Pick up 后 Destroy

添加可以Mutable函数,即非const函数:
屏幕截图 2025-08-05 124306
添加设置StackCount函数:
屏幕截图 2025-08-05 124339
PickedUp函数:
屏幕截图 2025-08-05 124408
屏幕截图 2025-08-05 124438
在蓝图中将Item的Replicates打开,确保在客户端可以使用:
屏幕截图 2025-08-05 123955

Add Stacks Count

添加委托,并在添加可堆叠物品时调用:
屏幕截图 2025-08-05 150718
屏幕截图 2025-08-05 150820
更新物品和槽位的堆叠数量:
屏幕截图 2025-08-05 150947
效果:
屏幕截图 2025-08-05 143217

创建Hover Item

创建Widget类:
屏幕截图 2025-08-06 114733
屏幕截图 2025-08-06 124504
创建Widget蓝图类:
屏幕截图 2025-08-06 123819
屏幕截图 2025-08-06 123841
配置Hover Item:
屏幕截图 2025-08-06 124128
创建InputCore Model:
屏幕截图 2025-08-06 142858
添加点击事件委托:
屏幕截图 2025-08-06 155808
屏幕截图 2025-08-06 155844
绑定委托:
屏幕截图 2025-08-06 155913
屏幕截图 2025-08-06 155939
屏幕截图 2025-08-06 155953
屏幕截图 2025-08-06 145151
效果:
屏幕截图 2025-08-06 160036

添加Tile Parameters

添加枚举类型,用于判断鼠标处于哪个象限:
屏幕截图 2025-08-06 171412
获取Widget的位置:
屏幕截图 2025-08-06 171505
在Tick中获取画布位置和鼠标位置,并传递给函数用来更新Tile parameters:
屏幕截图 2025-08-06 171523

Highlight and UnHighlight Hover Item

创建工具函数,用于获取Widget Size和判断鼠标是否处于Canvas内:
屏幕截图 2025-08-07 164758
创建结构体,用于判断Hover时处于的格子是否有物品:
屏幕截图 2025-08-07 164834
屏幕截图 2025-08-07 165550
屏幕截图 2025-08-07 165344
屏幕截图 2025-08-07 164920
屏幕截图 2025-08-07 165201
屏幕截图 2025-08-07 165247
屏幕截图 2025-08-07 165307
屏幕截图 2025-08-07 165318
屏幕截图 2025-08-07 165326
将GridSlot中bAvailable默认设置为true:
image
将CanvasPanel用Overlay包裹,不然无法计算:
屏幕截图 2025-08-07 145624
效果:
屏幕截图 2025-08-07 150742
屏幕截图 2025-08-07 151907

Swap Item

添加StackCount = Count用于记录StackCount:
屏幕截图 2025-08-09 135742
添加委托事件,用于处理鼠标进入,离开,点击GridSlot事件:
屏幕截图 2025-08-09 140248
屏幕截图 2025-08-09 145721
屏幕截图 2025-08-09 145734
屏幕截图 2025-08-09 145805
屏幕截图 2025-08-09 145844
屏幕截图 2025-08-09 145928
屏幕截图 2025-08-09 145942
屏幕截图 2025-08-09 150000
屏幕截图 2025-08-09 150012
屏幕截图 2025-08-09 150038
屏幕截图 2025-08-09 150049
屏幕截图 2025-08-09 150247
为3类Grid创建不同的鼠标指针:
屏幕截图 2025-08-08 201318
屏幕截图 2025-08-08 202048
为3类Grid分别配置Visible和Hidden Cursor Widget Class:
屏幕截图 2025-08-09 151156

ItemPopUpMenu

创建ItemPopUp类:
屏幕截图 2025-08-10 112340
创建3种委托:
屏幕截图 2025-08-10 185402
屏幕截图 2025-08-10 185417
屏幕截图 2025-08-10 185439
屏幕截图 2025-08-10 185455
判断物品是否为Consumable:
屏幕截图 2025-08-10 185511
创建设置CanvasPanel的函数:
屏幕截图 2025-08-10 185953
委托的回调函数:
屏幕截图 2025-08-10 190008
绑定ItemPopUp:
屏幕截图 2025-08-10 190433
点击时隐藏ItemPopUpMenu:
屏幕截图 2025-08-10 190451
屏幕截图 2025-08-10 190515
根据不同Item类型创建不同的ItemPopUpMenu:
屏幕截图 2025-08-10 190542
设置CanvasPanel:
屏幕截图 2025-08-10 190625
隐藏PopUpMenu:
屏幕截图 2025-08-10 190640
屏幕截图 2025-08-10 190701
创建ItemPopUp蓝图类:
屏幕截图 2025-08-10 123534
屏幕截图 2025-08-10 123547
添加SizeBox,Button,Split:
屏幕截图 2025-08-10 125223
配置Item Pop Up Class:
屏幕截图 2025-08-10 131129

Split,Drop,Consume Item

创建丢弃区域类:
屏幕截图 2025-08-11 181956
创建委托,当在丢弃区域鼠标按下时触发:
屏幕截图 2025-08-11 210859
创建丢弃函数:
屏幕截图 2025-08-11 203519
设置ItemPopUp的GridIndex:
屏幕截图 2025-08-11 203529
屏幕截图 2025-08-11 204433
设置ItemManifest:
屏幕截图 2025-08-11 204504
在ItemManifest中创建当前物品的Class,并添加生成Actor的函数:
屏幕截图 2025-08-11 204849
在Spatial中创建丢弃区域类,并绑定委托,执行丢弃物品函数:
屏幕截图 2025-08-11 210842
在Fragment中创建Consumable Fragment,并创建2个Consumable Fragment的子类:
屏幕截图 2025-08-11 210928
创建Consumable的Tag:
屏幕截图 2025-08-11 210941
将Component Replicates 设置为True:
屏幕截图 2025-08-11 163008
添加Health Fragment 和 Pickup Actor Class:
屏幕截图 2025-08-11 203403

Item Description

创建ItemDescription Widget类:
屏幕截图 2025-08-12 105255
设置SizeBox:
屏幕截图 2025-08-12 143536
在BaseWidget中创建父函数:
屏幕截图 2025-08-12 143551
判断是否存在HoverItem:
屏幕截图 2025-08-12 143633
从InventoryComponent中获取InventoryBaseWidget:
屏幕截图 2025-08-12 144732
创建工具函数,调用OnItemHovered和OnItemUnHovered函数 并 创建获取控件位置的函数:
屏幕截图 2025-08-12 144713
在SlottedItem中创建鼠标进入和离开函数,分别调用对应的工具函数:
屏幕截图 2025-08-12 144903
在BaseWidget的子类SpatialWidget中添加继承父类的函数 并 添加描述界面的Class:
屏幕截图 2025-08-12 150915
延迟一段时间后显示描述界面:
屏幕截图 2025-08-12 150954
屏幕截图 2025-08-12 151007
当物品被点击并处于Hover状态时,隐藏描述界面:
屏幕截图 2025-08-12 143724
创建Item Description蓝图类:
屏幕截图 2025-08-12 110729
屏幕截图 2025-08-12 110744
屏幕截图 2025-08-12 111436
设置Item Description Class:
屏幕截图 2025-08-12 125537

Composite Pattern

创建UInv_CompositeBase基类,定义了组合模式中的通用接口和方法:
屏幕截图 2025-08-12 153712
创建UInv_Composite组合类,继承自 UInv_CompositeBase:
屏幕截图 2025-08-12 162748
创建UInv_Leaf叶子节点类,继承自 UInv_CompositeBase:
屏幕截图 2025-08-12 173530
获取和设置标签,Collapse和Expand功能,应用函数(ApplyFunction)用于递归操作子节点
屏幕截图 2025-08-12 175357
初始化时收集子节点,递归调用子节点的 ApplyFunction 和 Collapse
屏幕截图 2025-08-12 175408
直接执行传入的函数(ApplyFunction)
屏幕截图 2025-08-12 175415
将ItemDescription的父类改为Composite:
屏幕截图 2025-08-12 175450

Assimilate Inventory Fragments

玩家悬停一个物品时,系统获取该物品的 Manifest(包含所有片段),通过 AssimilateInventoryFragments将片段同化到描述控件(DescriptionWidget)
屏幕截图 2025-08-12 195121
屏幕截图 2025-08-12 195131
屏幕截图 2025-08-12 195143
屏幕截图 2025-08-12 195204

Image Fragment

创建Image的叶子节点并继承自叶子类:
屏幕截图 2025-08-12 203048
创建Image Icon并配置大小:
屏幕截图 2025-08-12 211610
将ImageFragment的父类改为InventoryItemFragment并重写父类的同化函数:
屏幕截图 2025-08-12 211815
创建Leaf_Image蓝图类:
屏幕截图 2025-08-12 210420
屏幕截图 2025-08-12 210444
在Description Widget中添加Leaf Icon Widget 并 配置Fragment Tag:
屏幕截图 2025-08-12 211506
效果:
屏幕截图 2025-08-12 211424

Text Fragment

创建Leaf_Text类:
屏幕截图 2025-08-13 111816
创建Text:
屏幕截图 2025-08-13 141852
创建Text Fragment,用于在Manifest中修改对应物品的属性:
屏幕截图 2025-08-13 141824
创建FragmentTag,用于确保LeafText和Item中的TextFragment属性一致:
屏幕截图 2025-08-13 142044
创建蓝图类:
屏幕截图 2025-08-13 122426
屏幕截图 2025-08-13 122439
屏幕截图 2025-08-13 122615
设置Leaf_ItemName的FragmentTag:
屏幕截图 2025-08-13 122622
设置ItemManifest的Text和FragmentTag:
屏幕截图 2025-08-13 141341
效果:
屏幕截图 2025-08-13 141422

Labeled Number Fragment

创建LabeledValue类:
屏幕截图 2025-08-13 144210
设置Text和Value:
屏幕截图 2025-08-13 164304
在父类中添加Manifest继承函数:
屏幕截图 2025-08-13 164745
添加LabeledNumberFragment并重写父类的Manifest函数,用于在开始时随机初始化Value:
屏幕截图 2025-08-13 164805
在Manifest中调用每个Fragment的Manifest函数:
屏幕截图 2025-08-13 165033
创建PrimaryStatFragment:
屏幕截图 2025-08-13 165047
创建蓝图类:
屏幕截图 2025-08-13 155807
屏幕截图 2025-08-13 160326
设置Tag:
屏幕截图 2025-08-13 160333
添加到Description Widget中:
屏幕截图 2025-08-13 160551
添加Item的Fragment并设置Tag:
屏幕截图 2025-08-13 160748
效果:
屏幕截图 2025-08-13 161230

添加Consume Modifiers并确保随机值Value与使用时相同

更改Consumable的父类为InventoryItemFragment,添加Consumable的子类ConsumeModifiers但子类继承自LabelNumberFragment,修改Health和Eat的父类为ConsumeModifiers:
屏幕截图 2025-08-13 191451
添加3种属性标签:
屏幕截图 2025-08-13 191504
添加一个新的LabeledValue Widget:
屏幕截图 2025-08-13 185704
屏幕截图 2025-08-13 185747
屏幕截图 2025-08-13 185948
在Description中添加3个StaticModifier并配置对应的Tag:
屏幕截图 2025-08-13 191200
屏幕截图 2025-08-13 191206
屏幕截图 2025-08-13 191212
在Item中删除原先的Health Fragment和Labeled Number Fragment 并 添加Consumable Fragment,添加两个Consume Modifiers:
屏幕截图 2025-08-13 191230
效果:
屏幕截图 2025-08-13 191240

Add more widget

添加更多的Fragment Tag:
屏幕截图 2025-08-13 204424
添加更多的Widget:
屏幕截图 2025-08-13 204650
添加到Description Widget中:
屏幕截图 2025-08-13 204355
在Item中配置对应Fragment:
屏幕截图 2025-08-13 204901

Add Equipped Grid Slots

创建以GridSlot为父类的EquippedGridSlot类:
屏幕截图 2025-08-15 103413
在Grid中添加获取HoverItem的函数:
屏幕截图 2025-08-15 123408
在BaseInventory中添加获取HoverItem的虚函数:
屏幕截图 2025-08-15 123423
在Spatial中实现父类添加获取HoverItem的虚函数 并 创建数组以及点击的回调函数:
屏幕截图 2025-08-15 123749
遍历所有Widget来获取EquippedGridSlot并绑定委托:
屏幕截图 2025-08-15 123813
创建工具函数获取HoverItem:
屏幕截图 2025-08-15 123903
在EquippedGridSlot中通过HoverItem是否与EquipmentTypeTag一致来判断进入或离开:
屏幕截图 2025-08-15 124127
创建多个蓝图类:
屏幕截图 2025-08-15 115806
屏幕截图 2025-08-15 120701
设置每个的Equipment Type Tag:
屏幕截图 2025-08-15 120714
添加到Spatial Widget中:
屏幕截图 2025-08-15 123152
效果:
屏幕截图 2025-08-15 123349

Equip the Cloak

创建EquippedSlottedItem Widget类并继承自SlottedItem类:
屏幕截图 2025-08-15 175627
在GridSlot中创建函数用于Create EquippedSlottedItem Widget:
屏幕截图 2025-08-15 204143
创建Equipped Fragment,与Consume Fragment类似:
屏幕截图 2025-08-15 204225
设置EquippedSlottedItem并添加点击事件:
屏幕截图 2025-08-15 204257
创建工具函数用于获取InventoryWidget:
屏幕截图 2025-08-15 204333
在InventoryComponent中创建委托并创建EquipSlotClicked的多播函数:
屏幕截图 2025-08-15 204412
在Spatial Widget中设置EquippedGridSlotClicked的绑定函数,并调用InventoryComponent的多播函数:
屏幕截图 2025-08-15 205018
创建EquippedSlottedItem Widget的蓝图类:
屏幕截图 2025-08-15 202532
屏幕截图 2025-08-15 202545
为3个EquippedGridSlot都设置Equipped Slotted Item Class:
屏幕截图 2025-08-15 202712
效果:
屏幕截图 2025-08-15 203237

Equip and UnEquip Cloak

处理EquippedSlot的点击事件:
屏幕截图 2025-08-16 151718
屏幕截图 2025-08-16 151747
屏幕截图 2025-08-16 151840
装备/卸下事件:
屏幕截图 2025-08-16 151851

Bug:切换Gird或关闭库存会导致HoverItem丢失

在Component中创建委托,用于判断Inventory的开关:
屏幕截图 2025-08-16 152047
创建将HoverItem放回去的函数,并为HasRoomForItem添加强制堆叠数量,从而确保HoverItem放回时的数量正确:
屏幕截图 2025-08-16 152413
绑定函数,若库存关闭,则调用PutHoverItemBack,若没有Room则DropItem:
屏幕截图 2025-08-16 152514
配置强制指定的数量:
屏幕截图 2025-08-16 152946
若切换Grid,则调用PutHoverItemBack:
屏幕截图 2025-08-16 153124

Bug:格子有物品却可以放下并重叠

添加判断,格子是否为空:
屏幕截图 2025-08-16 153001

Bug:交换物品时,物品位置不对 并且 添加若被点击物品数量为最大值,则交换物品而不是只交换StackCount:

屏幕截图 2025-08-16 153030

Bug:拾取物品时,若还有剩余,物品剩余数量却没有减少 并且 捡起新物品时,也应该判断是否会有剩余

屏幕截图 2025-08-16 161555
屏幕截图 2025-08-16 161735

Equip Component

创建Equip Component类:
屏幕截图 2025-08-18 105119
通过InventoryComponent绑定委托,并在回调函数中调用EquipmentFragment的Equip函数:
屏幕截图 2025-08-18 123326
创建EquipmentFragment Tag:
屏幕截图 2025-08-18 123337
在EquipmentFragment中创建Manifest函数:
屏幕截图 2025-08-18 123403
将清除HoverItem的函数移动到最后使用:
屏幕截图 2025-08-18 123421
创建EquipComponent蓝图类:
屏幕截图 2025-08-18 113230
屏幕截图 2025-08-18 113242
添加到PlayerController中:
屏幕截图 2025-08-18 113333
在Cloak中添加Equipment Fragment:
屏幕截图 2025-08-18 115512
将Cloak改为Replicates:
屏幕截图 2025-08-18 115553
效果:
屏幕截图 2025-08-18 121136

Spawn Equip Actor

创建EquipActor类用于Spawn:
屏幕截图 2025-08-18 125709
在EquipActor中添加EquipmentType:
屏幕截图 2025-08-18 161918
在EquipmentFragment中添加要Spawn的EquipActorClass和EquipActor和SocketAttachPoint和EquipmentTag,以及Spawn和Destroy的函数:
屏幕截图 2025-08-18 161946
在EquipComponent中添加Spawn和Remove的函数:
屏幕截图 2025-08-18 161622
初始化Inventory Component:
屏幕截图 2025-08-18 161637
屏幕截图 2025-08-18 161658
屏幕截图 2025-08-18 161714
添加EquipActor蓝图类:
屏幕截图 2025-08-18 151413
cloak的Spawn Item类:
屏幕截图 2025-08-18 151428
添加SkeletalMesh:
屏幕截图 2025-08-18 151548
设置Equipment Tag为Red Cloak:
屏幕截图 2025-08-18 151933
在Equipment Fragment中添加Equip Actor Class:
屏幕截图 2025-08-18 151807

Proxy Mesh

创建ProxyMeshActor类:
屏幕截图 2025-08-18 165723
在EquipComponent中为ProxyMesh设置函数:
屏幕截图 2025-08-18 183004
屏幕截图 2025-08-18 183228
屏幕截图 2025-08-18 183233
屏幕截图 2025-08-18 183243
创建ProxyMeshActor蓝图类:
屏幕截图 2025-08-18 181720
屏幕截图 2025-08-18 181745
效果:
屏幕截图 2025-08-18 182254

Character Display

创建CharacterDisplay Widget类:
屏幕截图 2025-08-18 183636
获取ProxyMeshActor的Mesh:
屏幕截图 2025-08-18 223200
通过获取的Mesh创建新的Mesh并判断鼠标拖拽时,角色旋转:
屏幕截图 2025-08-18 223331
在SpatialWidget中创建CharacterDisplay并在鼠标松开或关闭库存时设置停止拖拽:
屏幕截图 2025-08-18 223407
取消ProxyMeshActor的复制:
屏幕截图 2025-08-19 110445
创建蓝图类:
屏幕截图 2025-08-18 195856
屏幕截图 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
在ProxyMeshActor中添加SpringArm和SceneCaptureComponent2D,并将Texture设置为Render Target,并隐藏除了Skeletal Mesh以外的:
屏幕截图 2025-08-18 215419
将ProxyMeshActor拖到世界中即可:
只有在打包后才会在多人游戏中正确运行 或者 取消Run Under One Process进行测试:
屏幕截图 2025-08-19 110646
效果:
屏幕截图 2025-08-18 222608

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

获取所有的子类:
屏幕截图 2025-08-19 142928
设置Visibility时,将所有子类先销毁:
屏幕截图 2025-08-19 142938
调换两个函数位置,先销毁,后同化:
屏幕截图 2025-08-19 143013

将PlayerController的内容移动到新的Actor Component:Inv_InteractComponent,并将其添加到PlayerController中:

All, with Sensei's permission, I am posting my changes to the plugin. I have moved all the functionality from the PlayerController into a separate component called Inv_InteractComponent. By separating this functionality from the player controller, this makes the plugin attachable to any existing PC with no changes to the PC, unless you have a functional conflict, like the LineTracing, etc. being done previously.

Instructions:
Important: If you have been modifying your own PC for your own project, you will need to remove all the course code from it for this to work! If you are worried about bricking your project because it is currently working for you, then don't do this on that project. Do it on a blank project like what we created for the course at the start. I am not liable for you screwing up your existing project - this is intended to augment Sensei Stephen's course as taught.

BACKUP YOUR PROJECT FIRST
  1. Add the Inv_InteractComponent files to the InventoryManagement/Components public and private folders
  2. Replace your Inv_PlayerController files from the course with the empty ones I have attached if you want a quick replacement. Otherwise create your own empty Inv_PlayerController class, or remove all of the existing interaction code that we put into it.
  3. Compile and launch editor
  4. Make a blueprint of the Inv_InteractComponent class and fill in its fields in the Inventory category
  5. Attach the component to the player controller blueprint alongside the Inv_InventoryComponent

Important change:
6) Modify the close button blueprint to call Get Component By Class from the PlayerController, and select Inv_InteractComponent. Then call ToggleInventory from that. Pic is attached.
WBP_CloseButton_-change

点击查看代码
// Fill out your copyright notice in the Description page of Project Settings.

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Inv_InteractComponent.generated.h"

class UInv_InventoryComponent;
class UInv_HUDWidget;
class UInputMappingContext;
class UInputAction;

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FShortItemDescription, UInv_ItemComponent*, ItemComponent);

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent), Blueprintable)
class INVENTORYSYSTEM_API UInv_InteractComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UInv_InteractComponent();
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UFUNCTION(BlueprintCallable, Category = "Inventory")
	void ToggleInventory();

	FShortItemDescription CreateShortItemDescription;
	FShortItemDescription DestroyShortItemDescription;
	
protected:
	virtual void BeginPlay() override;
	virtual void SetupInputComponent();

private:
	void PrimaryInteract();
	void CreateHUDWidget();
	void TraceForItem();

	TWeakObjectPtr<UInv_InventoryComponent> InventoryComponent;

	UPROPERTY(EditDefaultsOnly, Category = "Inventory")
	TObjectPtr<UInputMappingContext> DefaultIMC;

	UPROPERTY(EditDefaultsOnly, Category = "Inventory")
	TObjectPtr<UInputAction> PrimaryInteractAction;

	UPROPERTY(EditDefaultsOnly, Category = "Inventory")
	TObjectPtr<UInputAction> ToggleInventoryAction;

	//assign the class for our HUD Widget in the editor
	UPROPERTY(EditDefaultsOnly, Category = "Inventory")
	TSubclassOf<UInv_HUDWidget> HUDWidgetClass;

	UPROPERTY()
	TObjectPtr<UInv_HUDWidget> HUDWidget;

	UPROPERTY(EditDefaultsOnly, Category = "Inventory")
	double TraceLength;

	// Cannot expose an ENum directly for changing in the editor with UPROPERTY. It muSt be wrapped in a
	// TEnumAsByte<> for it to appear as a list.
	UPROPERTY(EditDefaultsOnly, Category = "Inventory")
	TEnumAsByte<ECollisionChannel> ItemTraceChannel;

	TWeakObjectPtr<AActor> ThisActor; //actor hit in this frame
	TWeakObjectPtr<AActor> LastActor; //actor hit last frame

};
点击查看代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "InventorySystem/InventoryManagement/Components/Inv_InteractComponent.h"

#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InventorySystem/InventoryManagement/Components/Inv_InventoryComponent.h"
#include "InventorySystem/Interaction/Inv_Highlightable.h"
#include "InventorySystem/Items/Components/Inv_ItemComponent.h"
#include "Kismet/GameplayStatics.h"
#include "InventorySystem/Widgets/HUD/Inv_HUDWidget.h"
#include "InventorySystem/Widgets/Utils/Inv_WidgetUtils.h"


class UEnhancedInputLocalPlayerSubsystem;

UInv_InteractComponent::UInv_InteractComponent()
{
	PrimaryComponentTick.bCanEverTick = true;
	TraceLength = 500.0f;
	ItemTraceChannel = ECC_GameTraceChannel1;
}

void UInv_InteractComponent::BeginPlay()
{
	Super::BeginPlay();
	SetupInputComponent();
	CreateHUDWidget();
	InventoryComponent = GetOwner()->FindComponentByClass<UInv_InventoryComponent>();
}

// *********************************************************************************************************
//   Actor Components do not have a SetupInputComponent function that can be overriden.
//   Therefore, this is defined manually and called from BeginPlay.
//   Get the PlayerController this is attached to and then bind actions to the PC's Enhanced Input Component
// *********************************************************************************************************
void UInv_InteractComponent::SetupInputComponent()
{
	if (const APlayerController* PC = Cast<APlayerController>(GetOwner()))
	{
		if (const ULocalPlayer* LocalPlayer = PC->GetLocalPlayer())
		{
			if (UEnhancedInputLocalPlayerSubsystem* EISubSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
			{
				EISubSystem->AddMappingContext(DefaultIMC, 0);
			}
		}

		// Retrieve the EnhancedInputComponent from the PC
		if (UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PC->InputComponent))
		{
			// now bind exactly as we did in SetupInputComponent on the PC
			EIC->BindAction(PrimaryInteractAction, ETriggerEvent::Started,this, &ThisClass::PrimaryInteract);
			EIC->BindAction(ToggleInventoryAction, ETriggerEvent::Started,this, &ThisClass::ToggleInventory);
		}
	}
}

void UInv_InteractComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	//Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	TraceForItem();
}


void UInv_InteractComponent::PrimaryInteract()
{
	if (!ThisActor.IsValid()) return;

	UInv_ItemComponent* ItemComp = ThisActor->FindComponentByClass<UInv_ItemComponent>();
	if (!IsValid(ItemComp) || !InventoryComponent.IsValid()) return;

	InventoryComponent->TryAddItem(ItemComp);
}

void UInv_InteractComponent::CreateHUDWidget()
{
	APlayerController* PC = Cast<APlayerController>(GetOwner());
	if (!PC->IsLocalController()) return; 
	
	HUDWidget = CreateWidget<UInv_HUDWidget>(PC, HUDWidgetClass);
	if (IsValid(HUDWidget))
	{
		HUDWidget->AddToViewport();
	}
}

void UInv_InteractComponent::ToggleInventory()
{
	if (!InventoryComponent.IsValid()) return;
	InventoryComponent->ToggleInventoryMenu();
	if (InventoryComponent->IsMenuOpen())
	{
		HUDWidget->SetVisibility(ESlateVisibility::Hidden);
	}
	else
	{
		// 将鼠标指针恢复到中心点
		if (APlayerController* PC = Cast<APlayerController>(GetOwner()))
		{
			FVector2D ViewportSize;
			GEngine->GameViewport->GetViewportSize(ViewportSize); // 获取视口大小
			const FVector2D ViewportCenter = ViewportSize / 2.f; // 获取中心点位置
			PC->SetMouseLocation(ViewportCenter.X, ViewportCenter.Y);
		}

		HUDWidget->SetVisibility(ESlateVisibility::HitTestInvisible);
	}
}


void UInv_InteractComponent::TraceForItem()
{
	if (!IsValid(GEngine) || !IsValid(GEngine->GameViewport)) return;

	const APlayerController* PC = Cast<APlayerController>(GetOwner());
	
	FVector2D ViewportSize;
	GEngine->GameViewport->GetViewportSize(ViewportSize); // 获取视口大小
	const FVector2D ViewportCenter = ViewportSize / 2.f; // 获取中心点位置

	FVector TraceStart;
	FVector Forward;
	bool bSuccessful = UGameplayStatics::DeprojectScreenToWorld(PC, ViewportCenter, TraceStart, Forward); // 转换为世界位置
	if (!bSuccessful) return;

	const FVector TraceEnd = TraceStart + Forward * TraceLength;

	FHitResult HitResult;
	GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ItemTraceChannel); // Line Trace

	LastActor = ThisActor;
	ThisActor = HitResult.GetActor();

	if (!ThisActor.IsValid())
	{
		// Hide Message
		if (IsValid(HUDWidget)) HUDWidget->HidePickupMessage(); 
	}

	if (ThisActor == LastActor) return;

	if (ThisActor.IsValid())
	{
		// High light
		UActorComponent* Highlightable = ThisActor->FindComponentByInterface(UInv_Highlightable::StaticClass());
		if (IsValid(Highlightable))
		{
			IInv_Highlightable::Execute_Highlight(Highlightable);
		}

		// Show Message
		UInv_ItemComponent* ItemComponent = ThisActor->FindComponentByClass<UInv_ItemComponent>();
		if (IsValid(ItemComponent))
		{
			if (IsValid(HUDWidget)) HUDWidget->ShowPickupMessage(ItemComponent->GetPickupMessage());
			
			// Create Short ItemDescription
			CreateShortItemDescription.Broadcast(ItemComponent);
		}
		
	}
	if (LastActor.IsValid())
	{
		// UnHigh light
		UActorComponent* Highlightable = LastActor->FindComponentByInterface(UInv_Highlightable::StaticClass());
		if (IsValid(Highlightable))
		{
			IInv_Highlightable::Execute_UnHighlight(Highlightable);
		}

		UInv_ItemComponent* ItemComponent = LastActor->FindComponentByClass<UInv_ItemComponent>();
		// Destroy Short ItemDescription
		if (ItemComponent)
		{
			DestroyShortItemDescription.Broadcast(ItemComponent);
		}
	}
}
点击查看代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "Inv_PlayerController.generated.h"

/**
 * 
 */
UCLASS()
class INVENTORYSYSTEM_API AInv_PlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	AInv_PlayerController();
	virtual void Tick(float DeltaSeconds) override;

protected:
	virtual void BeginPlay() override;

private:

};
点击查看代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "InventorySystem/Player/Inv_PlayerController.h"

AInv_PlayerController::AInv_PlayerController()
{
	PrimaryActorTick.bCanEverTick = true;
}

void AInv_PlayerController::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);
}

void AInv_PlayerController::BeginPlay()
{
	Super::BeginPlay();
}

Instructions
========================================
** Important: If you have been modifying your own PC for your own project, you will need to remove all the course code from it for this to work! If you are worried about bricking your project because it is currently working for you, don't do this on that project. Do it on a blank project like what we created for the course at the start. I am not liable for you screwing up your exiting project - this is intended to augment Sensei Stephen's course *as is*.

1) Add the Inv_InteractComponent files to the InventoryManagement/Components public and private folders
2) Replace your Inv_PlayerController files in the course with the empty ones I have attached if you want a quick replacement. Otherwise create your own empty PlayerController class.  
3) Compile and launch editor
4) Make a blueprint of the Inv_InteractComponent class and fill in its fields in the Inventory category
5) Attach the component to the player controller blueprint alongside the Inv_InventoryComponent

****** Important change:
6) Modify the close button blueprint to call "Get Component By Class" from the PlayerController, and select Inv_InteractComponent. Then call ToggleInventory from that.


Once these changes are made, the plugin can be added to an project.  Just make sure to add both components to the PC and you should be good.
posted @ 2025-07-28 12:52  pone1  阅读(22)  评论(0)    收藏  举报