第五章:制作系统
一、本章核心目标
本章的核心目标是构建一个功能完整、数据驱动且高度可扩展的物品制作系统。这一目标分为玩家直接感知的显性目标和支撑系统长期发展的隐性战略目标。
-
显性目标 (Player-Facing Goals):
-
UI集成: 在玩家主菜单中新增一个“制作”标签页,与现有的“物品栏”标签页并存。
-
配方展示: 以网格布局(Grid)动态展示所有玩家当前可制作的物品配方。
-
状态反馈: 根据玩家物品栏中的资源存量,直观地通过UI状态(如高亮/置灰)反馈某个配方是否可被制作。
-
制作流程: 玩家点击可制作的配方图标后,系统将播放制作进度条动画,并在完成后从玩家物品栏中扣除所需资源,同时添加新制成的物品。
-
信息提示 (Tooltip): 当鼠标悬停在任一配方上时,系统会显示一个详细的信息提示框,内容包括物品描述、所需资源列表(并以不同颜色标示资源是否足够)以及最终成品的基础信息。
-
-
隐性/战略目标 (Technical & Strategic Goals):
-
数据驱动架构: 建立一个以
Primary Data Asset
为核心的配方管理系统。这使得未来新增或修改配方只需操作数据资产文件,无需修改任何蓝图或代码,极大地提升了内容迭代效率和可维护性。 -
模块化与可扩展性: 将制作系统的各个部分(如配方数据、UI槽位、UI容器、制作逻辑)解耦,设计成独立的模块。特别是通过引入
E_CraftingType
枚举,为未来接入不同类型的制作站(如工作台、熔炉)预留了清晰的扩展路径。 -
客户端-服务器(C/S)权威验证模型: 建立一套严谨的C/S通信流程。客户端负责UI展示与用户输入,但真正的制作逻辑(资源检查、扣除、物品添加)完全在服务器上权威执行,从根本上杜绝了作弊的可能性。
-
开发效率提升: 实现一个“管理员模式”(Admin Mode),允许开发者在游戏内直接将任何物品(无论有无配方)添加到物品栏,极大地简化了新物品的测试流程。
-
未来系统联动: 为未来的“蓝图/印痕系统”(Engram System)预留接口。当前系统只展示所有配方,但其设计已为“只展示已解锁配方”的功能做好了准备。
-
二、系统与功能实现
本章的实现围绕着数据、UI和逻辑三个层面展开,并与前期的物品栏系统紧密耦合。
-
具体系统/功能列表:
-
数据层 (Data Layer):
-
PDA_ItemRecipe
: 核心的配方主数据资产,定义了制作一个物品所需的所有信息(如成品属性、所需资源、获得经验等)。 -
S_ItemRecipe
,S_ItemRecipeInfo
,S_ItemStructure
: 用于在不同模块间传递制作相关数据的结构体,确保了数据格式的统一和规范。 -
E_CraftingType
: 枚举类型,用于区分不同的制作环境(如玩家背包、工作台等),是系统可扩展性的关键。
-
-
UI层 (UI Layer):
-
W_CraftingSlot
: 单个制作槽位的控件蓝图,继承自CommonButtonBase
,负责显示配方图标并处理点击事件。 -
W_CraftingContainer
: 制作界面的核心容器,负责动态加载所有配方数据资产,并据此生成对应的W_CraftingSlot
列表。 -
W_CraftingInfo
&W_CraftItem
: 组合构成了鼠标悬停时的信息提示框(Tooltip),前者为外框,后者为单行资源显示的子控件。 -
W_CraftingProgressBar
: 独立的制作进度条控件,通过动画实现进度反馈。
-
-
逻辑层 (Logic Layer):
-
资源聚合逻辑 (
GetItemQuantities
): 在BPC_ItemsContainer_Master
中实现的核心功能,能够遍历整个物品栏,将分散在不同槽位中的同种资源进行数量汇总,为制作检查提供准确数据。 -
制作可行性检查 (
CheckIfCraftable
): 在UI层(客户端)和逻辑层(服务器)均有实现。客户端检查用于更新UI状态,服务器端检查用于最终的权威验证。 -
制作执行逻辑 (
CraftItem
): 在服务器端执行,负责精确扣除多个物品栏槽位中的资源,并将成品添加到物品栏。 -
管理员模式逻辑 (
AddAllItems
): 通过读取所有PDA_ItemInfo
数据资产(而非配方资产)来动态生成所有物品的添加列表。
-
-
三、关键设计思想
本章的实现体现了现代游戏开发中多个重要的设计思想与原则。
-
设计模式应用:
-
数据驱动设计 (Data-Driven Design): 这是本章最核心的设计模式。系统行为由外部数据(
PDA_ItemRecipe
资产)而非硬编码逻辑决定。W_CraftingContainer
通过AssetRegistry
的GetAssetsByPath
函数动态发现并加载指定目录下的所有配方资产,并为之生成UI,这是该模式的典型应用。 -
接口模式 (Interface Pattern): 大量使用蓝图接口 (
BPI_SurvivalCharacter
,BPI_SurvivalController
) 来实现不同模块间的通信。例如,W_CraftingSlot
通过接口调用角色身上的CraftItem
函数,而无需知道角色的具体实现类。这实现了模块间的“高内聚,低耦合”。 -
异步加载 (Asynchronous Loading): 在加载配方和图标等资源时,普遍使用了
AsyncLoadAsset
节点。这避免了因同步加载大量资源而可能导致的瞬间卡顿,保证了UI的流畅性。
-
-
设计原则体现:
-
单一职责原则 (SRP):
-
PDA_ItemRecipe
只负责存储配方数据。 -
W_CraftingContainer
只负责管理和展示制作槽位。 -
BPC_ItemsContainer_Master
中的CraftItem
函数只负责处理资源扣除逻辑。
-
-
开闭原则 (OCP):
-
对扩展开放: 可以通过添加新的
PDA_ItemRecipe
文件来增加新配方,或通过扩展E_CraftingType
枚举来支持新的制作台。 -
对修改关闭: 上述扩展操作均无需修改现有已稳定的代码或蓝图。
-
-
依赖倒置原则 (DIP): 高层模块(如UI层)不直接依赖底层模块(如角色逻辑),而是依赖于抽象(蓝图接口)。这使得系统的任何一部分都可以被替换或重构,而不会影响到其他部分。
-
四、核心技术点与难点
-
关键技术点:
-
Asset Registry
的动态路径扫描: 利用GetAssetsByPath
节点,实现了在运行时动态发现特定文件夹下的所有数据资产,是数据驱动架构的技术基础。 -
C/S 完整事件流: 构建了一个从客户端UI发起,经由角色、控制器,最终在服务器上执行,再将结果同步回客户端的完整、安全的远程事件调用链。
-
复杂数据结构的设计与传递: 设计了
S_ItemRecipeInfo
等多个结构体,用于在Tooltip等复杂UI中精确传递和展示多源信息(如物品名、图标、当前拥有数、所需数)。 -
UI动画与事件绑定: 使用
UMG
的动画系统制作了进度条,并通过BindToAnimationFinished
事件,在动画播放结束后触发回调,实现了时间驱动的逻辑。
-
-
技术难点与解决方案:
难点 | 解决方案 | 优势/劣势 |
---|---|---|
聚合分散在多格的同类资源 | 在 BPC_ItemsContainer_Master 中创建 GetItemQuantities 函数。该函数遍历所有物品槽,使用一个临时的 TMap 或结构体数组来累计每种物品的总数,最后返回这个聚合后的列表。 |
优势: 将复杂逻辑封装在物品栏组件内部,外部调用者只需关心结果。 劣势: 每次调用都需要完整遍历一次物品栏,存在性能开销。 |
处理跨越多格的资源扣除 | 在 CraftItem 函数中,设计了一个循环逻辑。它首先记录下需要移除的资源和数量,然后遍历所有包含该资源的物品槽,逐个进行扣除,直到扣够所需数量为止。同时,需要处理单个槽位被扣完后清空的情况。 |
优势: 逻辑严谨,能正确处理所有情况。 劣势: 实现较为复杂,需要仔细处理多个边界条件(如数量刚好扣完、一个槽不够扣等)。 |
Tooltip中异步加载导致的数据错乱 | 最初在 W_CraftingInfo 的循环中异步加载资源图标,导致所有子项都显示最后一个加载完成的图标。后重构为将异步加载逻辑移至子控件 W_CraftItem 内部的 EventConstruct 中。 |
优势: 遵循了SRP,每个子控件自我管理其资源的加载,解决了循环中异步回调的时序问题。 |
确保客户端与服务器状态一致性 | 客户端的“可制作”状态仅用于UI展示。实际点击制作时,服务器会重新进行一次完整的资源检查。制作成功后,服务器更新物品栏(该组件属性已设为可复制),Unreal Engine 的属性复制系统会自动将更新同步到客户端。 |
优势: 安全可靠,以服务器为唯一权威。 劣势: 存在微小延迟,但对于制作系统可接受。 |
五、自我批判与重构
-
遇到的“坑”与关键问题:
-
布尔值默认值问题: 在创建
isAdminMode
和isCrafting
两个布尔变量时,其默认值意外地为true
,导致初始逻辑判断错误,在调试后手动修正为false
。 -
整数除法与取模的精度: 在计算
W_CraftingContainer
的网格布局时,行列计算依赖于整数除法和取模,必须确保参与运算的变量(如每行槽位数)不为零,且逻辑正确。 -
UI重置逻辑缺失: 最初,当一个物品槽的资源被扣减至0时,UI上仍会显示一个数量为0的图标。通过在
UpdateUI
函数中增加ResetSlot
逻辑,才彻底清空了该槽位。 -
Tooltip的Admin Mode兼容性: Admin模式下,物品没有配方资产,直接获取会导致空指针。通过在显示Tooltip前增加对Admin Mode的判断,规避了此问题。
-
-
对前期设计的反思与修正:
- 本章的设计强调了UI状态的“即时性”需求。最初的设计可能没有考虑到当玩家关闭并重新打开菜单时,制作状态需要被刷新。后续通过在
W_GameInventory
的OnActivated
事件中调用ResetCraftingWindow
,强制将标签页重置回物品栏,确保了玩家每次打开制作页都能触发一次最新的状态检查,这是一个很好的修正。
- 本章的设计强调了UI状态的“即时性”需求。最初的设计可能没有考虑到当玩家关闭并重新打开菜单时,制作状态需要被刷新。后续通过在
-
如果重来一次,如何优化:
-
简化数据流: 当前从客户端发起制作请求,到最终UI更新的事件传递链条较长(
Slot
->Character
->Controller
->GameHUD
->Container
->Slot
),显得有些“绕圈”。可以考虑引入一个更中心化的UI事件分发器(Event Dispatcher)或者一个轻量级的UI状态管理器,让状态更新的流向更清晰、单向。 -
统一检查逻辑: 客户端为了更新UI而执行的
CheckIfCraftable
和服务器为了安全验证而执行的CheckIfCraftable
存在部分逻辑重复。可以将核心的比较算法提炼成一个纯函数(Pure Function),供两端调用,减少代码冗余。 -
Tooltip参数优化: 向
W_CraftingInfo
(Tooltip)传递了过多的独立参数(图标、名称、资源列表等)。更优的设计是只传递一个核心的RecipeAsset
引用,由Tooltip控件自己负责从这个资产中异步加载和解析所需的所有信息,这更符合“信息隐藏”和“单一职责”的原则。
-