This commit is contained in:
meishibiezb
2026-06-04 21:37:53 +08:00
parent b0d2a0e2e7
commit 29a3f77908
63 changed files with 4068 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
# BP_Bomb
## 基本信息
- **类型**: Blueprint (Actor)
- **父类**: Actor
- **源文件**: /Game/Blueprints/Playground/BP_Bomb.BP_Bomb
- **模块**: Content
## 功能概述
BP_Bomb 是物品系统和追踪器系统的交互核心示例 Actor。它结合了 IItemContainer物品容器接口、UItemTracer物品追踪器和 Owner 检测,演示了物品在 Actor 之间的完整生命周期:创建、追踪、转移和响应。
## 设计用意
BP_Bomb 是最复杂的蓝图示例,演示了物品系统的全部核心功能。它通过 ItemContainer 持有炸弹物品的容器,通过 ItemLocationUItemTracer追踪炸弹物品的位置变化通过 Item IDGUID实现跨容器追踪。追踪器绑定 OnItemMoved 委托当检测到炸弹转移到其他容器时触发相应的业务逻辑如爆炸。GetContainer() 和 FindActorInOuterChain 函数则演示了 C++/蓝图桥接模式——在蓝图层面实现等同于 C++ UItemTracer 的查找逻辑。
## 职责范围
BP_Bomb 负责演示物品+追踪器的完整交互流程,包括物品创建、容器查找、追踪器委托绑定、以及物品转移响应。底层容器存储实现由 UDefaultContainer 处理BP_Bomb 专注于物品生命周期管理的示例代码。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| IItemContainer | 持有 (ItemContainer) | Item 插件 |
| UItemTracer | 持有 (ItemLocation) | Item 插件 |
## 对外接口
- **蓝图函数**
- `GetContainer() -> TScriptInterface<IItemContainer>` -- 返回本 Actor 持有的物品容器接口,用于外部获取容器的物品操作能力
- `FindActorInOuterChain(UObject* Obj) -> AActor*` -- 在给定对象的 Outer 链中查找第一个 AActor蓝图版本的 Outer 遍历(等同于 C++ UItemTracer::FindActorInOuterChain 的蓝图实现)
- **蓝图变量**(均为 is_instance_editable
- `ItemContainer` (TScriptInterface<IItemContainer>) -- 物品容器接口引用,提供 CreateItem、MoveItem、GetItemViews 等所有物品操作
- `Current Owner` (UObject*) -- 当前持有者对象引用,用于追踪物品在哪个 Actor 身上
- `ItemLocation` (UItemTracer*) -- 物品追踪器引用,持有 `OnItemMoved` 委托FOnItemMoved用于监听物品位置变化
- `Item ID` (FGuid) -- 追踪的物品 ID用于跨容器识别和追踪物品
- **EventGraph**:含 5 个事件和 44 个节点,处理物品生命周期演示逻辑
## 使用方法
- 作为示例 Actor 放置到关卡中,演示物品系统的完整使用流程
- 在蓝图编辑器中配置:创建 UDefaultContainer 作为 `ItemContainer`,通过 CreateItem 创建炸弹物品并记录 `Item ID`
- 创建 UItemTracer 实例并赋值给 `ItemLocation`,绑定 `OnItemMoved` 委托监听物品位置变化
- `GetContainer()` 用于外部获取容器的物品操作接口
- `FindActorInOuterChain()` 用于通过物品容器对象的 Outer 链查找实际持有该物品的 Actor
-`ItemLocation.OnItemMoved` 触发时(物品转移到其他容器),通过 `FindActorInOuterChain` 检测新持有者,并触发相应的业务逻辑(如爆炸)
- 实现物品系统的完整交互演示:创建 -> 追踪 -> 转移检测 -> 响应
## 用例
- 物品系统演示 Actor展示 IItemContainer + UItemTracer 的完整交互流程
- 放置于关卡中作为示例,演示物品创建、转移追踪、所有者检测等核心功能

View File

@@ -0,0 +1,52 @@
# BP_CameraPawn
## 基本信息
- **类型**: Blueprint (Pawn)
- **父类**: ACameraPawn
- **源文件**: /Game/Blueprints/BP_CameraPawn.BP_CameraPawn
- **模块**: Content
## 功能概述
BP_CameraPawn 是分离式第三人称摄像机的蓝图实现,继承 C++ 的 ACameraPawn。在蓝图层面配置摄像机参数初始旋转、臂长范围、速度等并提供编辑器内可视化调整能力。
## 设计用意
ACameraPawn 在 C++ 层提供了完整的 SpringArm+Camera 组件和缩放、旋转、移动、重置逻辑。BP_CameraPawn 作为蓝图配置层,将摄像机的默认参数(初始旋转 -60 度、初始臂长 1200、臂长范围 300-3000、旋转速度 2.0、缩放速度 10.0、移动速度 10.0、移动限制 800暴露在蓝图中方便在编辑器内调整和迭代。
## 职责范围
BP_CameraPawn 负责摄像机参数的蓝图级配置和编辑器内可视化调整。底层的摄像机变换逻辑SpringArm 的臂长插值、旋转计算、移动偏移等)全部由 ACameraPawn C++ 层处理。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ACameraPawn | 父类 | C++ 源码 |
## 对外接口
- **继承自 ACameraPawn 的 BlueprintCallable 函数**
- `CameraZoom(const FInputActionValue& Value)` -- 摄像机缩放,调整 SpringArm 臂长
- `CameraRotate(const FInputActionValue& Value)` -- 摄像机旋转,调整 SpringArm 的旋转偏移
- `CameraMove(const FInputActionValue& Value)` -- 摄像机平移,调整 SpringArm 的 SocketOffset
- `CameraReset(const FInputActionValue& Value)` -- 重置摄像机到默认位置和旋转
- **继承自 ACameraPawn 的 BlueprintReadOnly 属性**
- `SpringArmComponent` (USpringArmComponent*) -- SpringArm 组件引用
- `CameraComponent` (UCameraComponent*) -- Camera 组件引用
- `FollowTarget` (APawn*) -- 跟随目标 PawnVisibleInstanceOnly
- **蓝图可调的配置参数**EditDefaultsOnly, BlueprintReadOnly
- `InitialRotation` = FRotator(-60, 0, 0) -- 初始俯仰角
- `InitialArmLength` = 1200.0f -- 初始臂长
- `RotateSpeed` = 2.0f -- 旋转速度
- `ZoomSpeed` = 10.0f -- 缩放速度
- `MoveSpeed` = 10.0f -- 平移速度
- `MaxArmLength` = 3000.0f / `MinArmLength` = 300.0f -- 臂长范围
- `CameraMoveClamp` = 800.0 -- 平移限制范围
## 使用方法
- 通过 BP_TestChar (AMyCharacter) 的 `CameraActorClass` 属性配置,角色 Possess 时自动生成摄像机实例
- 继承自 ACameraPawnC++ 层提供了完整的 SpringArm+Camera 组件和变换逻辑
- 蓝图层面可在 EventGraph 中响应事件event_count=3node_count=3
- 配置参数均为 `EditDefaultsOnly`,在蓝图类默认值中调整摄像机行为,无需修改 C++ 代码
- ACameraPawn 的 Tick() 处理每帧的臂长插值、旋转跟随和移动偏移,蓝图不参与底层变换
- `FollowTarget` 在运行时由 SetFollowTarget 逻辑自动设置为当前控制的角色
## 用例
- BP_TestChar (AMyCharacter) 中 `CameraActorClass` 属性引用,作为角色的分离式摄像机
- 被 Possess 角色时自动生成,跟随 `FollowTarget` Pawn

View File

@@ -0,0 +1,55 @@
# BP_ControllerComp
## 基本信息
- **类型**: Blueprint (ActorComponent)
- **父类**: BP_EndpointComp_C (即 UEndpointComponent)
- **源文件**: /Game/Blueprints/Component/BP_ControllerComp.BP_ControllerComp
- **模块**: Content
## 功能概述
BP_ControllerComp 是自定义控制器端点组件管理摄像机的缩放和跟随模式。持有多个摄像机相关配置参数移动速度MoveSpeed、臂长范围MinArmLength / MaxArmLength、缩放速度ZoomSpeed、跟随模式开关IsFollowMode以及跟随目标角色引用FollowedChar
## 设计用意
BP_ControllerComp 是 BP_TestChar 上最复杂的端点组件,负责处理摄像机控制指令。它持有自己的配置参数而非依赖 ACameraPawn 的默认值表明它可能在运行时动态调整摄像机行为例如切换跟随模式、调整臂长和移动速度。IsFollowMode 和 FollowedChar 的组合支持摄像机锁定特定角色的功能。
## 职责范围
BP_ControllerComp 负责摄像机控制参数的管理、跟随模式的切换以及摄像机移动指令的处理。摄像机实际的 SpringArm 变换和空间计算由 ACameraPawn 及其 SpringArm 组件负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| BP_EndpointComp_C | 父类 | CharacterControl 插件 |
| UEndpointComponent | 祖父类 | CharacterControl 插件 |
| ACharacter | 引用 (FollowedChar) | 引擎内置 |
## 对外接口
- **蓝图函数**
- `MoveController(A: GameplayTag, InVec: Vector)` -- 摄像机移动指令处理,接收 GameplayTag 和移动向量
- `RotateController(A: GameplayTag, InVec: Vector)` -- 摄像机旋转指令处理
- `ZoomCamera(A: GameplayTag, InVec: Vector)` -- 摄像机缩放指令处理,通过 InVec 中的值调整臂长
- `ResetControllerLocation(DiscreteMeta: DiscreteMeta)` -- 摄像机位置重置,接收离散命令元数据
- `TickFollowChar()` -- 跟随模式每帧更新,将摄像机位置锁定到 FollowedChar
- **蓝图变量**(均为 is_instance_editable
- `MoveSpeed` (double) -- 摄像机移动速度
- `MinArmLength` (double) -- 最小臂长
- `MaxArmLength` (double) -- 最大臂长
- `ZoomSpeed` (double) -- 缩放速度
- `IsFollowMode` (bool) -- 是否启用跟随模式
- `FollowedChar` (Character*) -- 跟随目标角色引用
- **继承自 (BP_EndpointComp_C / UEndpointComponent) 的接口**
- `EndpointState` -- 端点状态,含 `InterestedTags` 配置摄像机控制相关标签
- `OnCommandReceived` -- 收到指令时的处理入口
- **EventGraph**:含 6 个事件和 27 个节点,处理摄像机控制的核心逻辑
## 使用方法
- 作为 ActorComponent 添加到 BP_TestChar
- 在组件实例上配置 `MoveSpeed``MinArmLength``MaxArmLength``ZoomSpeed` 等摄像机参数
- 通过 `EndpointState.InterestedTags` 配置关注的摄像机控制 GameplayTag如 "Camera.Move"、"Camera.Zoom"、"Camera.Rotate"、"Camera.Reset"
- 开启 `IsFollowMode` 并设置 `FollowedChar` 可启用角色跟随模式
- `MoveController``RotateController``ZoomCamera` 函数由 OnCommandReceived 事件根据指令标签分发调用
- `TickFollowChar()` 在 EventGraph 的 Tick 事件中调用,实现每帧跟随
- `UCommandRouterComponent` 自动发现并注册本组件
## 用例
- BP_TestChar 的组件,处理摄像机控制指令
- 通过配置参数在运行时动态调整 ACameraPawn 的行为(臂长、速度、跟随目标)

View File

@@ -0,0 +1,45 @@
# BP_DefaultContainer
## 基本信息
- **类型**: Blueprint (UObject)
- **父类**: UDefaultContainer
- **源文件**: /Game/Blueprints/Playground/BP_DefaultContainer.BP_DefaultContainer
- **模块**: Content
## 功能概述
BP_DefaultContainer 是 UDefaultContainer 的蓝图子类,作为物品系统的默认容器在蓝图层中使用。无额外自定义变量,直接使用 C++ 父类的完整 IItemContainer 实现,提供 CreateItem、GetItemViews、MoveItem 等物品操作接口。
## 设计用意
UDefaultContainer 是 C++ 中 IItemContainer 的默认实现但不方便在编辑器中直接引用。BP_DefaultContainer 提供了一个蓝图可引用、可放置的资产版本,使得其他蓝图(如 BP_InventoryComp可以通过编辑器直接引用它作为物品存储容器简化了工作流。
## 职责范围
BP_DefaultContainer 作为物品容器的蓝图实例化版本负责在编辑器中作为可引用的资产存在。底层的物品存储机制、IItemContainer 接口实现全部由 C++ 父类 UDefaultContainer 处理。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| UDefaultContainer | 父类 | Item 插件 |
| IItemContainer | 间接实现 | Item 插件 |
## 对外接口
- **继承自 UDefaultContainer即 IItemContainer 接口)的全部 BlueprintCallable 函数**
- `GetItemViews() const -> TArray<FItemView>` -- 返回容器中所有物品视图列表
- `GetItemViewByID(const FGuid& ItemID) const -> FItemView` -- 按物品 ID 获取单个物品视图
- `GetItemCount() const -> int32` -- 返回当前容器中的物品数量
- `MoveItem(const FGuid& ItemID, const TScriptInterface<IItemContainer>& TargetContainer) -> bool` -- 将物品移动到目标容器BlueprintAuthorityOnly
- `CreateItem(FName ItemType, TArray<FGuid>& NewItemIDs, int32 Count=1) -> bool` -- 按 ItemType 创建物品BlueprintAuthorityOnly
- `GetItemProperty(FGuid ItemID, FName PropertyName, int32& Value) -> bool` -- 读取物品动态属性CustomThunk属性类型自动匹配
- `SetItemProperty(FGuid ItemID, FName PropertyName, int32 Value)` -- 写入物品动态属性CustomThunk
- **无自定义蓝图变量或函数**MCP: variables=[], functions=[]):纯父类能力透传,所有接口由 UDefaultContainer C++ 层实现
## 使用方法
- 作为 UObject 资产创建并保存在 /Game/Blueprints/Playground/ 路径下
- 被 BP_InventoryComp 的 `DefaultContainer` 变量引用,作为物品的实际存储容器
- 通过 IItemContainer 接口的所有 BlueprintCallable 函数进行物品操作
- 实际存储逻辑由 C++ 父类 UDefaultContainer 的 `TArray<TUniquePtr<FItemInstance>> Items` 处理
- BP_DefaultContainer 存在的意义是提供一个蓝图可引用的资产版本,方便在编辑器中直接拖拽配置
- 无需在蓝图层面编写任何逻辑functions=[]),完全依赖父类实现
## 用例
- BP_InventoryComp 的 `DefaultContainer` 变量默认值,作为库存组件的底层存储
- 任何需要 IItemContainer 接口的地方均可引用此资产

View File

@@ -0,0 +1,49 @@
# BP_DropItemInvComp
## 基本信息
- **类型**: Blueprint (ActorComponent)
- **父类**: BP_InventoryComp
- **源文件**: /Game/Blueprints/Playground/BP_DropItemInvComp.BP_DropItemInvComp
- **模块**: Content
## 功能概述
BP_DropItemInvComp 是掉落物品库存组件,继承 BP_InventoryComp 并增加了一个 FName 类型的 `Item` 变量,用于指定该掉落物包含什么物品类型。适用于可拾取的掉落物 Actor。
## 设计用意
BP_DropItemInvComp 将 BP_InventoryComp 的通用库存功能特化为"掉落物"用途。通过 Item 变量指定掉落物的物品类型,在 BeginPlay 时自动调用继承的 CreateItemInternal 创建物品,结合继承的 IInventory 接口实现支持玩家通过 ReceiveItem 拾取。这种设计使掉落物 Actor 只需添加一个组件并设置一个变量即可完整支持物品掉落和拾取流程。
## 职责范围
BP_DropItemInvComp 负责掉落物品的自动创建和作为可拾取物品容器运行。掉落物的物理表现由所在的 Actor 负责,复杂的物品行为由物品类型配置决定。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| BP_InventoryComp | 父类 | /Game/Blueprints/Playground/BP_InventoryComp |
## 对外接口
- **蓝图变量**is_instance_editable
- `Item` (FName) -- 掉落物品的类型名称,对应数据表中物品行的 RowName。在组件实例上设置指定该掉落物包含什么物品
- **继承自 BP_InventoryComp 的全部接口**
- `DefaultContainer` -- 底层物品容器引用
- `ViewCache` (TArray<FItemView>) -- 物品视图缓存
- `OnViewChanged` -- 视图变化委托
- `UpdateViewCache()` -- 刷新视图缓存
- `CreateItemInternal(FName ItemType, int32 Count) -> bool` -- 创建物品
- **继承自 BP_InventoryComp 的 IInventory 接口实现**
- `GetItemViews()``GetItemViewByID()``GetItemCount()` -- 查询接口
- `RequestMoveItem(ID, TargetInventory)` -- 请求移出物品(掉落物被拾取时调用)
- `ReceiveItem(ID, SourceContainer)` -- 从其他容器接收物品(拾取验证场景使用)
- **EventGraph**:含 2 个事件和 7 个节点,在 BeginPlay 时根据 `Item` 变量自动调用 CreateItemInternal
## 使用方法
- 作为 ActorComponent 添加到掉落物 Actor 上
- 在组件实例上设置 `Item` 变量为数据表中物品的 RowNameFName 类型)
- 在 BeginPlay 事件中自动调用 `CreateItemInternal(Item, 1)` 创建物品,实现掉落物生成
- 可以通过设置 `DefaultContainer` 覆盖默认的 BP_DefaultContainer
- 玩家拾取时,通过 IInventory::RequestMoveItem 将物品从掉落物容器转移至玩家库存
- 掉落物的物理外观由所在的 Actor 负责,本组件仅处理物品数据的创建和转移
- 继承自 BP_InventoryComp所有库存管理功能均可用
## 用例
- 掉落物 Actor 的库存组件,在运行时自动生成配置的物品
- 其父类 BP_InventoryComp 同时被 WBP_InventoryView 引用以显示物品列表

View File

@@ -0,0 +1,47 @@
# BP_Hud
## 基本信息
- **类型**: Blueprint (HUD)
- **父类**: HUD
- **源文件**: /Game/Blueprints/BP_Hud.BP_Hud
- **模块**: Content
## 功能概述
BP_Hud 是项目的 HUD 蓝图,管理三个 UI 窗口的引用物品栏窗口InvView类型 WBP_InventoryView、库存窗口InventoryWindow和提示窗口HintWindow。作为 UI 系统的"根"容器,持有并显示各种 Widget。
## 设计用意
BP_Hud 将 UI 管理集中在 HUD 中,而不是分散在各个 Widget 或 Controller 中。它将物品视图、库存窗口和提示窗口都集中持有在一个地方,作为 UI 系统的入口点,方便其他系统通过 HUD 访问和操作 UI。
## 职责范围
BP_Hud 负责持有主要 UI 窗口引用并作为 UI 系统的入口点。具体的 UI 逻辑(物品列表渲染、窗口交互、提示显示)由各 Widget 自身负责BP_Hud 仅提供引用的集中管理。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| WBP_InventoryView | 引用 (InvView) | /Game/Blueprints/UI/Item/WBP_InventoryView |
| WBP_Window | 引用 (InventoryWindow, HintWindow) | /Game/Blueprints/UI/WBP_Window |
## 对外接口
- **蓝图可编辑变量**(均为 Instance Editable
- `InvView` (WBP_InventoryView_C*) -- 物品栏视图 Widget 引用,类型为 WBP_InventoryView
- `InventoryWindow` (WBP_Window_C*) -- 库存窗口 Widget 引用,类型为 WBP_Window
- `HintWindow` (WBP_Window_C*) -- 提示窗口 Widget 引用,类型为 WBP_Window
- **继承自 AHUD 的标准接口**
- `DrawHUD()` -- HUD 绘制入口
- `ReceiveDrawHUD(SizeX, SizeY)` -- 蓝图可覆盖的 UI 绘制事件
- `GetOwningPlayerController()` -- 获取所属的 PlayerController
- HUD 生命周期事件BeginPlay、EndPlay 等)
- **EventGraph**:含 4 个事件和 39 个节点,负责 Widget 的创建和初始化逻辑
## 使用方法
- 在 BP_TestMode 中设置为 `HUDClass`,每次 PlayerController 初始化时自动创建
- 在蓝图 EventGraph 中创建和初始化三个 Widget`InvView`(物品栏)、`InventoryWindow`(库存窗口)、`HintWindow`(提示窗口)
- 三个 Widget 变量为 Instance Editable可在编辑器中替换为不同的 Widget 子类
- 其他系统通过 `PlayerController->GetHUD()` 获取 BP_Hud 实例,再通过变量访问各 Widget
- 作为 UI 系统的"根"容器,所有主要 UI 窗口的引用集中在此处管理
## 用例
- BP_TestMode 的 `HUDClass`,玩家进入关卡时生成
- `InvView` 变量引用 WBP_InventoryView物品系统通过此引用更新物品列表 UI
- `InventoryWindow` 变量引用 WBP_Window库存窗口的显示和隐藏通过此引用控制
- `HintWindow` 变量引用 WBP_Window提示信息的显示通过此引用控制

View File

@@ -0,0 +1,54 @@
# BP_InventoryComp
## 基本信息
- **类型**: Blueprint (ActorComponent)
- **父类**: ActorComponent
- **源文件**: /Game/Blueprints/Playground/BP_InventoryComp.BP_InventoryComp
- **模块**: Content
## 功能概述
BP_InventoryComp 是库存管理组件,包装了一个 UDefaultContainer 实例,持有物品视图缓存和视图变化委托,提供物品创建和视图更新功能。它是物品系统与 Actor 之间的桥梁组件,也是 IInventory 接口的蓝图实现者。
## 设计用意
BP_InventoryComp 将库存功能封装为可复用的 ActorComponent使任何 Actor 都可以通过添加此组件来获得库存能力。它通过 DefaultContainer 引用底层存储,通过 ViewCache 缓存物品视图避免重复生成,通过 OnViewChanged 委托通知 UI 更新。这种设计将库存管理从具体的 Actor 类型中解耦出来,提高了可复用性。
## 职责范围
BP_InventoryComp 负责持有并管理 UDefaultContainer 实例、维护 FItemView 缓存、处理物品创建和视图更新、以及实现 IInventory 接口的蓝图逻辑。底层物品存储机制由 UDefaultContainer 处理,物品类型定义由 DataTable 负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| UDefaultContainer | 持有 (DefaultContainer) | Item 插件 |
| BP_DefaultContainer | 默认实例 | /Game/Blueprints/Playground/BP_DefaultContainer |
| FItemView | 缓存 (ViewCache) | Item 插件 |
| IInventory | 实现 | Item 插件 |
## 对外接口
- **蓝图函数**
- `UpdateViewCache()` -- 刷新 ViewCache从 DefaultContainer 重新获取物品视图列表,同步缓存与底层容器状态
- `CreateItemInternal(FName ItemType, int32 Count) -> bool` -- 内部物品创建函数,通过 DefaultContainer.CreateItem 创建物品后更新 ViewCache
- **蓝图变量**(均为 is_instance_editable
- `DefaultContainer` (DefaultContainer*/UDefaultContainer*) -- 底层物品容器引用,默认指向 BP_DefaultContainer
- `ViewCache` (TArray<FItemView>) -- 物品视图缓存数组UI 从此读取显示数据,结构与 FItemView 一致ItemID, ItemType, ItemName, ItemDescription, Icon, ItemDataText
- `OnViewChanged` (动态多播委托) -- 视图变化通知委托,每次 ViewCache 更新时广播,供 UI 绑定自动刷新
- **IInventory 接口实现**BlueprintNativeEvent
- `GetItemViews() -> TArray<FItemView>` -- 返回所有物品视图
- `GetItemViewByID(const FGuid& ItemID) -> FItemView` -- 按 ID 获取视图
- `GetItemCount() -> int32` -- 物品数量
- `RequestMoveItem(const FGuid& ItemID, const TScriptInterface<IInventory>& TargetInventory)` -- 请求移动物品到目标库存
- `ReceiveItem(const FGuid& ItemID, const TScriptInterface<IItemContainer>& SourceContainer)` (BlueprintAuthorityOnly) -- 从源容器接收物品
- **EventGraph**:含 7 个事件和 30 个节点,处理库存初始化和生命周期
## 使用方法
- 作为 ActorComponent 添加到需要库存功能的 Actor 上
- 设置 `DefaultContainer` 变量指向 BP_DefaultContainer 资产(或其他 IItemContainer 实现)
- UI (如 WBP_InventoryView) 绑定 `OnViewChanged` 委托,在库存变化时自动刷新
- 调用 `CreateItemInternal(ItemType, Count)` 创建新物品,之后 ViewCache 自动更新
- 通过 IInventory 接口与其他库存组件交互:`RequestMoveItem` 将物品移出,`ReceiveItem` 从其他容器接收物品
- `UpdateViewCache` 在物品变化后同步缓存与底层容器的状态
- 继承类 BP_DropItemInvComp 通过覆盖 BeginPlay 自动调用 CreateItemInternal
## 用例
- BP_DropItemInvComp 的父类,提供库存管理的通用基础
- WBP_InventoryView 通过 `Comp` 变量引用此组件,读取 ViewCache 渲染物品列表
- 添加到任何 Actor 上使其具备库存能力

View File

@@ -0,0 +1,45 @@
# BP_MoveInput
## 基本信息
- **类型**: Blueprint (ActorComponent)
- **父类**: UEndpointComponent
- **源文件**: /Game/Blueprints/Component/BP_MoveInput.BP_MoveInput
- **模块**: Content
## 功能概述
BP_MoveInput 是移动输入端点组件,持有对 CharacterMovementComponent 的引用(通过 CharMove 变量),将指令系统中的移动指令转化为实际的角色移动。
## 设计用意
BP_MoveInput 是指令系统和 UE 移动系统之间的桥梁。它持有 `CharMove` (CharacterMovementComponent*) 引用,在 OnCommandReceived 中接收移动指令(连续指令,如 "Command.Move"),从 FCommandPacket.ContinuousPayload.ContinuousValue 提取移动向量,然后调用 CharacterMovementComponent 的移动接口。这种设计将输入系统和移动系统解耦——指令的生成、路由和执行分别由不同的组件负责。
## 职责范围
BP_MoveInput 负责接收移动指令并将其转换为 CharacterMovementComponent 的移动调用。移动向量通过连续指令的 ContinuousValue 传入,由 BP_MoveInput 提取并应用。指令的生成由 UCommandInputComponent 负责,指令的路由匹配由 UCommandRouter 负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| UEndpointComponent | 父类 | CharacterControl 插件 |
| UCharacterMovementComponent | 引用 (CharMove) | 引擎内置 |
## 对外接口
- **蓝图变量**
- `CharMove` (CharacterMovementComponent*) -- 持有对所属角色的 CharacterMovementComponent 引用is_instance_editable可在组件实例上配置
- **继承自 UEndpointComponent 的接口**
- `EndpointState` (FEndpointState) -- 端点状态,含 `InterestedTags` 配置关注的移动指令标签(如 "Command.Move"
- `OnCommandReceived(const FCommandPacket& Command)` (BlueprintImplementableEvent) -- 收到移动指令时调用,在 EventGraph 中实现node_count=22
- `OnEndpointStateChanged` / `OnCommandOutput` -- 状态和输出委托
- **通过 EventGraph 的行为**
-`Command.ContinuousPayload.ContinuousValue` 提取移动向量 (FVector3f)
- 调用 `CharMove` 的移动接口将向量转化为实际角色移动
## 使用方法
- 作为 ActorComponent 添加到 BP_TestChar
- 设置 `CharMove` 变量指向所属角色的 `CharacterMovementComponent`
- 配置 `EndpointState.InterestedTags` 添加移动相关 GameplayTag如 "Command.Move"
- 设置 `EndpointState.bIsContinuousFriendly = true`(移动是连续指令)
- `UCommandRouterComponent` 自动发现并注册本组件为端点
- 当 EnhancedInput 产生移动输入 -> UCommandInputComponent 生成 FCommandPacket -> UCommandRouterComponent 按标签匹配 -> 本组件的 OnCommandReceived 收到指令 -> 从 ContinuousPayload.ContinuousValue 提取 FVector3f -> 调用 CharacterMovementComponent 执行移动
## 用例
- BP_TestChar 的组件,处理移动指令
- 通过 `CharMove` 变量连接 CharacterMovementComponent实现指令到移动的桥接

View File

@@ -0,0 +1,43 @@
# BP_SayHello
## 基本信息
- **类型**: Blueprint (ActorComponent)
- **父类**: UEndpointComponent
- **源文件**: /Game/Blueprints/Component/BP_SayHello.BP_SayHello
- **模块**: Content
## 功能概述
BP_SayHello 是一个简单的测试/调试端点组件,用于验证 CharacterControl 指令路由系统是否正常工作。它没有任何自定义变量,仅覆盖 OnCommandReceived 事件输出 Hello 日志或消息。
## 设计用意
作为 CharacterControl 插件的最小化端点实现示例BP_SayHello 验证了从 EnhancedInput 到端点响应的全链路EnhancedInput 产生输入动作UCommandInputComponent 生成 FCommandPacketUCommandRouter 进行标签匹配路由,最终到达 BP_SayHello 的 OnCommandReceived 并执行响应。它同时也可作为新端点的参考实现模板。
## 职责范围
BP_SayHello 负责验证指令路由系统的端到端工作流程,并作为新端点的参考实现。它的处理逻辑极简(输出日志),不涉及任何业务逻辑。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| UEndpointComponent | 父类 | CharacterControl 插件 |
## 对外接口
- **继承自 UEndpointComponent 的接口**
- `EndpointState` (FEndpointState) -- 端点状态,含 `InterestedTags` 配置关注的指令标签
- `OnCommandReceived(const FCommandPacket& Command)` (BlueprintImplementableEvent) -- 收到匹配指令时调用,在 EventGraph 中实现4 个事件、27 个节点)
- `OnEndpointStateChanged` -- 状态变化委托
- `OnCommandOutput` -- 命令输出委托
- **无自定义蓝图变量**:纯行为型组件,不暴露状态数据
## 使用方法
- 作为 ActorComponent 添加到 BP_TestChar用作测试端点
- 配置 `EndpointState.InterestedTags` 添加测试用 GameplayTag如 "Command.SayHello"
- `OnCommandReceived` 事件在蓝图 EventGraph 中实现:收到指令后输出调试日志或 Hello 消息
- 配合 EnhancedInput 的测试输入动作使用:
1. EnhancedInput 产生输入 -> UCommandInputComponent 生成 FCommandPacket
2. UCommandRouterComponent 按标签匹配路由到本组件
3. OnCommandReceived 触发,输出日志
- 可作为新端点的实现模板:仅需在蓝图 EventGraph 中覆盖 `OnCommandReceived` 实现自定义行为
## 用例
- BP_TestChar 的组件,验证 CharacterControl 指令路由系统的端到端工作流程
- 当 EnhancedInput 绑定到 "Command.SayHello" 指令时,输出响应消息

View File

@@ -0,0 +1,57 @@
# BP_TestChar
## 基本信息
- **类型**: Blueprint (Actor)
- **父类**: AMyCharacter
- **源文件**: /Game/Blueprints/BP_TestChar.BP_TestChar
- **模块**: Content
## 功能概述
BP_TestChar 是玩家主角的蓝图实现,继承 C++ 的 AMyCharacter。它通过添加 CharacterControl 插件的路由组件来扩展角色能力,在蓝图层面组合移动、指令路由和摄像机控制。
## 设计用意
AMyCharacter 在 C++ 层提供了移动和摄像机 Pawn 创建的基础能力。BP_TestChar 在蓝图层面通过组件化的方式组装角色功能:添加 UCommandRouterComponent 将角色变成指令路由中心,添加各种 ICommandEndpoint 组件处理不同类型的指令。作为项目的核心 Pawn它是指令系统和角色能力的"组装层"——新功能通过添加新的端点组件来扩展,而非修改 C++ 代码。
## 职责范围
BP_TestChar 是角色的蓝图配置层,负责添加并配置 CharacterControl 路由组件和端点组件实现角色重叠、Tick 等蓝图级逻辑。移动逻辑由 AMyCharacter C++ 层处理,摄像机逻辑由 ACameraPawn 负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| AMyCharacter | 父类 | /Game/Blueprints/BP_TestChar (C++ 继承) |
| ACameraPawn | 间接CameraActorClass | /Game/Blueprints/BP_CameraPawn |
| UCommandRouterComponent | 组件 | CharacterControl 插件 |
| BP_UniversalEndpointComp | 组件 | /Game/Blueprints/Component/BP_UniversalEndpointComp |
| BP_MoveInput | 组件 | /Game/Blueprints/Component/BP_MoveInput |
| BP_ControllerComp | 组件 | /Game/Blueprints/Component/BP_ControllerComp |
| BP_SayHello | 组件 | /Game/Blueprints/Component/BP_SayHello |
## 对外接口
- **继承自 AMyCharacter 的 BlueprintCallable 函数**
- `Move(const FInputActionValue& Value)` -- 移动输入处理,供 EnhancedInput 绑定调用
- `BeginRun(const FInputActionValue& Value)` / `StopRun(const FInputActionValue& Value)` -- 奔跑切换
- `BeginCrouch(const FInputActionValue& Value)` / `StopCrouch(const FInputActionValue& Value)` -- 蹲伏切换
- `IsRunning() const` / `IsCrouching() const` -- 状态查询
- **继承自 AMyCharacter 的 BlueprintReadOnly 属性**
- `DefaultMapping` (UInputMappingContext*) -- 默认输入映射上下文
- `MoveAction`, `RunAction`, `CrouchAction`, `CameraMoveAction`, `CameraZoomAction`, `CameraResetAction`, `CameraRotateAction` -- 各输入动作引用
- `CameraActorClass` (TSubclassOf<ACameraPawn>) -- 摄像机 Pawn 类,设置为 BP_CameraPawn
- **身上的组件作为对外能力**
- `UCommandRouterComponent` -- 指令路由中心,通过 RegisterEndpoint / InputCommand 接收和分发指令
- `BP_UniversalEndpointComp` -- 通用端点,对外暴露 `CommandReceivedDispatcher` 动态多播委托
- `BP_MoveInput` -- 移动端点,对外暴露 `CharMove` (CharacterMovementComponent*) 变量
- `BP_ControllerComp` -- 摄像机控制端点,对外暴露 `MoveController``RotateController``ZoomCamera``ResetControllerLocation``TickFollowChar` 函数及摄像机配置参数
- `BP_SayHello` -- 测试端点,验证指令路由系统
## 使用方法
- 在 BP_TestMode 中设置为 `DefaultPawnClass`,作为项目的默认 Pawn
- 在蓝图编辑器中添加 CharacterControl 路由和端点组件:`UCommandRouterComponent` 作为指令路由器,`BP_UniversalEndpointComp``BP_MoveInput``BP_ControllerComp``BP_SayHello` 作为各类指令端点
- 通过父类 AMyCharacter 的 `CameraActorClass` 属性指定 BP_CameraPawn 作为分离式摄像机
- 配置父类的 InputAction 属性MoveAction、RunAction 等)连接 EnhancedInput 系统
- 各端点组件通过 `EndpointState.InterestedTags` 配置关注的 GameplayTagUCommandRouterComponent 的 `AutoRegisterEndpoints` 在 BeginPlay 时自动注册所有组件上的端点
- 指令链路EnhancedInput -> UCommandInputComponent -> UCommandRouterComponent -> 匹配的端点组件
## 用例
- BP_TestMode 的 `DefaultPawnClass`,作为玩家进入关卡时的默认角色
- 身上挂载的 `BP_UniversalEndpointComp``BP_MoveInput``BP_ControllerComp``BP_SayHello` 作为该角色的指令处理端点
- `CameraActorClass` 属性引用 BP_CameraPawn角色被 Possess 时自动生成摄像机

View File

@@ -0,0 +1,43 @@
# BP_TestCtl
## 基本信息
- **类型**: Blueprint (PlayerController)
- **父类**: AMyPlayerController
- **源文件**: /Game/Blueprints/BP_TestCtl.BP_TestCtl
- **模块**: Content
## 功能概述
BP_TestCtl 是项目的玩家控制器蓝图实现,继承 C++ 的 AMyPlayerController。它实现了 OnClientPossessFinished 事件用于 Possess 完成后的初始化,并提供 SetupControllingPawn 自定义函数用于控制权切换后的 Pawn 设置。
## 设计用意
BP_TestCtl 利用 AMyPlayerController 的禁用摄像机计算特性(支持 ACameraPawn 分离式摄像机),在蓝图层面提供 Possess 完成后的客户端初始化和 Pawn 设置逻辑。作为一个薄蓝图层,它将 C++ 层的控制器能力暴露给蓝图系统进行配置和扩展。
## 职责范围
BP_TestCtl 负责 Possess 完成时的客户端初始化和控制权切换后的 Pawn 设置。摄像机计算由 C++ 层禁用CalcCamera 空操作),角色移动由 AMyCharacter 负责BP_TestCtl 不涉及这些底层逻辑。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| AMyPlayerController | 父类 | C++ 源码 |
## 对外接口
- **继承自 AMyPlayerController 的 BlueprintCallable 函数**
- `GetOwnedPawn()` -- 获取当前控制的 Pawn返回 APawn*,供蓝图查询当前 Possess 目标
- **继承自 AMyPlayerController 的 BlueprintImplementableEvent 事件**
- `OnClientPossessFinished(APawn* P)` -- 客户端 Possess 完成时调用,在 BP_TestCtl 的 EventGraph 中实现,客户端收到 Possess 确认后的初始化入口EventGraph 中 event_count=3
- **蓝图自定义函数**
- `SetupControllingPawn()` -- 控制权切换后的 Pawn 设置函数,无参数,在 EventGraph 的 18 个节点中被调用
- **继承自 APlayerController 的标准接口**:所有 APlayerController 的标准事件和函数均可用(如 OnPossess、OnUnPossess、GetPlayerViewPoint 等)
## 使用方法
- 在 BP_TestMode 中设置为 `PlayerControllerClass`,作为项目的默认玩家控制器
- 父类 AMyPlayerController 重写了 `CalcCamera`(空操作),禁用默认摄像机计算,配合 ACameraPawn 的分离式摄像机设计
- 父类 AMyPlayerController 重写了 `AcknowledgePossession`,在 Possess 确认后触发 `OnClientPossessFinished` 事件
- `OnClientPossessFinished` 在蓝图 EventGraph 中实现,用于客户端在 Possess 完成后的初始化逻辑
- `SetupControllingPawn` 在控制权切换时调用,设置新的受控 Pawn 所需的配置(如 UI 绑定、输入上下文切换等)
- 作为薄蓝图层,主要职责是将 C++ 层的控制器能力暴露给蓝图系统
## 用例
- BP_TestMode 的 `PlayerControllerClass`,每次玩家加入时实例化
- `OnClientPossessFinished` 事件连接客户端 Possess 完成的初始化流程
- `SetupControllingPawn` 在角色切换时重新配置 Pawn 资源

View File

@@ -0,0 +1,47 @@
# BP_TestMode
## 基本信息
- **类型**: Blueprint (GameMode)
- **父类**: GameModeBase
- **源文件**: /Game/Blueprints/BP_TestMode.BP_TestMode
- **模块**: Content
## 功能概述
BP_TestMode 是项目的 GameMode 蓝图,配置默认的 Pawn、PlayerController 和 HUD 类,将项目的所有核心类串联起来。
## 设计用意
作为项目唯一的 GameModeBP_TestMode 将分散的蓝图类通过引擎框架连接在一起。它将 BP_TestChar 设为默认 Pawn、BP_TestCtl 设为玩家控制器、BP_Hud 设为 HUD 类,形成一个完整的游戏运行时配置。这样所有核心类型的替换只需要修改这一个地方。
## 职责范围
BP_TestMode 负责配置默认游戏类和串联项目核心类型之间的关系。具体的角色行为、控制器逻辑、UI 管理分别由各自对应的类负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| BP_TestChar | DefaultPawn | /Game/Blueprints/BP_TestChar |
| BP_TestCtl | PlayerController | /Game/Blueprints/BP_TestCtl |
| BP_Hud | HUD | /Game/Blueprints/BP_Hud |
## 对外接口
- **继承自 AGameModeBase 的标准接口**
- GameMode 生命周期事件:`InitGame``PreLogin``PostLogin``Logout``RestartPlayer``SpawnDefaultPawnAtTransform` 等,均可在蓝图 EventGraph 中覆盖EventGraph 中 event_count=2, node_count=2
- `GetDefaultPawnClassForController` -- 返回配置的 DefaultPawnClass
- GameMode 的标准函数和事件均继承可用
- **编辑器配置属性**(通过 GameMode 基类继承):
- `DefaultPawnClass` = BP_TestChar -- 默认 Pawn 类型
- `PlayerControllerClass` = BP_TestCtl -- 玩家控制器类型
- `HUDClass` = BP_Hud -- HUD 类型
- 其他 GameMode 标准配置属性GameStateClass、SpectatorClass 等)
## 使用方法
- 在关卡的世界设置World Settings中将 `GameMode Override` 设置为 BP_TestMode
- 或在项目设置中将其设为默认 GameMode应用于所有关卡
- 配置三个核心 class 属性,将 BP_TestChar、BP_TestCtl、BP_Hud 串联为一个完整游戏框架
- EventGraph 中无额外蓝图逻辑node_count=2纯配置型蓝图所有替换只需修改此文件中的属性值
- 项目有且仅有一个 GameMode所有核心类型的替换集中在此处管理
## 用例
- 关卡 World Settings 的 `GameMode Override`,引擎启动时加载
- `DefaultPawnClass=BP_TestChar` -- 新玩家进入时自动生成该 Pawn
- `PlayerControllerClass=BP_TestCtl` -- 新玩家连接时自动创建该控制器
- `HUDClass=BP_Hud` -- 创建控制器时自动生成该 HUD

View File

@@ -0,0 +1,44 @@
# BP_UniversalEndpointComp
## 基本信息
- **类型**: Blueprint (ActorComponent)
- **父类**: UEndpointComponent
- **源文件**: /Game/Blueprints/Component/BP_UniversalEndpointComp.BP_UniversalEndpointComp
- **模块**: Content
## 功能概述
BP_UniversalEndpointComp 是通用指令端点组件,继承 CharacterControl 的 UEndpointComponent。它添加了 `CommandReceivedDispatcher` 事件分发器委托,让其他蓝图可以动态绑定到该端点的指令接收事件,无需创建新子类。
## 设计用意
UEndpointComponent 提供了 OnCommandReceived (BlueprintImplementableEvent) 用于蓝图处理指令但该事件只能在子类中覆盖外部蓝图无法直接订阅。BP_UniversalEndpointComp 通过添加一个动态多播委托 `CommandReceivedDispatcher`,在收到指令时将事件转发为委托广播,使外部蓝图也能监听该端点的指令接收事件,提升了端点组件的可组合性。
## 职责范围
BP_UniversalEndpointComp 负责提供外部的指令接收委托,将内部事件转发为可外部订阅的委托。它不处理具体的指令逻辑(由外部订阅者负责),也不涉及路由器逻辑(由 UCommandRouter 负责)。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| UEndpointComponent | 父类 | CharacterControl 插件 |
## 对外接口
- **蓝图变量**
- `CommandReceivedDispatcher` (动态多播委托) -- 指令接收事件分发器is_instance_editable其他蓝图可绑定此委托接收指令通知
- **继承自 UEndpointComponent 的接口**
- `EndpointState` (FEndpointState) -- 端点状态,含 `EndpointGuid``InterestedTags` (FGameplayTagContainer)、`bIsActive``bIsContinuousFriendly``bIsAsynchronous`
- `OnCommandReceived(const FCommandPacket& Command)` (BlueprintImplementableEvent) -- 收到匹配指令时调用,可在蓝图子类或本蓝图 EventGraph 中实现
- `OnEndpointStateChanged` (FOnEndpointStateChanged) -- 状态变化委托
- `OnCommandOutput` (FOnCommandOutput) -- 命令输出委托
- **继承自 ICommandEndpoint 接口**
- `ReceiveCommand_BP(const FCommandPacket& Command)` (BlueprintNativeEvent) -- 蓝图版本的指令接收入口
- `GetEndpointDispatcher_BP()` (BlueprintNativeEvent) -- 获取端点分发器
## 使用方法
- 作为 ActorComponent 添加到 BP_TestChar 等需要通用指令处理的 Actor 上
- 配置 `EndpointState.InterestedTags` 指定该端点关注的 GameplayTag 集合
- 外部蓝图通过绑定 `CommandReceivedDispatcher` 委托来监听指令,无需创建新的端点子类
- EventGraph 中当 `OnCommandReceived` 触发时,自动广播 `CommandReceivedDispatcher`,将内部事件转发为外部委托
- `UCommandRouterComponent` 在 BeginPlay 时通过 `AutoRegisterEndpoints` 自动发现并注册本组件
## 用例
- BP_TestChar 的组件,作为通用指令端点接收路由来的指令
- 外部蓝图通过绑定 `CommandReceivedDispatcher` 订阅指令事件

View File

@@ -0,0 +1,49 @@
# WBP_InventoryView
## 基本信息
- **类型**: Widget Blueprint (UserWidget)
- **父类**: UserWidget
- **源文件**: /Game/Blueprints/UI/Item/WBP_InventoryView.WBP_InventoryView
- **模块**: Content
## 功能概述
WBP_InventoryView 是物品列表显示 Widget持有 BP_InventoryComp 组件的引用,负责将库存中的物品视图列表渲染到 UI。通过 UpdateListItems 函数刷新列表显示,使用 WBP_ItemViewBox 作为单个物品视图框的子 Widget。
## 设计用意
WBP_InventoryView 是物品系统和 UI 系统之间的桥梁。它通过 Comp 变量引用 BP_InventoryComp从中读取 ViewCache 获取物品视图数据,然后渲染到 UI 列表。通过监听 Comp 的 OnViewChanged 委托它能在库存变化时自动刷新显示无需手动触发更新。这种设计将数据层BP_InventoryComp和显示层WBP_InventoryView完全解耦。
## 职责范围
WBP_InventoryView 负责显示物品列表 UI、监听库存变化并自动刷新、以及提供物品交互的入口。物品数据管理由 BP_InventoryComp 负责,物品存储由 UDefaultContainer 负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| BP_InventoryComp | 引用 (Comp) | /Game/Blueprints/Playground/BP_InventoryComp |
| FItemView | 显示数据 | Item 插件 |
| WBP_ItemViewBox | 子 Widget | /Game/Blueprints/UI/Item/WBP_ItemViewBox |
## 对外接口
- **蓝图函数**
- `UpdateListItems()` -- 刷新物品列表显示,从 `Comp` (BP_InventoryComp) 的 ViewCache 读取 FItemView 数据并更新 UI 列表
- **蓝图变量**(均为 is_instance_editable
- `Bo` (bool) -- 布尔标志位,用于控制 UI 显示/隐藏或其他状态
- `Comp` (BP_InventoryComp_C*) -- 目标库存组件引用,指向要显示的 BP_InventoryComp 实例。UI 从此组件的 ViewCache 读取物品数据
- **通过 Comp 变量访问的 BP_InventoryComp 接口**
- `ViewCache` (TArray<FItemView>) -- 物品视图缓存,包含 ItemID、ItemType、ItemName、ItemDescription、Icon、ItemDataText
- `OnViewChanged` (动态多播委托) -- 绑定此委托实现库存变化时自动调用 UpdateListItems
- `CreateItemInternal` -- 可用于触发物品创建(通常不直接从此 Widget 调用)
- **EventGraph**:含 5 个事件和 8 个节点
## 使用方法
- 作为 UMG UserWidget被 BP_Hud 的 `InvView` 变量引用(类型为 WBP_InventoryView_C*
- 设置 `Comp` 变量指向目标 Actor 上的 BP_InventoryComp 实例,建立 UI 到数据的绑定
- 在 EventGraph 中绑定 `Comp.OnViewChanged` 委托,每次库存变化时自动调用 `UpdateListItems` 刷新列表
- `UpdateListItems``Comp.ViewCache` 读取 FItemView 数组,遍历创建 WBP_ItemViewBox 子 Widget 填充列表
- WBP_ItemViewBox 作为单个物品的视图框,显示物品图标、名称、描述等信息
- `Bo` 布尔变量用于控制 ListView 的条件显示(如空库存时隐藏列表)
- 物品系统的数据流向UDefaultContainer (存储) -> BP_InventoryComp (管理+ViewCache) -> WBP_InventoryView (显示)
## 用例
- BP_Hud 的 `InvView` 变量引用,作为物品栏 UI 界面
- 通过 `Comp` 变量绑定 BP_InventoryComp显示玩家或掉落物的物品列表
- 使用 WBP_ItemViewBox 作为子 Widget 渲染单个物品视图

View File

@@ -0,0 +1,57 @@
# WBP_TestUI
## 基本信息
- **类型**: Widget Blueprint (UserWidget)
- **父类**: UserWidget
- **源文件**: /Game/Blueprints/UI/Dialog/WBP_TestUI.WBP_TestUI
- **模块**: Content
## 功能概述
WBP_TestUI 是对话演示脚本的显示/执行 Widget实现了 IPresentationScriptExecutor 接口。它接收 FDialogPresentationScriptData解析其中的 JSON 命令,构建 UI 选择按钮,驱动演示脚本的执行流。同时持有 Ultra Dynamic Sky 引用以支持环境变化命令。
## 设计用意
WBP_TestUI 是 Dialog 插件在 UI 层的具体实现,承担了脚本执行和 UI 展示的双重职责。在脚本执行侧,它通过 IPresentationScriptExecutor 接口接收 UDialogPresentationSubsystem 的脚本调用,使用 ExecuteScript 启动脚本、ProcessScript 逐条执行命令、ExecuteNextScript 控制流程跳转。在 UI 管理侧,它通过 SummonChoiceButtons 动态生成选项按钮、SetNextScript 设置跳转目标、Get_MainText_Text 绑定主文本显示。这种双职责设计使一个 Widget 即可完成从数据到显示的完整链路。
## 职责范围
WBP_TestUI 负责演示脚本的接收和执行、对话 UI 的生成和更新、脚本流程控制(跳转、选项分支)。命令的具体实现由 IPresentationStrategyPlugin 处理DataTable 管理由 UDialogPresentationSubsystem 负责。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogPresentationScriptData | 输入数据 | Dialog 插件 |
| FDialogPresentationScriptCommand | 输入数据 | Dialog 插件 |
| IPresentationScriptExecutor | 实现接口 | Dialog 插件 |
| UDialogPresentationSubsystem | 调用 | Dialog 插件 |
## 对外接口
- **蓝图函数**
- `ExecuteScript(FDialogPresentationScriptData InScript)` -- 启动演示脚本,接收脚本数据并开始执行流程
- `ProcessScript(FDialogPresentationScriptCommand Presentation)` -- 处理单条脚本命令,逐条执行演示命令
- `ExecuteNextScript(FName Name)` -- 跳转到指定名称的下一个脚本,实现脚本流程控制
- `SummonChoiceButtons(FDialogPresentationScriptData Data)` -- 根据脚本数据中的 Choices 字段动态生成选择按钮 UI
- `SetNextScritp(FDialogPresentationScriptData Data)` -- 设置下一个要执行的脚本(通过 NextScriptName 变量)
- `Get_MainText_Text() -> FText` -- 数据绑定函数,返回主文本框的对话文本内容
- **蓝图变量**(均为 is_instance_editable
- `NextScriptName` (FName) -- 存储下一个脚本的名称,用于流程跳转
- `Sky` (Ultra_Dynamic_Sky_C*) -- Ultra Dynamic Sky 引用,用于执行环境变化命令(如改变天气、时间)
- **IPresentationScriptExecutor 接口实现**BlueprintNativeEvent
- `ExecutePresentationScript(const FDialogPresentationScriptData& ScriptStruct)` -- 执行演示脚本
- `PlugStrategyIn(FName StrategyName, const TScriptInterface<IPresentationStrategyPlugin>& NewStrategy)` -- 插件式注入策略
- `UnplugStrategy(FName StrategyName)` -- 移除策略插件
- **EventGraph**:含 5 个事件和 8 个节点
## 使用方法
- 作为 UserWidget 创建并添加到视口,通常由 UDialogPresentationSubsystem 管理
- 实现 IPresentationScriptExecutor 接口,作为 Dialog 插件的脚本执行器
- UDialogPresentationSubsystem 调用 `ExecutePresentationScript` 投递脚本,本 Widget 通过 `ExecuteScript` 启动执行
- `ProcessScript` 逐条解析脚本命令:通过 UPresentationJsonLibrary 解析 JSON 命令字符串,识别 CommandName 和 Params
- `SummonChoiceButtons` 根据 Choices 数据动态创建选项按钮列表,按钮绑定 `ExecuteNextScript` 处理选择
- `SetNextScritp` / `ExecuteNextScript` 控制脚本流程的线性或分支推进
- `Get_MainText_Text` 作为 UMG 数据绑定函数,连接 UI 文本框显示对话内容
- `Sky` 变量用于连接到场景中的 Ultra Dynamic Sky Actor脚本命令可改变天气/时间等环境效果
- 策略插件通过 `PlugStrategyIn` / `UnplugStrategy` 动态注入,支持在运行时扩展脚本命令的处理策略
## 用例
- Dialog 系统的 UI 执行器,实现 IPresentationScriptExecutor 接口
- 由 UDialogPresentationSubsystem 调用,作为对话脚本的显示和交互界面
- 通过 `Sky` 变量连接 Ultra Dynamic Sky支持演示脚本中的环境效果命令

View File

@@ -0,0 +1,44 @@
# Content/Blueprints 依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| BP_TestChar | 父类 | AMyCharacter | Source/lonese/MyCharacter.h |
| BP_TestCtl | 父类 | AMyPlayerController | Source/lonese/MyPlayerController.h |
| BP_CameraPawn | 父类 | ACameraPawn | Source/lonese/CameraPawn.h |
| BP_TestMode | 父类 | GameModeBase | Engine |
| BP_Hud | 父类 | HUD | Engine |
| BP_UniversalEndpointComp | 父类 | UEndpointComponent | CharacterControl plugin |
| BP_MoveInput | 父类 | UEndpointComponent | CharacterControl plugin |
| BP_ControllerComp | 父类 | BP_EndpointComp_C | Content/Blueprints |
| BP_SayHello | 父类 | UEndpointComponent | CharacterControl plugin |
| BP_DefaultContainer | 父类 | UDefaultContainer | Item plugin |
| BP_InventoryComp | 父类 | ActorComponent | Engine |
| BP_DropItemInvComp | 父类 | BP_InventoryComp | Content/Blueprints |
| BP_Bomb | 父类 | Actor | Engine |
| WBP_TestUI | 父类 | UserWidget | Engine |
| WBP_InventoryView | 父类 | UserWidget | Engine |
| BP_TestChar | 组件 | BP_UniversalEndpointComp | Content/Blueprints |
| BP_TestChar | 组件 | BP_MoveInput | Content/Blueprints |
| BP_TestChar | 组件 | BP_ControllerComp | Content/Blueprints |
| BP_TestChar | 组件 | BP_SayHello | Content/Blueprints |
| BP_TestChar | 组件 | UCommandRouterComponent | CharacterControl plugin |
| BP_TestChar | CameraActorClass | BP_CameraPawn | Content/Blueprints |
| BP_TestMode | DefaultPawnClass | BP_TestChar | Content/Blueprints |
| BP_TestMode | PlayerControllerClass | BP_TestCtl | Content/Blueprints |
| BP_TestMode | HUDClass | BP_Hud | Content/Blueprints |
## 关键依赖链
Game Mode → Pawn: BP_TestMode (DefaultPawnClass=BP_TestChar, PlayerControllerClass=BP_TestCtl, HUDClass=BP_Hud)
Character → Camera: BP_TestChar → CameraActorClass=BP_CameraPawn → ACameraPawn
Input Pipeline: BP_TestCtl (AMyPlayerController) → Enhanced Input → UCommandInputComponent (on BP_TestChar) → UCommandRouterComponent → UEndpointComponent subclasses
Item Pipeline: BP_DefaultContainer (UDefaultContainer) ← BP_InventoryComp (IInventory) ← BP_DropItemInvComp
UI Pipeline: WBP_TestUI (implements IPresentationScriptExecutor) → UDialogPresentationSubsystem
Item UI: WBP_InventoryView → BP_InventoryComp (via ViewCache/OnViewChanged) → BP_DefaultContainer (IItemContainer)

415
HOWTO_REGENERATE_DOCS.md Normal file
View File

@@ -0,0 +1,415 @@
# 文档生成操作手册
## 这篇文章是干什么的
当你需要更新这个项目的文档,或者想把同一套方法套到另一个 UE 项目时,看这个就够了。
本文记录:
1. **踩过的坑** — 初版被骂的 10 项问题,别再犯
2. **正确的文档模板** — 每个单位应该长什么样
3. **完整操作步骤** — 从零到 56 个文档是怎么生成的
4. **工具和命令** — 用了什么、怎么用
---
## 一、血泪教训:初版的 10 项问题
第一次跑完「项目文档化计划」后,被用户逐条指出以下问题,全部在修订版中修正:
| # | 问题 | 初版做法 | 正确做法 |
|---|------|---------|---------|
| 1 | 多个单位合并到一个文件 | `ItemDataStructures.md` 里塞了 3 个 struct | 每个结构体/类/接口/枚举独立一个 .md |
| 2 | "设计用意"和"职责范围"合并 | 一个 `## 设计用意与职责范围` 章节糊在一起 | 拆成两个独立章节 |
| 3 | "职责范围"用二分法 | 写"职责xxx / 不负责yyy" | 自然语言描述工作范围 |
| 4 | 多余的模块 | 文档里出现"变量"、"关键成员"表格 | 不写实现细节,对外接口从调用者视角描述 |
| 5 | 使用方法不引用真代码 | 写"可以这样用"但没有出处 | 每条引用真实文件路径和行号 |
| 6 | 用例编造示例代码 | 写伪代码 `// 继承 AMyActor...` | 只列出项目中实际使用该单位的文件 |
| 7 | `_relationships.md` 太花哨 | ASCII 艺术图、设计模式分析 | 简单的文件级依赖表格 + 文本箭头 |
| 8 | 执行顺序混乱 | 边写接口边写依赖 | 严格:孤立文档 → 关系文档 → 接口+用例 |
| 9 | README.md 没更新 | 还是旧的待办列表 | 更新为完整的文档索引 |
| 10 | "项目内依赖"列名不对 | 用了"说明"列 | 改为"源文件"列,放文件路径 |
### 为什么这些问题很重要
- **合并文件**:后人要找 `FItemView` 的文档时,不能一眼看到文件名,还得打开文件在里面搜。单位名和文件名一一对应,是文档可发现性的底线。
- **设计用意 vs 职责范围**:前者回答"为什么存在",后者回答"管什么事"。混在一起会让维护者搞不清这个类的边界。
- **真实代码引用**:没有行号的使用说明是废纸。半年后代码改了,维护者无法验证文档是否过期。
---
## 二、正确的文档模板
### 2.1 孤立单位文档模板
每个单位class/struct/enum/BP一个 .md 文件,**严格**以下结构:
```markdown
# [单位名称]
## 基本信息
- **类型**: UCLASS / USTRUCT / UENUM / C++ class / Blueprint
- **父类**: XXX没有就写 —)
- **源文件**: path/to/file.h蓝图写 /Game/ 路径)
- **模块**: ModuleName
## 功能概述
[一段话概括这个单位做什么,什么场景用]
## 设计用意
[为什么这样设计,解决什么问题,在系统中的定位]
## 职责范围
[自然语言描述该单位承担的工作范围,它负责什么流程/数据/决策。
禁止使用"职责"/"不负责"二分格式]
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| XXX | 包含/引用/继承/调用 | path/to/file.h:行号 |
## 对外接口
[从外部调用者的角度描述:
- C++ 调用者:哪些 public 方法/虚函数可调用
- 蓝图调用者:哪些 UFUNCTION/BlueprintNativeEvent 可调用
- 委托:哪些委托可绑定
- 结构体:哪些 UPROPERTY 字段对外可读写
禁止单纯罗列函数签名]
## 使用方法
[引用项目中的真实代码位置说明如何使用本单位的典型流程。
每条使用方式必须给出源文件和行号出处]
## 用例
[列举项目中实际使用本单位的文件和上下文。不编造示例。
格式:文件路径:行号 — 用途说明]
```
### 2.2 关系文档模板 (`_relationships.md`)
每个模块目录下一个:
```markdown
# [模块名] 依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| XXX.h | #include | YYY.h | YYY |
| XXX.h | forward-declare | — | ZZZ |
## 关键依赖链
[用简单的文本箭头描述关键数据/控制流,如:
DefaultContainer.cpp → ItemFactory::CreateItemInstance → FItemInstance
]
```
### 2.3 README.md 模板
`Document/README.md`,核心是每个模块的完整单位列表表,含:单位名、文件名(链接)、类型、父类。
---
## 三、完整操作步骤
以下是在一个全新的 UE 项目上复现整个过程的操作步骤。
### 前置条件
1. **Claude Code CLI**(本文使用的 AI 编程助手)
- 安装方式见 [Claude Code 官方文档](https://docs.anthropic.com/en/docs/claude-code)
- 关键是它支持 Task 工具(可以启动后台子代理并行干活)
- 支持 MCP用于查询 UE 编辑器中的蓝图信息)
2. **UnrealClaude MCP 插件** — 安装在项目的 `Plugins/UnrealClaude/`
- 提供 `unreal_blueprint_query``unreal_asset_search` 等 MCP 工具
- 用来查询蓝图资产信息(因为在 .uasset 二进制文件上没法直接 grep
3. **ripgrep** (`rg`) — 用于在源代码中搜索单位引用
4. **bash shell** — Windows 上用 Git Bash 或 WSLmacOS/Linux 直接用
### 步骤 1确定文档化范围
先画出要文档化的所有单位清单:
```bash
# 列出所有需要文档化的 C++ 头文件
find Source/ -name "*.h" | grep -v Intermediate | grep -v Generated
find Plugins/ -name "*.h" -path "*/Public/*" | grep -v Intermediate | grep -v Generated
# 列出所有需要文档化的蓝图(通过 MCP
# 在 Claude Code 对话中使用:
# mcp__unrealclaude__unreal_blueprint_query operation=list path_filter=/Game/Blueprints/
```
对每个头文件,手动列出它包含的所有单位(一个 .h 可能包含多个 class/struct/enum。这就是你的文档化清单。
### 步骤 2分析所有源文件
这是最耗时但最关键的一步。你需要从每个源文件中提取:
- 单位的类型、父类、所属模块
- 功能概述(从注释和代码推断)
- 设计意图(为什么存在)
- `#include` 依赖关系
- 前向声明
- public 方法、UFUNCTION、delegate
**用 Claude Code 的做法**(推荐):
启动多个 `Task` 子代理并行分析:
```
对 Claude Code 说:
"用 Explore 子代理分析 D:\workspace\u\lonese\Plugins\Item\ 目录下所有 .h 和 .cpp 文件,
对每个 class/struct/enum 提取:类型、父类、功能、设计意图、依赖、公开方法。"
```
关键提示词要点:
- 让代理先 `Glob` 找到所有 .h 文件,再逐文件 `Read`
- 要求结构化输出(每个单位一个条目)
- 明确列出需要的字段:类型/父类/文件路径/模块/功能/设计意图/include依赖/前向声明/公开方法/UFUNCTION/delegate/BlueprintNativeEvent
**手动做法**(如果没有 AI 工具):
```
对每个 .h 文件:
1. 记录文件中定义的所有 UCLASS/USTRUCT/UENUM/接口
2. 记录每个单位的父类和包含的引擎头文件
3. 搜索 #include "项目内头文件" 记录依赖
4. 搜索 forward declaration
5. 记录 UPROPERTY 和 UFUNCTION 标记
6. 从类/方法注释中提取功能描述
```
结果应该整理成一张大表,每个单位一行,包含上述所有字段。
### 步骤 3阶段 1 — 生成孤立单位文档
对每个单位创建一个 .md 文件,这个阶段**只填**
- 基本信息
- 功能概述
- 设计用意
- 职责范围
- 项目内依赖
**暂不填**(留占位符 `待阶段3填写`
- 对外接口
- 使用方法
- 用例
**用 Claude Code 的做法**
```markdown
对 Claude Code 说:
"在 Document/Plugins/Item/ 下为以下每个单位创建独立的 .md 文件,
遵循 [粘贴模板]。每个单位一个文件,不要合并。
现在只填基本信息、功能概述、设计用意、职责范围、项目内依赖。
对外接口、使用方法、用例三个章节写'待阶段3填写'。"
```
然后把每个单位的结构化分析数据附在后面。
**注意**
- 每个单位**必须独立成文件**,绝对不能合并
- "设计用意"和"职责范围"是**两个独立章节**,不要偷懒合并
- "职责范围"用自然语言,**禁止**出现"职责:/不负责:"这种二分写法
- 依赖表列名必须是 `依赖项 | 关系 | 源文件`
**手动做法**
```
对清单中的每个单位:
1. 创建 Document/[模块]/[单位名].md 文件
2. 从步骤 2 的分析结果中抄入:基本信息、功能概述、设计用意、职责范围、依赖
3. 最后三节写 "待阶段3填写"
```
### 步骤 4阶段 2 — 生成关系文档
基于阶段 1 的依赖数据,为每个模块创建 `_relationships.md`
**用 Claude Code 的做法**
```markdown
对 Claude Code 说:
"为 Document/Plugins/Item/ 创建 _relationships.md。
表格列出每个文件间的 #include/forward-declare 关系。
再列出关键数据/控制流依赖链,用简单文本箭头。"
```
**手动做法**
```
1. 画一张表:
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
2. 从步骤 2 的 #include/forward-declare 数据中抄入
3. 画出关键数据流:
- 创建流程:谁调用谁的什么方法创建什么
- 查询流程:谁通过谁获取什么数据
- 更新流程:数据从哪来,经过谁,到哪去
```
### 步骤 5阶段 3 — 填充接口、使用方法和用例
这是最需要"搜代码"的阶段。
**对外接口**的做法:
```
对每个 C++ 单位:
1. 打开其头文件
2. 找出所有 public/protected 方法
3. 找出所有 UFUNCTION 宏BlueprintCallable/BlueprintNativeEvent/BlueprintImplementableEvent
4. 找出所有 UPROPERTY 宏(尤其是 BlueprintReadOnly/BlueprintReadWrite 的)
5. 找出所有 DECLARE_DELEGATE / DECLARE_DYNAMIC_MULTICAST_DELEGATE
6. 用"调用者视角"重写:不要罗列函数签名,而是描述"外部代码可以做哪些事"
好例子:"调用 GetItemViews() 获取所有物品的只读视图,用于 UI 列表展示"
坏例子:"GetItemViews() const -> TArray<FItemView>"(这是罗列签名)
对每个蓝图单位:
1. 通过 MCP unreal_blueprint_query operation=inspect 查询变量和函数
2. 描述蓝图事件图中可被外部调用的函数和事件
3. 列出可编辑的实例变量
```
**使用方法**的做法:
```
对每个单位:
1. grep 搜索它在项目中的所有使用位置
rg "[单位名]" Source/ Plugins/ --type cpp --type h
2. 读相关代码段,理解它被如何使用
3. 挑选 2-5 个最典型的用法
4. 写成:文件路径:行号 — 在这段代码中是怎么用的
```
**用例**的做法:
```
对每个单位:
1. 从 grep 结果中确定哪些文件会用到它
2. 对每个使用文件写一条:文件路径:行号 — 用途说明
3. 不要伪造。如果某个文件只是 include 了但没实际用,不要写进去
```
**用 Claude Code 的做法**
```markdown
对 Claude Code 说:
"对 Document/Plugins/Item/ 下的所有 .md 文件,
用 Grep 搜索每个单位在 Source/ 和 Plugins/ 中的使用位置,
然后更新对外接口、使用方法、用例三个章节。
要求每条引用有文件路径和行号,只写真实代码中存在的用法。"
```
### 步骤 6阶段 4 — 更新 README.md
```
1. 列出所有 5 个模块
2. 每个模块下列出所有单位、文件名(带链接)、类型、父类
3. 描述文档模板的章节结构
4. 写下统计数字:多少单位、多少文件、覆盖哪些模块
```
### 步骤 7验证
```
□ 每个 .h 中定义的 class/struct/enum 都有对应的 .md 文件
□ 没有多单位合并到一个文件的情况(检查文件名列表跟单位列表一一对应)
□ 每个 .md 的"设计用意"和"职责范围"是两个独立章节
□ "职责范围"中没有出现"不负责"字样(这不是要求删除信息,而是用自然语言描述边界)
□ 每个 _relationships.md 的表格列名是"源文件|引用方式|目标文件|目标单位"
□ 每个使用方法/用例条目都带有文件路径和行号
□ 没有编造的伪代码示例
□ README.md 有完整的文档索引
□ 所有占位符"(待阶段3填写)"已被替换
```
---
## 四、工具速查
### 4.1 Grep 搜索命令
```bash
# 搜索某个类/结构体在项目中的使用
rg "FItemView" Plugins/Item/ Source/ --type-add 'ue:*.h' --type-add 'ue:*.cpp' --type ue -n
# 只列出文件名
rg "IItemContainer" -l
# 搜索 #include 关系
rg '#include.*ItemFactory' Plugins/Item/
```
### 4.2 MCP 蓝图查询命令
在 Claude Code 对话中可以直接用以下工具:
```
# 列出所有蓝图
mcp__unrealclaude__unreal_blueprint_query operation=list path_filter=/Game/Blueprints/ limit=50
# 查看单个蓝图的变量和函数
mcp__unrealclaude__unreal_blueprint_query operation=inspect
blueprint_path=/Game/Blueprints/BP_TestChar
include_variables=true
include_functions=true
# 搜索资产
mcp__unrealclaude__unreal_asset_search class_filter=Blueprint name_pattern=Test
```
### 4.3 Claude Code Task 代理
```
# 启动一个分析代理(后台运行)
Task subagent_type=Explore run_in_background=true
prompt="分析 X 目录下所有头文件..."
# 启动一个写文件的代理(后台运行)
Task subagent_type=general-purpose run_in_background=true
prompt="为以下单位创建/更新文档..."
# 查看代理输出
TaskOutput task_id=<代理ID> block=true timeout=120000
```
---
## 五、作者注
### 我是怎么被骂的
第一轮跑完「项目文档化计划」后,用户逐项检查,指出了 10 个问题(详见第一章)。核心批评:
1. **合并文件是最愚蠢的错误** — 文档的目的是让人查,文件名就是索引。把三个 struct 塞进一个 `ItemDataStructures.md`,等于给后人埋坑。
2. **"设计用意"和"职责范围"不能合并** — 前者是历史/动机,后者是边界。维护者需要知道"为什么存在",也需要知道"管多宽"。
3. **自我编造示例代码** — 我写了一堆 `// 继承 AMyActor 创建自定义 Actor` 的伪代码。用户一眼看出这不是项目里的真实代码。
4. **罗列函数签名** — 对外接口章节写成了 API reference而不是从调用者角度说明"你能干什么"。
5. **花式 ASCII 图**`_relationships.md` 里搞了一堆 ASCII 艺术框图。用户要的是简洁的文件级表格。
### 为什么这个流程可以复现
- **模板化**:每个文档的结构完全一致,没有自由发挥空间
- **可验证**每条引用必须有文件路径和行号grep 一下就能确认
- **分阶段**4 个阶段每条有明确的输入和输出,不会出现循环依赖
- **工具支持**Claude Code 的 Task 代理可以并行处理大量文件MCP 可以查询二进制蓝图
### 如果你没有 Claude Code
整个流程的核心逻辑不依赖 AI 工具。你可以纯手工完成:
1. 在 Excel/Notion 里建一张表,列出所有单位及其分析数据(步骤 2
2. 按模板逐个创建 .md 文件(步骤 3-5
3. 用 grep/VSCode 搜索来验证引用关系(步骤 7
只是一个人做 56 个文档大概需要 3-5 个工作日。
### 后续维护
代码改动后,更新对应文档:
- 新增类:按模板创建新 .md更新 `_relationships.md``README.md`
- 删除类:删除对应 .md更新 `_relationships.md``README.md`
- 修改接口:更新"对外接口"章节和对应的"使用方法"引用
- 修改依赖:更新"项目内依赖"表和 `_relationships.md`

View File

@@ -0,0 +1,43 @@
# FCommandMeta
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (none)
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Metadata header for every command packet. Carries CommandID (int32, from UInputAction), HopLimit (uint8, mutable, decremented per router hop), bIsContinuous (bool, routing strategy selector), bIsUpward (bool, upward propagation flag).
## 设计用意
Routing envelope - decouples "what the command is" (payload) from "how it travels" (hop limit, direction). HopLimit prevents broadcast storms in router graphs. Upward flag supports parent-child router topologies.
## 职责范围
Carries routing metadata for one command packet. Embedded in FCommandPacket::Meta. Decremented at each router hop.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| (none) | | |
## 对外接口
FCommandMeta 是嵌入在 FCommandPacket::Meta 中的数据容器,所有字段通过 UPROPERTY(BlueprintReadOnly) 暴露给蓝图。调用者不直接构造 FCommandMeta而是由 UCommandInputComponent::BuildPacket 在输入事件触发时自动填充。路由过程中 UCommandRouter::ReceiveCommand 会递减 HopLimit。
- **CommandID** (int32, BlueprintReadOnly): 由 BuildPacket 从 UInputAction::GetUniqueID() 填充,标识命令来源
- **HopLimit** (uint8, mutable, BlueprintReadOnly): 每经过一层路由递减 1耗尽则丢包。BuildPacket 对连续命令设为 3、离散命令设为 5
- **bIsContinuous** (bool, BlueprintReadOnly): 决定路由分发策略(连续走 RouteContinuousCommand离散走 RouteDiscreteCommand
- **bIsUpward** (bool, BlueprintReadOnly): 上行传播标志,在上级路由器中决定是否同时投递本地和向上转发
## 使用方法
FCommandMeta 作为 FCommandPacket 的成员由 BuildPacket 构建,在 CommandRouter 中消费:
- **构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:70-72` -- BuildPacket 设置 CommandID、bIsContinuous、HopLimit连续 3、离散 5
- **跳数递减**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:16-21` -- ReceiveCommand 检查 HopLimit 并在 >0 时递减
- **上行传播跳数递减**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:155` -- 上行命令在路由器层面再递减一次
- **路由策略选择**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:23-30` -- 根据 bIsContinuous 选择 RouteContinuousCommand 或 RouteDiscreteCommand
## 用例
- **BuildPacket 构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:70-72` -- 输入触发时根据 FInputCommand::bIsContinuous 设置 Meta 各字段,连续命令 HopLimit=3离散命令 HopLimit=5
- **HopLimit 防护**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:16-18` -- ReceiveCommand 入口处首先检查 HopLimit<=0 直接丢包,防止广播风暴
- **逐跳递减**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:21` -- 每经过一个路由器mutable 的 HopLimit 减 1确保命令不会无限传播
- **上行转发**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:148-155` -- 当 bIsUpward 为 true 时,先投递本地(清除上行标志),再向上级路由器转发

View File

@@ -0,0 +1,47 @@
# FCommandPacket
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (none)
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Complete unified command message flowing through routing system. Composed of Meta (FCommandMeta), ContinuousPayload (for continuous commands), DiscretePayload (TInstancedStruct<FDiscreteMeta>, for discrete commands).
## 设计用意
Unified message envelope. All routers and endpoints share single ReceiveCommand(const FCommandPacket&) interface. TInstancedStruct for discrete payload enables future extensibility without changing packet layout.
## 职责范围
Carries ONE command through the routing system. Either continuous or discrete payload is valid (determined by Meta.bIsContinuous).
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FCommandMeta | 包含 (成员) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
| FContinuousPayload | 包含 (成员) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
| FDiscreteMeta | 包含 (TInstancedStruct) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
## 对外接口
FCommandPacket 是贯穿整个路由系统的统一命令消息。所有路由器、端点组件、输入组件的 ReceiveCommand 接口都接收 `const FCommandPacket&`。三个 UPROPERTY(BlueprintReadWrite) 成员字段:
- **Meta** (FCommandMeta): 携带路由元数据CommandID、HopLimit、bIsContinuous、bIsUpward
- **ContinuousPayload** (FContinuousPayload): 连续命令的有效载荷,当 Meta.bIsContinuous 为 true 时有效
- **DiscretePayload** (TInstancedStruct<FDiscreteMeta>): 离散命令的有效载荷,当 Meta.bIsContinuous 为 false 时有效。使用 TInstancedStruct 支持未来扩展
命令包在 UCommandInputComponent::BuildPacket 中构建,通过 FOnCommandOutput 委托广播,最终到达所有匹配端点的 ReceiveCommand。路由过程中 Meta.HopLimit 被递减,但载荷内容保持不变。
## 使用方法
FCommandPacket 的生命周期BuildPacket 构建 -> 委托广播 -> Router 接收分发 -> Endpoint 接收:
- **构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:41-80` -- BuildPacket 根据 FInputCommand 和 FInputActionInstance 构造完整的 FCommandPacket
- **广播**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:108-110` -- 在 BindAllCommands 的 lambda 中构建后通过 OnCommandOutput.ExecuteIfBound 广播
- **路由消费**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:13-31` -- ReceiveCommand 检查 HopLimit 后分发到 RouteContinuousCommand 或 RouteDiscreteCommand
- **端点消费**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:16-19` -- ReceiveCommand 转调 OnCommandReceived 蓝图事件
- **蓝图桥接**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:23-26` -- ICommandEndpoint 默认实现通过 Execute_ReceiveCommand_BP 转发到蓝图
## 用例
- **BuildPacket 构造**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:41-80` -- 输入触发时BuildPacket 从 FInputCommand 配置和 FInputActionInstance 运行时值构造完整的 FCommandPacket
- **BindAllCommands 中广播**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:106-111` -- 每次输入事件触发 lambdaBuildPacket 构造包OnPacketBuilt 钩子执行后通过 OnCommandOutput 广播
- **路由器入口分发**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:13-31` -- ReceiveCommand 根据 bIsContinuous 分发到不同的路由路径
- **上行传播本地拷贝**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:151-153` -- 上行命令先做本地拷贝(清除 bIsUpward投递本地端点后再向上转发原始命令

View File

@@ -0,0 +1,41 @@
# FContinuousPayload
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (none)
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Payload for continuous (analog) commands. Contains CommandTag (FGameplayTag, semantic ID) and ContinuousValue (FVector3f, analog value from input).
## 设计用意
Separates continuous payload from discrete. FVector3f supports Boolean (0 or 1), Axis1D, Axis2D, Axis3D input types. Tag-based identification means endpoints subscribe by semantics, not input action IDs.
## 职责范围
Data container for continuous input values. Used when FCommandMeta::bIsContinuous is true.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| (none) | | |
## 对外接口
FContinuousPayload 是连续(模拟量)命令的数据载荷,作为 FCommandPacket::ContinuousPayload 成员存在。所有字段为 BlueprintReadWrite可在蓝图/C++ 中读写。
- **CommandTag** (FGameplayTag, BlueprintReadWrite): 命令的语义标签,端点通过匹配此标签来订阅连续命令
- **ContinuousValue** (FVector3f, BlueprintReadWrite): 从 Enhanced Input 转换来的模拟量值。Boolean 输入转为 OneVector/ZeroVectorAxis1D 存入 X 分量Axis2D 存入 XYAxis3D 存入 XYZ
调用者不直接创建 FContinuousPayload而是通过 UCommandInputComponent::BuildPacket 在连续输入事件触发时自动构建。路由匹配时 UCommandRouter::IsEndpointMatched 读取 CommandTag 与端点 InterestedTags 进行比对。
## 使用方法
FContinuousPayload 由 BuildPacket 根据输入值类型自动构造:
- **构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:73-75` -- 使用聚合初始化构造,`FContinuousPayload{ Command.ContinousCommandTag, ContinuousValue }`;非连续命令则构造为空 `FContinuousPayload{}`
- **值转换**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:46-67` -- 根据 EInputActionValueType 将 Boolean/Axis1D/Axis2D/Axis3D 统一转换为 FVector3f
- **匹配使用**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:277` -- IsEndpointMatched 检查端点 InterestedTags 是否包含 CommandTag
## 用例
- **连续输入构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:73-74` -- bIsContinuous 为 true 时BuildPacket 将 FInputCommand::ContinousCommandTag 和转换后的 ContinuousValue 打包进 FContinuousPayload
- **空载荷构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:75` -- 离散命令时 ContinuousPayload 为空
- **连续命令匹配**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:277` -- 连续命令路由时,端点必须声明 bIsContinuousFriendly 且 InterestedTags 包含 CommandTag 才能收到命令

View File

@@ -0,0 +1,43 @@
# FDiscreteMeta
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (none)
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Routing metadata for discrete (one-shot) commands. Contains CommandTags (FGameplayTagContainer, semantic IDs for endpoint matching) and bIsAll (bool, match mode: ALL tags must match vs ANY tag).
## 设计用意
Bridges raw input events to tag-based subscription model. Defines matching semantics. TInstancedStruct in FCommandPacket allows this to be subclassed for specialized routing logic.
## 职责范围
Defines subscription matching rules for one discrete command. Used by UCommandRouter::IsEndpointMatched for endpoint filtering.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| (none) | | |
## 对外接口
FDiscreteMeta 定义离散命令的订阅匹配规则。作为 TInstancedStruct<FDiscreteMeta> 嵌入在 FCommandPacket::DiscretePayload 和 FInputCommand::DiscreteCommandMeta 中。所有字段为 BlueprintReadWrite。
- **CommandTags** (FGameplayTagContainer, BlueprintReadWrite): 离散命令携带的语义标签集合,路由时与端点 InterestedTags 进行匹配
- **bIsAll** (bool, BlueprintReadWrite): 匹配模式开关。true = ALL 模式(端点必须包含所有 CommandTagsfalse = ANY 模式(端点包含任意一个即可)
调用者在 UInputCommandData 资产中配置每个 FInputCommand 的 DiscreteCommandMeta。运行时由 UCommandRouter::IsEndpointMatched 读取并执行匹配逻辑。因为有 TInstancedStruct 支持,可子类化 FDiscreteMeta 添加自定义路由字段。
## 使用方法
FDiscreteMeta 在 InputCommandData 资产中配置,运行时由路由器使用:
- **配置定义**: `Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h:24` -- FInputCommand::DiscreteCommandMeta 字段类型为 `TInstancedStruct<FDiscreteMeta>`
- **包构建**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:76-78` -- 离散命令时 DiscretePayload 直接取用 Command.DiscreteCommandMeta
- **提取验证**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:221-249` -- ExtractDiscreteMeta 验证 bIsContinuous、有效性、类型兼容性后提取
- **匹配判断**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:301-327` -- IsEndpointMatched 根据 bIsAll 使用 HasAll 或 HasAny 匹配
## 用例
- **ALL 模式匹配**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:308-318` -- 当 bIsAll 为 true 时,端点必须用 HasAll 完全包含命令的所有 CommandTags 才会被投递
- **ANY 模式匹配**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:322-323` -- 当 bIsAll 为 false 时,端点 InterestedTags 中包含任意一个 CommandTags 即命中
- **类型兼容性检查**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:240-245` -- ExtractDiscreteMeta 运行时检查 DiscretePayload 的 ScriptStruct 是否为 FDiscreteMeta 的子类,确保 TInstancedStruct 扩展安全
- **空命令防护**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:311-315` -- CommandTags 为空时不匹配任何端点,避免无标签命令广播给所有端点

View File

@@ -0,0 +1,47 @@
# FEndpointState
## 基本信息
- **类型**: USTRUCT(BlueprintType) with TStructOpsTypeTraits specialization
- **父类**: (none)
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Full state and identity of a command endpoint. Contains EndpointGuid (FGuid, unique identity), InterestedTags (FGameplayTagContainer, subscription list), bIsActive, bIsContinuousFriendly, bIsAsynchronous flags. Custom operator= preserves GUID on copy while updating config fields. TStructOpsTypeTraits with WithCopy=true forces UE to use C++ copy path instead of memcpy.
## 设计用意
Solves endpoint identity problem in dynamic routing graph. Custom copy operator prevents CDO/Blueprint default overwriting of runtime-assigned GUID. Tag subscription model enables declarative routing (no hard references between producers/consumers).
## 职责范围
Identity and configuration for one endpoint in the routing graph. GUID serves as registry key in UCommandRouter. Tags determine which commands the endpoint receives.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| (none) | | |
## 对外接口
FEndpointState 定义命令端点的完整身份和配置。在 UCommandRouter::EndpointState、UEndpointComponent::EndpointState、UEndpointDispatcher::CurrentState 中作为成员使用。
- **EndpointGuid** (FGuid, VisibleAnywhere, BlueprintReadOnly): 端点的唯一标识符,自动初始化为 NewGuid(),路由注册表以此为 Key。自定义 operator= 在目标已有身份时保留 GUID防止 CDO/蓝图赋值覆盖运行时身份
- **InterestedTags** (FGameplayTagContainer, EditDefaultsOnly, BlueprintReadOnly): 端点订阅的命令标签集合,决定端点接收哪些命令
- **bIsActive** (bool, EditDefaultsOnly, BlueprintReadOnly): 端点活跃开关false 时 IsEndpointMatched 直接返回 false端点不接收任何命令
- **bIsContinuousFriendly** (bool, EditDefaultsOnly, BlueprintReadOnly): 是否接收连续命令false 的端点不会被投递连续命令
- **bIsAsynchronous** (bool, EditDefaultsOnly, BlueprintReadOnly): 异步端点标志,端点应在自身 ReceiveCommand 中自行调度异步执行以保证线程安全
FEndpointState 有 TStructOpsTypeTraits<WithCopy=true> 特化,强制 UE 使用 C++ 拷贝路径(而非 memcpy确保自定义 operator= 被正确调用。
## 使用方法
FEndpointState 在不同位置设定和使用:
- **端点配置**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:13` -- UEndpointComponent 构造函数中设置 `EndpointState.bIsActive = true`
- **路由器配置**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:8-11` -- UCommandRouter 构造函数中设置 `bIsActive=true, bIsContinuousFriendly=true, bIsAsynchronous=false`
- **注册时读取**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:52-53` -- RegisterEndpoint 读取 EndpointGuid 验证有效性和唯一性
- **匹配时读取**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:266-298` -- IsEndpointMatched 读取 bIsActive 和 bIsContinuousFriendly 决定匹配结果
- **状态变更**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:33-42` -- SetRouterState 只更新配置字段,保留 Guid 并广播 StateChangedDelegate
## 用例
- **GUID 保护机制**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h:88-105` -- 自定义 operator= 通过 bGuidAssigned 标志判断:未分配时拷贝 GUID已分配时保留现有 GUID。防止蓝图 CDO 属性灌装覆盖运行时赋予的唯一 ID
- **端点注册验证**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:53-58` -- RegisterEndpoint 检查 EndpointGuid 有效性,无效时拒绝注册并输出警告
- **自注册防护**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:65-69` -- 检查端点 GUID 是否等于路由器自身 GUID防止拓扑环路
- **匹配判断入口**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:268-271` -- bIsActive 为 false 时直接返回 false快速短路所有匹配逻辑

View File

@@ -0,0 +1,44 @@
# FInputCommand
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (none)
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h
- **模块**: CharacterControl
## 功能概述
Data row mapping a UInputAction to command routing parameters. Contains InputAction reference, bIsContinuous flag, ContinousCommandTag (FGameplayTag for continuous), DiscreteCommandMeta (TInstancedStruct<FDiscreteMeta> for discrete).
## 设计用意
Designer configuration unit. Answers "When this input fires, what command packet should be generated?" Separates continuous and discrete metadata into distinct fields reflecting different routing semantics.
## 职责范围
Configuration data for one input-to-command mapping. Used by UCommandInputComponent::BuildPacket. Stored in UInputCommandData::InputCommands array.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| CommandEndpoint.h | #include (for FDiscreteMeta, etc.) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
## 对外接口
FInputCommand 是一个纯数据配置结构体,定义"某个 Enhanced Input 动作触发时生成什么命令"的映射规则。在 UInputCommandData 资产编辑器中通过详情面板配置。所有字段为 BlueprintReadWrite。
- **InputAction** (TObjectPtr<UInputAction>, BlueprintReadWrite): 引用的 Enhanced Input Action 资产,用于绑定输入事件
- **bIsContinuous** (bool, BlueprintReadWrite): 命令类型开关。true = 连续命令(使用 Triggered 事件每帧触发false = 离散命令(使用 Started 事件,一次性)
- **ContinousCommandTag** (FGameplayTag, BlueprintReadWrite): 连续命令的语义标签BuildPacket 时写入 FContinuousPayload::CommandTag
- **DiscreteCommandMeta** (TInstancedStruct<FDiscreteMeta>, BlueprintReadWrite): 离散命令的路由元数据,包含 CommandTags 和 bIsAll 匹配模式。BuildPacket 时直接拷贝到 FCommandPacket::DiscretePayload
调用者通过 UInputCommandData 资产配置 FInputCommand 数组,运行时由 UCommandInputComponent::BuildPacket 逐字段读取构建命令包。
## 使用方法
FInputCommand 在 UInputCommandData::InputCommands 数组中配置,运行时由 UCommandInputComponent 读取:
- **配置定义**: `Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h:39` -- UInputCommandData::InputCommands 类型为 `TArray<FInputCommand>`
- **BuildPacket 消费**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:41-80` -- BuildPacket 从 FInputCommand 读取 InputAction获取 CommandID、bIsContinuous决定处理路径、ContinousCommandTag连续载荷标签、DiscreteCommandMeta离散载荷
- **绑定循环**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:96` -- BindAllCommands 遍历 `CommandData->InputCommands` 数组
- **事件类型选择**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:99-101` -- 根据 bIsContinuous 选择 ETriggerEvent::Triggered 或 Started
## 用例
- **连续命令配置**: 策划在 UInputCommandData 资产中将某个 FInputCommand 的 bIsContinuous 设为 true设置 ContinousCommandTag如 "Input.Move"BuildPacket 将摇杆移动量转为 FVector3f生成每帧触发的连续命令包
- **离散命令配置**: 策划将 bIsContinuous 设为 false配置 DiscreteCommandMeta.CommandTags如 "Action.Jump")和 bIsAll 匹配模式BuildPacket 在按键按下时生成一次性命令包
- **BuidPacket 字段读取**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:70-78` -- BuildPacket 逐一读取 Command.InputActionCommandID、Command.bIsContinuousMeta+载荷选择、Command.ContinousCommandTag连续载荷、Command.DiscreteCommandMeta离散载荷组装完整的 FCommandPacket

View File

@@ -0,0 +1,56 @@
# ICommandEndpoint
## 基本信息
- **类型**: UINTERFACE + C++ interface
- **父类**: UInterface
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Central abstraction for all nodes in command routing graph. Pure virtual methods: GetEndpointState, GetStateChangedDelegate, GetCommandOutputDelegate, ReceiveCommand. BlueprintNativeEvents: ReceiveCommand_BP, GetEndpointDispatcher_BP. Default C++ implementations route through dispatcher for Blueprint subclasses, while C++ subclasses override directly.
## 设计用意
Uniform interface enabling heterogeneous routing graph. Routers manage collections of ICommandEndpoint without knowing concrete types. Dispatcher pattern solves UE limitation that BlueprintNativeEvent can't return delegate references directly.
## 职责范围
Defines contract for command routing participants. All routers, endpoint components, and input components implement this. Single ReceiveCommand entry point for all command types.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FEndpointState | 返回类型 (GetEndpointState) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
| FCommandPacket | 参数类型 (ReceiveCommand) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
| UEndpointDispatcher | 返回类型 (GetEndpointDispatcher_BP) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
## 对外接口
ICommandEndpoint 是命令路由图中所有参与者的统一抽象接口。任何希望接收或产出命令的对象都需要实现此接口。接口包含四个 C++ 纯虚方法和两个 BlueprintNativeEvent。
**C++ 纯虚方法**C++ 子类直接重写):
- **GetEndpointState()** -> const FEndpointState&: 返回端点的身份和配置状态。UCommandRouter::RegisterEndpoint 以此获取 GUID 和 InterestedTags
- **GetStateChangedDelegate()** -> FOnEndpointStateChanged&: 返回状态变更委托引用。Router 通过 BindDynamic 订阅,当端点状态变化时触发标签聚合更新
- **GetCommandOutputDelegate()** -> FOnCommandOutput&: 返回命令输出委托引用。Router 订阅以接收端点产出的命令,转发给其他端点
- **ReceiveCommand(const FCommandPacket&)**: 接收命令的统一入口。Router 调用此方法向匹配的端点投递命令
**BlueprintNativeEvent**(蓝图子类重写):
- **ReceiveCommand_BP(const FCommandPacket&)**: 蓝图端点的命令接收入口。C++ 默认实现通过调度器桥接调用此事件
- **GetEndpointDispatcher_BP()** -> UEndpointDispatcher*: 蓝图端点返回调度器实例。C++ 默认实现通过此方法获取委托引用
**默认实现策略**C++ 默认实现CommandEndpoint.cpp:8-26全部通过 Execute_GetEndpointDispatcher_BP 获取 UEndpointDispatcher然后委托执行。这意味着蓝图类如 UEndpointComponent_BP 子类)必须重写 GetEndpointDispatcher_BP 并返回有效调度器。C++ 类UCommandRouter、UCommandRouterComponent、UEndpointComponent则直接重写纯虚函数不使用调度器。
## 使用方法
ICommandEndpoint 是接口而非具体类,通过 TScriptInterface<ICommandEndpoint> 在多处使用:
- **路由器注册**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h:37` -- RegisterEndpoint 接受 `TScriptInterface<ICommandEndpoint>`,从接口获取端点状态、委托并完成注册
- **路由器实现**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h:14` -- UCommandRouter 实现此接口,作为端点参与上级路由器的路由
- **组件实现**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouterComponent.h:14` -- UCommandRouterComponent 实现此接口,委托给 InternalRouter
- **端点组件实现**: `Plugins/CharacterControl/Source/CharacterControl/Public/EndpointComponent.h:13` -- UEndpointComponent 实现此接口,作为蓝图端点的基类
- **默认 C++ 实现**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:8-26` -- 所有纯虚函数的默认实现,通过调度器桥接蓝图
## 用例
- **Router 实现端点的 ReceiveCommand**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h:22-25` -- UCommandRouter 直接重写 ReceiveCommand、GetEndpointState、GetStateChangedDelegate、GetCommandOutputDelegate不使用调度器桥接
- **EndpointComponent 实现端点的 ReceiveCommand**: `Plugins/CharacterControl/Source/CharacterControl/Public/EndpointComponent.h:30-33` -- UEndpointComponent 直接重写纯虚方法ReceiveCommand 转调 BlueprintImplementableEvent OnCommandReceived
- **端点注册入口**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:45-84` -- RegisterEndpoint 通过 TScriptInterface 获取端点的 EndpointGuid、委托绑定状态变更和命令输出回调执行标签聚合
- **自动发现注册**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:37-41` -- AutoRegisterEndpoints 遍历 Actor 组件,通过 `Implements<UCommandEndpoint>()` 检查并注册所有实现接口的兄弟组件
- **蓝图桥接**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:23-26` -- 蓝图类没有重写 ReceiveCommand 时C++ 默认实现调用 Execute_ReceiveCommand_BP由蓝图元数据系统查找蓝图重写

View File

@@ -0,0 +1,56 @@
# UCommandInputComponent
## 基本信息
- **类型**: UCLASS(BlueprintSpawnableComponent, Blueprintable)
- **父类**: UEndpointComponent
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandInputComponent.h
- **模块**: CharacterControl
## 功能概述
Translates Enhanced Input actions into FCommandPacket commands. BeginPlay adds InputMappingContext to local player subsystem, then binds all InputActions from CommandData. BuildPacket converts Boolean/Axis1D/Axis2D/Axis3D values to FVector3f. Continuous commands use Triggered (every frame), discrete use Started. OnPacketBuilt virtual hook for inspection/modification. Default hop limits: 3 continuous, 5 discrete.
## 设计用意
Bridge between UE Enhanced Input and tag-based routing. Decouples gameplay logic from input bindings - systems subscribe to tags instead. Same input can trigger different responses based on state. Hop limit differentiation reflects frequency differences.
## 职责范围
Input-to-command translation. Binds Enhanced Input actions, converts values to command packets, outputs via delegate. Does NOT route commands.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| EndpointComponent.h | #include (parent) | Plugins/CharacterControl/Source/CharacterControl/Public/EndpointComponent.h |
| UInputCommandData | forward-declare (header) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandInputComponent.h |
| FInputCommand | forward-declare (header) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandInputComponent.h |
| InputCommandData.h | #include (in .cpp) | Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h |
## 对外接口
UCommandInputComponent 继承 UEndpointComponent是 Enhanced Input 到命令路由系统的翻译器。挂载到 APlayerController 上,自动加载 InputMappingContext 并将输入动作绑定为命令。
**配置属性**
- **CommandData** (TObjectPtr<UInputCommandData>, EditAnywhere, BlueprintReadOnly): 指向 UInputCommandData 资产,包含要加载的 InputMappingContext 和 FInputCommand 映射列表
**可重写方法**
- **BuildPacket(const FInputCommand&, const FInputActionInstance&) -> FCommandPacket** (virtual): 根据配置和运行时输入值构建命令包。默认实现Boolean -> ZeroVector/OneVector, Axis1D -> X, Axis2D -> XY, Axis3D -> XYZ。连续命令 HopLimit=3离散命令 HopLimit=5。C++ 子类可重写自定义构造逻辑
- **OnPacketBuilt(const FInputCommand&, FCommandPacket&)** (virtual): 钩子函数,在包构建完成、广播之前调用。子类可在此检查、修改或拦截命令包。默认实现为空
**继承自 UEndpointComponent 的接口**
- OnCommandReceived、OnEndpointStateChanged、OnCommandOutput、EndpointState 等全部可用。同时自动注册到兄弟 UCommandRouterComponent
## 使用方法
将 UCommandInputComponent 添加到 PlayerController配置 CommandData 资产即可:
- **BeginPlay 初始化**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:16-32` -- 获取 PlayerController通过 UEnhancedInputLocalPlayerSubsystem 添加 InputMappingContext调用 BindAllCommands
- **EndPlay 清理**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:34-39` -- 调用 UnbindAllCommands 移除所有输入绑定,再调用 Super::EndPlay
- **BuildPacket**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:41-80` -- 根据动作值类型转换输入值,设置 Meta 和载荷字段
- **BindAllCommands**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:82-116` -- 遍历 CommandData->InputCommands使用 BindActionInstanceLambda 绑定。连续命令用 ETriggerEvent::Triggered离散命令用 Started
- **UnbindAllCommands**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:118-133` -- 遍历 BindingHandles 逐一 RemoveBindingByHandle 后清空数组
## 用例
- **完整输入翻译**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:96-114` -- BindAllCommands 对每个 FInputCommand 绑定 lambdaBuildPacket 构建包 -> OnPacketBuilt 钩子修改 -> OnCommandOutput.ExecuteIfBound 广播。一条输入动作的完整流水线
- **连续命令Triggered**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:99-100` -- bIsContinuous 为 true 时绑定 ETriggerEvent::Triggered每帧触发用于移动、瞄准等模拟量操作
- **离散命令Started**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:101` -- bIsContinuous 为 false 时绑定 ETriggerEvent::Started按一次触发一次用于跳跃、射击等一次性动作
- **输入值类型转换**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:49-67` -- 根据 GetValueType() 将 Boolean/Axis1D/Axis2D/Axis3D 统一转为 FVector3f实现多类型输入的标准化输出
- **InputMappingContext 加载**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:20-29` -- BeginPlay 中通过 UEnhancedInputLocalPlayerSubsystem::AddMappingContext 加载映射上下文

View File

@@ -0,0 +1,63 @@
# UCommandRouter
## 基本信息
- **类型**: UCLASS(BlueprintType, EditInlineNew, DefaultToInstanced)
- **父类**: UObject, implements ICommandEndpoint
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h
- **模块**: CharacterControl
## 功能概述
Core command routing engine. Manages child endpoint registry (TMap<FGuid, FEndpointEntry>). Implements tag-based matching with two modes: continuous (tag + ContinuousFriendly check) and discrete (tag ALL/ANY matching). Tag aggregation system merges child tags upward with ref-counting. Hop-limit guarded dispatch. Upward propagation for child output commands. Self-identity protection prevents circular registration.
## 设计用意
Heart of the plugin. Tag-based publish-subscribe bus replacing hard references. Hierarchical tree topology support (routers inside routers). Hop-limit prevents infinite loops. Tag aggregation enables efficient parent-level filtering. EditInlineNew/DefaultToInstanced for embedding in components.
## 职责范围
Command routing and endpoint management. Registers/unregisters endpoints. Matches and dispatches commands. Aggregates and manages tag subscriptions. Does NOT generate commands (input components do that).
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| CommandEndpoint.h | #include | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
## 对外接口
UCommandRouter 是命令路由系统的核心引擎。作为 UObject(EditInlineNew, DefaultToInstanced) 且实现 ICommandEndpoint它既可以嵌入组件又可以参与上级路由。蓝图和 C++ 均可调用以下 UFUNCTION
**端点管理BlueprintCallable**
- **RegisterEndpoint(TScriptInterface<ICommandEndpoint>) -> bool**: 注册一个端点。检查 GUID 有效性、重复注册、自注册防护。注册成功后绑定端点的两个委托并执行标签聚合。返回 false 表示注册失败
- **UnregisterEndpoint(const FGuid&) -> bool**: 按 GUID 注销端点。解绑委托、移除标签聚合、从注册表删除
- **UnregisterEndpointByInterface(TScriptInterface<ICommandEndpoint>) -> bool**: 按接口指针注销端点,内部提取 GUID 后转调 UnregisterEndpoint
- **ClearAllEndpoints()**: 批量注销所有已注册端点。遍历收集所有端点后逐个调用 UnregisterEndpointByInterface
- **GetEndpointCount() -> int32**: 返回当前已注册端点数量
**路由器自身配置BlueprintCallable**
- **SetRouterState(const FEndpointState&)**: 设置路由器作为端点时的配置。只更新 InterestedTags/bIsActive/bIsContinuousFriendly/bIsAsynchronous 字段,保留 EndpointGuid。更新后广播 StateChangedDelegate
- **GetRouterState() -> FEndpointState**: 返回当前路由器的端点状态
**命令输入BlueprintCallable**
- **InputCommand(const FCommandPacket&)**: 向路由器注入命令,直接调用 ReceiveCommand 触发路由逻辑。可用于外部系统或上级路由器向本路由器投递命令
**委托public field**
- **StateChangedDelegate** (FOnEndpointStateChanged): 路由器自身状态变更时广播
- **CommandOutputDelegate** (FOnCommandOutput): 路由器作为端点向上级输出的命令通过此委托广播
## 使用方法
UCommandRouter 通常不直接使用,而是通过 UCommandRouterComponent 包裹后挂载到 Actor
- **组件嵌入选框**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:15` -- UCommandRouterComponent 通过 CreateDefaultSubobject 创建 InternalRouter
- **端点注册入口**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:45-84` -- RegisterEndpoint 的完整注册流程
- **命令分发**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:13-31` -- ReceiveCommand 入口HopLimit 检查 -> 递减 -> 按类型分发
- **离散路由**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:164-197` -- RouteDiscreteCommand 遍历注册表,提取离散元数据,对每个匹配端点投递
- **连续路由**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:199-218` -- RouteContinuousCommand 遍历注册表,对每个 bIsContinuousFriendly 且 Tag 匹配端点投递
- **端点注册**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:38` -- UEndpointComponent::BeginPlay 找到兄弟 UCommandRouterComponent 后调用 `Router->InternalRouter->RegisterEndpoint(this)`
## 用例
- **命令注入**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:145-162` -- OnRegisteredEndpointOutputCommand 接收子端点产出的命令:上行命令先投递本地再转发上级,非上行命令直接投递本地端点
- **端点注册完整流程**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:45-84` -- 验证 -> 注册 -> 委托绑定BindDynamic -> 标签聚合AddEndpointToAggregation全在 RegisterEndpoint 中完成
- **上行传播**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:148-155` -- bIsUpward 命令处理:拷贝命令清除上行标志后本地投递,同时对原始命令做额外 HopLimit 递减后通过 CommandOutputDelegate 向上转发
- **标签聚合更新**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:137-143` -- OnRegisteredEndpointStateChanged 收到端点状态变更后调用 UpdateEndpointInAggregation 做差分更新
- **自注册防护**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouter.cpp:65-69` -- RegisterEndpoint 检查端点 GUID 是否等于自身 GUID防止路由器将自己注册为子端点造成环路

View File

@@ -0,0 +1,45 @@
# UCommandRouterComponent
## 基本信息
- **类型**: UCLASS(BlueprintSpawnableComponent, Blueprintable)
- **父类**: UActorComponent, implements ICommandEndpoint
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouterComponent.h
- **模块**: CharacterControl
## 功能概述
ActorComponent wrapper around UCommandRouter. Creates InternalRouter as default subobject. Implements ICommandEndpoint by delegating all methods to InternalRouter. BeginPlay calls AutoRegisterEndpoints which discovers ICommandEndpoint siblings and registers them, skipping self and already-registered endpoints.
## 设计用意
Bridge putting a command router on an Actor without C++. Auto-registration implements "local bus" pattern: all endpoint components on same actor auto-wire through this router. Designer adds component -> automatic local command bus.
## 职责范围
Actor-attached router host. Owns and delegates to UCommandRouter. Auto-discovers and registers sibling endpoints. Does NOT implement routing logic (delegated to InternalRouter).
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| CommandEndpoint.h | #include | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
| CommandRouter.h | #include | Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h |
## 对外接口
UCommandRouterComponent 是将 UCommandRouter 挂载到 Actor 上的组件包装器。实现 ICommandEndpoint 接口(全部委托给 InternalRouter使得挂载此组件的 Actor 本身可作为端点参与上级路由。
- **InternalRouter** (TObjectPtr<UCommandRouter>, EditAnywhere, BlueprintReadOnly, Instanced): 暴露给蓝图的设计时属性,可在组件详情面板中查看/配置路由器的端点状态。作为默认子对象通过 CreateDefaultSubobject 创建
- **实现 ICommandEndpoint**: GetEndpointState、GetStateChangedDelegate、GetCommandOutputDelegate、ReceiveCommand 全部直接委托给 InternalRouter 的同名方法。这意味着对 UCommandRouterComponent 调用 ReceiveCommand 等价于向 InternalRouter 注入命令
UCommandRouterComponent 的核心价值在于 BeginPlay 自动调用 AutoRegisterEndpoints()。不需要手动注册端点 -- 只需将 UEndpointComponent 或 UCommandInputComponent 作为兄弟组件添加到同一 Actor 上BeginPlay 时自动发现并注册。
## 使用方法
UCommandRouterComponent 是"即插即用"的本地命令总线:
- **创建默认子对象**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:15` -- 构造函数中 `CreateDefaultSubobject<UCommandRouter>("InternalCommandRouter")`
- **BeginPlay 自动注册**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:20-24` -- BeginPlay 调用 Super::BeginPlay 后立即执行 AutoRegisterEndpoints
- **自动发现**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:27-43` -- 遍历 Owner 的所有 UActorComponent通过 `Implements<UCommandEndpoint>()` 检查,跳过自身和已注册(委托已绑定)的端点,其余全部注册
- **端点自注册**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:22-42` -- UEndpointComponent::BeginPlay 也会主动查找兄弟 UCommandRouterComponent 并注册
- **端点间通信**: 注册后的端点通过路由器互相收发命令。端点 A 调用 OutputCommand -> Router 接收 -> 匹配 -> 投递到端点 B 的 ReceiveCommand
## 用例
- **Actor 本地命令总线**: 在 Character 蓝图上添加 UCommandRouterComponent + UCommandInputComponent + UEndpointComponent蓝图子类BeginPlay 时自动完成连线,输入组件产出的命令自动匹配到端点组件
- **自动注册兄弟端点**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:31-42` -- AutoRegisterEndpoints 遍历所有兄弟组件,调用 Implements<UCommandEndpoint>() 检查接口实现,将未注册的端点通过 InternalRouter->RegisterEndpoint 注册
- **跳过已注册端点**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandRouterComponent.cpp:40` -- 通过 `GetCommandOutputDelegate().IsBound()` 判断端点是否已注册(委托已绑 = 已注册),避免重复注册
- **以路由器作为端点参与上级路由**: 由于 UCommandRouterComponent 实现 ICommandEndpoint 且全部委托给 InternalRouter其他 Actor 上的上级路由器可以通过 TScriptInterface<ICommandEndpoint> 将此组件注册为端点

View File

@@ -0,0 +1,53 @@
# UEndpointComponent
## 基本信息
- **类型**: UCLASS(BlueprintSpawnableComponent, Blueprintable)
- **父类**: UActorComponent, implements ICommandEndpoint
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/EndpointComponent.h
- **模块**: CharacterControl
## 功能概述
Base ActorComponent for Blueprint-authored endpoints. Holds own FEndpointState, delegates. ReceiveCommand calls OnCommandReceived (BlueprintImplementableEvent). BeginPlay auto-registers with sibling UCommandRouterComponent.
## 设计用意
Primary base class for Blueprint endpoints. Subclass in Blueprint, override OnCommandReceived. Auto-wiring with UCommandRouterComponent for default setup. Blueprintable meta explicitly encourages Blueprint subclassing.
## 职责范围
Blueprint-friendly endpoint base. Handles state, delegates, and auto-registration. OnCommandReceived event is the Blueprint extension point.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| CommandEndpoint.h | #include | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
## 对外接口
UEndpointComponent 是蓝图端点的主要基类。BlueprintSpawnableComponent + Blueprintable 允许直接在蓝图中创建子类并重写事件。
**蓝图可重写事件**
- **OnCommandReceived(const FCommandPacket&)** (BlueprintImplementableEvent): 蓝图的主要扩展点。当路由器投递命令到此端点时调用。蓝图子类重写此事件实现具体逻辑移动、技能、UI 等。C++ 侧 ReceiveCommand 直接调用此事件
**委托C++ 访问)**
- **OnEndpointStateChanged** (FOnEndpointStateChanged, BlueprintReadOnly): 状态变更委托。被路由器订阅以触发标签聚合更新
- **OnCommandOutput** (FOnCommandOutput, BlueprintReadOnly): 命令输出委托。端点产出命令时通过此委托广播,由路由器接收并转发
**配置**
- **EndpointState** (FEndpointState, EditAnywhere): 端点的身份和配置。在组件详情面板中设置 InterestedTags、bIsActive、bIsContinuousFriendly、bIsAsynchronous。EndpointGuid 自动生成
**自动注册**BeginPlay 时 UEndpointComponent 主动查找兄弟 UCommandRouterComponent 并通过其 InternalRouter->RegisterEndpoint(this) 注册。只需将端点组件添加到有 UCommandRouterComponent 的 Actor 上即可自动接入本地命令总线。
## 使用方法
蓝图子类化 UEndpointComponent 并配置 EndpointState 是最常见的端点创建方式:
- **构造函数初始化**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:8-14` -- 构造函数中设置 bIsActive=true禁用 Tick
- **ReceiveCommand 桥接**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:16-19` -- ReceiveCommand 直接调用 OnCommandReceived(Command),将 C++ 接口调用桥接到蓝图事件
- **BeginPlay 自动注册**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:22-42` -- 查找 Owner 上的 UCommandRouterComponent通过 `OnCommandOutput.IsBound()` 判断是否已注册,未注册则调用 `Router->InternalRouter->RegisterEndpoint(this)`
- **注册到第一个路由器**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:39` -- `break` 语句确保只注册到第一个找到的 UCommandRouterComponent
## 用例
- **蓝图端点实现**: 创建 Blueprint 类继承 UEndpointComponent在 EndpointState 中配置 InterestedTags如 "Input.Move", "Action.Jump"),在 Event Graph 中重写 OnCommandReceived 实现具体逻辑
- **自动连线**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:22-42` -- BeginPlay 自动找到兄弟 UCommandRouterComponent 并注册。策划只需将组件拖入 Actor 即可,无需手动连线
- **重复注册防护**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:34-37` -- 通过检查 OnCommandOutput.IsBound() 判断是否已注册,避免重复注册导致标签聚合错误
- **只注册到一个路由器**: `Plugins/CharacterControl/Source/CharacterControl/Private/EndpointComponent.cpp:39` -- break 退出循环,一个端点只注册到一个路由器

View File

@@ -0,0 +1,47 @@
# UEndpointDispatcher
## 基本信息
- **类型**: UCLASS(BlueprintType)
- **父类**: UObject
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h
- **模块**: CharacterControl
## 功能概述
Bridge object exposing endpoint delegates to Blueprint. Holds OnStateChanged and OnCommandOutput delegates, plus CurrentState. Provides NotifyStateChanged/OutputCommand wrapper UFUNCTIONs for Blueprint callers.
## 设计用意
Glue between C++ delegates and Blueprint VM. Blueprint endpoint implementations return this via GetEndpointDispatcher_BP. The ICommandEndpoint default C++ implementations all route through this dispatcher.
## 职责范围
Delegate bridge for Blueprint endpoints. Broadcasts state changes and command output to C++ observers. Does NOT implement endpoint logic.
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| (none) | | |
## 对外接口
UEndpointDispatcher 是蓝图端点与 C++ 委托系统之间的桥接对象。它暴露委托引用供 C++ 直接访问,同时提供 UFUNCTION 包装供蓝图调用。
- **OnStateChanged** (FOnEndpointStateChanged, public field): C++ 直接访问的委托。当端点状态变更时广播。被 UCommandRouter 通过 BindDynamic 订阅以触发聚合更新
- **OnCommandOutput** (FOnCommandOutput, public field): C++ 直接访问的委托。当端点输出命令时广播。被 UCommandRouter 订阅以转发端点产出的命令
- **CurrentState** (FEndpointState, EditAnywhere, BlueprintReadWrite): 端点当前状态的副本ICommandEndpoint::GetEndpointState 默认实现返回此字段
- **NotifyStateChanged** (UFUNCTION, BlueprintCallable): 蓝图广播入口,调用后执行 OnStateChanged.ExecuteIfBound(NewState)C++ 订阅者收到通知
- **OutputCommand** (UFUNCTION, BlueprintCallable): 蓝图广播入口,调用后执行 OnCommandOutput.ExecuteIfBound(Command),将命令推送到路由系统
蓝图端点实现 ICommandEndpoint 时,需重写 GetEndpointDispatcher_BP 返回调度器实例。ICommandEndpoint 的 C++ 默认实现全部通过此调度器工作。
## 使用方法
UEndpointDispatcher 是 ICommandEndpoint 蓝图实现的内部机制:
- **C++ 默认桥接**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:8-26` -- ICommandEndpoint 的四个纯虚函数默认实现全部通过 GetEndpointDispatcher_BP 获取调度器后委托执行
- **GetEndpointState 默认实现**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:10` -- 返回 `Execute_GetEndpointDispatcher_BP(...)->CurrentState`
- **ReceiveCommand 默认实现**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:25` -- 调用 `Execute_ReceiveCommand_BP(..., Command)`
- **GetStateChangedDelegate 默认实现**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandEndpoint.cpp:15` -- 返回调度器的 OnStateChanged 引用
- **NotifyStateChanged 内联**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h:140-143` -- 函数体内直接 ExecuteIfBound
- **OutputCommand 内联**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h:146-149` -- 函数体内直接 ExecuteIfBound
## 用例
- **蓝图端点状态通知**: 蓝图端点实现类调用 NotifyStateChanged调度器转发给所有绑定的 C++ 订阅者(如 UCommandRouter::OnRegisteredEndpointStateChanged触发标签聚合更新
- **蓝图端点命令输出**: 蓝图端点调用 OutputCommand 将命令包推入路由系统,调度器广播 OnCommandOutput由 UCommandRouter::OnRegisteredEndpointOutputCommand 接收处理
- **C++ 端点绕过调度器**: `Plugins/CharacterControl/Source/CharacterControl/Public/CommandRouter.h:22-25` -- UCommandRouter 作为 C++ 端点直接重写 GetEndpointState/ReceiveCommand 等方法,不使用调度器;`Plugins/CharacterControl/Source/CharacterControl/Public/EndpointComponent.h:30-33` -- UEndpointComponent 同样直接重写

View File

@@ -0,0 +1,45 @@
# UInputCommandData
## 基本信息
- **类型**: UCLASS(BlueprintType)
- **父类**: UDataAsset
- **源文件**: Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h
- **模块**: CharacterControl
## 功能概述
UDataAsset container for input configuration. Holds InputMappingContext (Enhanced Input mapping context to load) and InputCommands (TArray<FInputCommand>, the action-to-command mappings). Pure data container with no runtime logic.
## 设计用意
Standard UE data asset pattern for designer-friendly configuration. Designer creates asset, fills in mappings, assigns to UCommandInputComponent::CommandData. Decouples configuration from code.
## 职责范围
Configuration data container. Holds mapping context reference and command array. No runtime logic (empty .cpp file).
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FInputCommand | 包含 (TArray成员) | Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h |
| CommandEndpoint.h | #include (for FDiscreteMeta) | Plugins/CharacterControl/Source/CharacterControl/Public/CommandEndpoint.h |
## 对外接口
UInputCommandData 是 UDataAsset 子类,纯数据容器,无运行时逻辑。在 Content Browser 中创建资产,通过资产编辑器配置输入映射。所有属性为 EditAnywhere BlueprintReadOnly设计师可在编辑器中填写。
- **InputMappingContext** (TObjectPtr<UInputMappingContext>, EditAnywhere, BlueprintReadOnly): 指向 Enhanced Input 的 InputMappingContext 资产。UCommandInputComponent::BeginPlay 将此上下文加载到 EnhancedInputLocalPlayerSubsystem
- **InputCommands** (TArray<FInputCommand>, EditAnywhere, BlueprintReadOnly): FInputCommand 数组,每个条目定义一条 InputAction 到命令的映射。包含InputAction 引用、bIsContinuous 开关、ContinousCommandTag连续标签、DiscreteCommandMeta离散元数据
创建资产后将其赋值给 UCommandInputComponent::CommandData 属性即可激活。多个 UCommandInputComponent 可共享同一个 UInputCommandData 资产。
## 使用方法
UInputCommandData 是设计时配置资产,运行时由 UCommandInputComponent 消费:
- **定义**: `Plugins/CharacterControl/Source/CharacterControl/Public/InputCommandData.h:31-40` -- UCLASS(BlueprintType) UDataAsset 子类,两个 UPROPERTY
- **读取 InputMappingContext**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:26-29` -- CommandData->InputMappingContext 传给 Subsystem->AddMappingContext
- **读取 InputCommands**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:96` -- 遍历 `CommandData->InputCommands` 数组
- **消费 FInputCommand 字段**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:41-80` -- BuildPacket 读取每个 FInputCommand 的各字段构建 FCommandPacket
- **空实现**: `Plugins/CharacterControl/Source/CharacterControl/Private/InputCommandData.cpp` -- .cpp 文件只包含 include无任何实现确认为纯数据资产
## 用例
- **设计师配置流程**: 在 Content Browser 中右键创建 UInputCommandData 资产,配置 InputMappingContext 指向已有的 Enhanced Input 映射上下文。在 InputCommands 数组中添加条目:选择 InputAction设置 bIsContinuous填入 GameplayTag配置 DiscreteCommandMeta 的匹配规则。最后将资产赋值给 PlayerController 上的 UCommandInputComponent::CommandData
- **InputMappingContext 加载**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:26-29` -- BeginPlay 从 CommandData 读取 InputMappingContext 并通过 Subsystem->AddMappingContext 加载
- **InputCommands 数组遍历**: `Plugins/CharacterControl/Source/CharacterControl/Private/CommandInputComponent.cpp:96-114` -- BindAllCommands 遍历 CommandData->InputCommands对每条 FInputCommand 执行输入绑定
- **多组件共享资产**: 同一 UInputCommandData 资产可被多个 UCommandInputComponent 引用,实现配置复用

View File

@@ -0,0 +1,32 @@
# CharacterControl 插件依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| CommandEndpoint.h | 定义(自包含) | GameplayTagContainer.h | FGameplayTagContainer |
| CommandEndpoint.h | 定义(自包含) | InstancedStruct.h | FInstancedStruct |
| CommandEndpoint.h | 定义(自包含) | —(自身) | FCommandMeta, FContinuousPayload, FCommandPacket, FDiscreteMeta, FEndpointState, UEndpointDispatcher, ICommandEndpoint, UCommandEndpoint |
| CommandRouter.h | #include | CommandEndpoint.h | ICommandEndpoint, FCommandPacket, FEndpointState |
| CommandRouterComponent.h | #include | CommandEndpoint.h | ICommandEndpoint, FCommandPacket, FEndpointState |
| CommandRouterComponent.h | #include | CommandRouter.h | UCommandRouter |
| CommandRouterComponent.cpp | #include | CommandRouterComponent.h | UCommandRouterComponent |
| EndpointComponent.h | #include | CommandEndpoint.h | ICommandEndpoint, UCommandEndpoint, FEndpointState, FCommandPacket, UEndpointDispatcher |
| CommandInputComponent.h | #include | EndpointComponent.h | UEndpointComponent |
| CommandInputComponent.h | 前向声明 | — | UInputCommandData, UEnhancedInputComponent, FInputCommand |
| CommandInputComponent.cpp | #include | InputCommandData.h | UInputCommandData, FInputCommand |
| CommandInputComponent.cpp | #include | EnhancedInputComponent.h | UEnhancedInputComponent |
| CommandInputComponent.cpp | #include | EnhancedInputSubsystems.h | UEnhancedInputLocalPlayerSubsystem |
| CommandInputComponent.cpp | #include | InputAction.h | UInputAction |
| InputCommandData.h | #include | CommandEndpoint.h | FCommandMeta, FContinuousPayload, FDiscreteMeta |
| InputCommandData.h | 前向声明 | — | UInputAction |
## 关键依赖链
1. Input to Command: Enhanced Input → UCommandInputComponent::BuildPacket → FInputCommand (from UInputCommandData) → FCommandPacket → OnCommandOutput delegate
2. Command Routing: FCommandPacket → UCommandRouter::ReceiveCommand (hop check) → RouteDiscreteCommand/RouteContinuousCommand → IsEndpointMatched → child ICommandEndpoint::ReceiveCommand
3. Upward Propagation: child endpoint outputs → UCommandRouter::OnRegisteredEndpointOutputCommand → if bIsUpward → re-inject locally + forward upward
4. State Change Propagation: child endpoint state changes → UCommandRouter::OnRegisteredEndpointStateChanged → tag aggregation update → CommitTagsToInterest
5. Auto-Wiring: UCommandRouterComponent::BeginPlay → AutoRegisterEndpoints → discovers sibling ICommandEndpoint on owner Actor → RegisterEndpoint
6. Endpoint Auto-Registration: UEndpointComponent::BeginPlay → finds sibling UCommandRouterComponent → self-registers with InternalRouter
7. Blueprint Endpoint Pattern: ICommandEndpoint (BP implementation) → GetEndpointDispatcher_BP → UEndpointDispatcher → delegates broadcast to C++ observers

View File

@@ -0,0 +1,72 @@
# EDialogPresentationScriptCommandParamType
## 基本信息
- **类型**: UENUM(BlueprintType)
- **父类**: uint8
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h
- **模块**: Dialog
## 功能概述
对话脚本命令参数类型的枚举None、Number、String、Boolean、Array、Object。在 FDialogPresentationScriptParam 中作为类型标签使用,实现带标签的联合体语义。
## 设计用意
由于 USTRUCT 无法持有变体值,因此需要显式的类型鉴别机制。运行时代码通过 switch 该枚举值来读取正确的字段。Array 和 Object 类型以序列化的 JSON 字符串形式存储,以兼容蓝图。
## 职责范围
类型标签枚举。被 FDialogPresentationScriptParam 和 UPresentationJsonLibrary 引用,用于 JSON 解析和分发。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
(无项目内依赖)
## 对外接口
BlueprintType 枚举,作为 `FDialogPresentationScriptParam``Type` 字段的类型标签。消费者通过 switch 该枚举来决定读取哪个值字段。
枚举值:
- **None**: 无类型/空值。构造函数默认值JSON 解析失败时也为 None。
- **Number**: 对应 JSON Number 类型,值存储在 `FDialogPresentationScriptParam::NumberValue` (float)。
- **String**: 对应 JSON String 类型,值存储在 `FDialogPresentationScriptParam::StringValue`
- **Boolean**: 对应 JSON Boolean 类型,值存储在 `FDialogPresentationScriptParam::BooleanValue`
- **Array**: 对应 JSON Array 类型,整个数组序列化为 JSON 字符串存储在 `FDialogPresentationScriptParam::ArrayValue`
- **Object**: 对应 JSON Object 类型,整个对象序列化为 JSON 字符串存储在 `FDialogPresentationScriptParam::ObjectValue`
注意Array 和 Object 以序列化 JSON 字符串形式存储,而非嵌套结构体。消费者需要在蓝图或 C++ 中自行反序列化。
## 使用方法
在 JSON 解析中按值类型分发:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp:163-196
switch (JsonValue->Type)
{
case EJson::String:
Param.Type = EDialogPresentationScriptCommandParamType::String;
Param.StringValue = JsonValue->AsString();
break;
case EJson::Number:
Param.Type = EDialogPresentationScriptCommandParamType::Number;
Param.NumberValue = static_cast<float>(JsonValue->AsNumber());
break;
// ... Boolean, Array, Object cases ...
}
```
在 JSON 序列化中按类型标签写入:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp:353-389
switch (Param.Type)
{
case EDialogPresentationScriptCommandParamType::String:
return FString::Printf(TEXT("\"%s\""), *lambda(Param.StringValue));
case EDialogPresentationScriptCommandParamType::Number:
return FString::SanitizeFloat(Param.NumberValue);
// ... Boolean, Array, Object, default cases ...
}
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h | 43-51,61,69 | 枚举定义FDialogPresentationScriptParam 构造函数默认值 (None);字段声明 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 155-195 | JsonValueToParam 中按 JSON 类型分发到对应枚举值 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 355-389 | ParamToJson 中按枚举值序列化到 JSON 字符串 |

View File

@@ -0,0 +1,68 @@
# FDialogPresentationScriptCommand
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (无)
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h
- **模块**: Dialog
## 功能概述
对话表现脚本中的单条可执行命令。包含 CommandName例如 "SetSpeaker"、"PlayAnimation")和一个有序的 FDialogPresentationScriptParam 数组。CommandName 作为策略路由的分发键。
## 设计用意
命令抽象用于策略模式的分发。CommandName 是路由键——每个 IPresentationStrategyPlugin 检查自己是否能处理该名称。Params 数组是通用的;每个策略知道自己期望的参数格式。将数据(做什么)与执行器(怎么做)分离。
## 职责范围
单条命令的数据容器。传递给 IPresentationStrategyPlugin::PerformCommand。自身不执行命令。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogPresentationScriptParam | 包含TArray成员 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
## 对外接口
BlueprintType 结构体,代表对话脚本中的单条可执行命令。外部代码(策略插件)通过 `IPresentationStrategyPlugin::PerformCommand` 接口接收此结构体。
关键字段:
- **CommandName** (FName): 命令名称作为策略路由的分发键。IPresentationScriptExecutor 根据此名称将命令分发给对应的 IPresentationStrategyPlugin。
- **Params** (TArray<FDialogPresentationScriptParam>): 命令参数数组。各策略插件知道如何解释自身命令的参数格式和类型。
典型流程:
1. `UPresentationJsonLibrary``FDialogPresentationScriptData::PresentationScript` JSON 中解析出命令数组。
2. 执行器遍历命令,根据 `CommandName` 找到对应策略。
3. 策略插件的 `PerformCommand` 接收该命令,按 Type 逐个解析 Params 中的参数。
## 使用方法
在编辑器对话图节点中编辑命令:
```cpp
// Plugins/Dialog/Source/DialogEditor/Public/DialogGraphNode.h:31
TArray<FDialogPresentationScriptCommand> PresentationScriptCommands;
```
在编辑器中通过 UPresentationJsonLibrary 将命令数组序列化为 JSON
```cpp
// Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp:478
auto ScriptString = UPresentationJsonLibrary::PresentationScriptToJson(DialogNode->PresentationScriptCommands);
```
在 UPresentationJsonLibrary 内部将命令序列化为 JSON
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp:392-402
FString Result = TEXT("[");
Result += FString::Printf(TEXT("\"%s\""), *Command.CommandName.ToString());
for (const FDialogPresentationScriptParam& Param : Command.Params)
{
Result += TEXT(",") + ParamToJson(Param);
}
Result += TEXT("]");
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h | 88-98 | 结构体定义 |
| Plugins/Dialog/Source/DialogEditor/Public/DialogGraphNode.h | 31 | 编辑器对话图节点中存储可编辑的命令列表 |
| Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp | 478 | 编辑器导出时将节点命令序列化为 JSON |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 279-290 | PresentationScriptToJson 遍历命令数组并序列化 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 392-402 | CommandToJson 将单条命令序列化为 JSON |
| Plugins/Dialog/Source/Dialog/Public/PresentationStrategyPlugin.h | 26 | IPresentationStrategyPlugin::PerformCommand 接收此类型参数 |

View File

@@ -0,0 +1,57 @@
# FDialogPresentationScriptData
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: FTableRowBase
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h
- **模块**: Dialog
## 功能概述
DataTable 行结构体,用于存储单个对话脚本条目。包含 ChoicesJSON 数组存储选项标识符、PresentationScriptJSON 数组存储命令序列、Comment设计者注释和 ScriptID运行时填充的标识符。命令以 JSON 编码的 [CommandName, param1, param2, ...] 数组形式存储。
## 设计用意
将对话数据与执行逻辑解耦。JSON 格式使数据紧凑、便于外部工具编辑,且支持任意长度的参数列表而无需修改结构体定义。继承 FTableRowBase 使其能够存储在 UDataTable 中。
## 职责范围
单个对话节点的脚本数据容器。ScriptID 由 UDialogPresentationSubsystem 在查找时注入。结构体自身不解析或执行其 JSON 数据(解析工作由 UPresentationJsonLibrary 负责)。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
(无项目内依赖)
## 对外接口
作为 DataTable 行结构体,供外部代码通过 `UDataTable::FindRow<FDialogPresentationScriptData>()` 读取。
关键字段:
- **Choices** (FString): JSON 数组字符串,存储对话选项的标识符名称列表,如 `["Choice_A","Choice_B"]`
- **PresentationScript** (FString): JSON 命令数组字符串,每个命令为 `[CommandName, param1, param2, ...]` 格式的 JSON 数组。
- **Comment** (FText): 设计者注释,可通过 `UPresentationJsonLibrary::GetPresentationScriptComment` 读取。
- **ScriptID** (FName): 运行时由 `UDialogPresentationSubsystem::GetDialogPresentationScriptData` 自动填充,值为传入的行查找键名 (StructID)。
外部代码不应直接解析 Choices 和 PresentationScript 的 JSON 字符串,而应通过 `UPresentationJsonLibrary` 的静态方法(如 `GetPresentationScriptChoicesNum``GetPresentationScriptCommandName` 等)来访问其中数据。
## 使用方法
在 C++ 中通过子系统查找行数据:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:30
FDialogPresentationScriptData* ScriptData = Table->FindRow<FDialogPresentationScriptData>(StructID, TEXT("DialogPresentationSubsystem"));
```
在编辑器工具中构造行数据并写入 DataTable
```cpp
// Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp:481-487
FDialogPresentationScriptData RowData;
FName RowName = DialogNode->NodeID;
RowData.Choices = Result;
RowData.Comment = CommentText;
RowData.PresentationScript = ScriptString;
NewTable->AddRow(RowName, RowData);
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 30 | 从已注册的 DataTable 中按 StructID 查找并返回脚本行数据 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 6,11,24,50,63,98,201,246 | UPresentationJsonLibrary 解析 Choices/PresentationScript JSON 字段 |
| Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp | 481-487,489 | 编辑器从对话图节点导出 DataTable 时构造行数据 |

View File

@@ -0,0 +1,63 @@
# FDialogPresentationScriptParam
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (无)
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h
- **模块**: Dialog
## 功能概述
用于单个命令参数的带标签联合体值类型。包含一个 Type 枚举和多个值字段String、Numberfloat、Boolean、ArrayJSON 字符串、ObjectJSON 字符串)。显式构造函数初始化为 None/0.0/false。
## 设计用意
采用带标签的联合体模式来处理异构命令参数。使得单一灵活的命令系统成为可能,各策略根据类型解释参数。对蓝图可用。
## 职责范围
单个命令参数的值容器。消费者通过 switch Type 来读取正确的值字段。不解释或执行参数值。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| EDialogPresentationScriptCommandParamType | 成员类型 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
## 对外接口
BlueprintType 结构体,作为对话脚本命令参数的带标签联合体值类型。
关键字段:
- **Type** (EDialogPresentationScriptCommandParamType): 类型标签,指示哪个值字段有效。消费者应先检查此字段,然后读取对应的值字段。
- **StringValue** (FString): 字符串值,当 Type == String 时有效。
- **NumberValue** (float): 数值,当 Type == Number 时有效。
- **BooleanValue** (bool): 布尔值,当 Type == Boolean 时有效。
- **ArrayValue** (FString): JSON 数组的序列化字符串,当 Type == Array 时有效。需自行解析为 TArray。
- **ObjectValue** (FString): JSON 对象的序列化字符串,当 Type == Object 时有效。需自行解析为 TSharedPtr<FJsonObject>。
构造函数行为:
- Type 默认为 `EDialogPresentationScriptCommandParamType::None`
- NumberValue 默认为 `0.0f`
- BooleanValue 默认为 `false`
- StringValue、ArrayValue、ObjectValue 默认为空字符串FString 默认构造)
## 使用方法
从 FDialogPresentationScriptData 获取单个参数:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp:201-225
FDialogPresentationScriptParam Param = UPresentationJsonLibrary::GetPresentationScriptCommandParaAt(Data, CommandIndex, ParaIndex);
```
从字符串构建参数对象:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp:228-243
FDialogPresentationScriptParam Param = UPresentationJsonLibrary::BuildPresentationScriptCommandParaFromStr(SourceString);
```
消费者按 Type 分发读取正确的值字段,参见 `ParamToJson` 中的 switch 分发模式DialogPresentationScript.cpp:353-389
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h | 54-85 | 结构体定义,包含所有字段和构造函数 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 150-198 | JsonValueToParam 解析 JSON 值并填充字段 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 201-225 | GetPresentationScriptCommandParaAt 获取指定命令的指定参数 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 228-243 | BuildPresentationScriptCommandParaFromStr 从字符串构建参数 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 246-276 | GetPresentationScriptCommandParams 获取某命令的所有参数数组 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 351-389 | ParamToJson 按 Type 序列化为 JSON |

View File

@@ -0,0 +1,70 @@
# FDialogTextData
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: FTableRowBase
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h
- **模块**: Dialog
## 功能概述
DataTable 行结构体,用于存储本地化对话文本。包含 bIsChoice 标志、Source/Target 标识符、DialogText 正文和 ChoiceText 标签。当 bIsChoice 为 true 时表示一个玩家选项。Source 和 Target 组成复合键,用于子系统索引构建。
## 设计用意
将对话内容说了什么与表现逻辑如何展示分离。DataTable 存储方式支持本地化和独立编辑。同一结构体通过 bIsChoice 标志同时服务于对话行和选项两种用途。复合键Source|Target用于对相关行进行分组。
## 职责范围
单条对话文本的数据容器。由 UDialogPresentationSubsystem 索引到 DialogTextIndex按 Source 索引)或 ChoiceTextIndex按 Source|Target 索引)中。不执行任何查找操作。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
(无项目内依赖)
## 对外接口
作为 DataTable 行结构体,供外部代码通过 `UDataTable::FindRow<FDialogTextData>()` 读取。
关键字段:
- **bIsChoice** (bool): 标识该行是普通对话文本 (false) 还是玩家选项文本 (true)。该标志决定行数据在 `BuildIndexes()` 中被索引到 `DialogTextIndex` 还是 `ChoiceTextIndex`
- **Source** (FName): 源节点 ID即对话所属节点的标识符。在 `DialogTextIndex` 中作为单键,在 `ChoiceTextIndex` 中与 Target 组成复合键。
- **DialogText** (FText): 对话正文。仅当 `bIsChoice == false` 时有意义。可通过 `UPresentationJsonLibrary::GetDialogText``UDialogPresentationSubsystem::GetDialogTextFromMapTable` 读取。
- **Target** (FName): 目标节点 ID即该选项跳转到的节点。仅当 `bIsChoice == true` 时有意义。
- **ChoiceText** (FText): 选项显示文本。仅当 `bIsChoice == true` 时有意义。可通过 `UPresentationJsonLibrary::GetChoicesText``UDialogPresentationSubsystem::GetChoiceTextFromMapTable` 读取。
外部代码建议通过 `UDialogPresentationSubsystem` 的查询方法(`GetDialogTextFromMapTable` / `GetChoiceTextFromMapTable`)来获取文本,而不是直接遍历 DataTable。
## 使用方法
在编辑器中构造对话文本行并写入 DataTable
```cpp
// Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp:500-505
FDialogTextData DialogTextRowData;
FName DialogTextRowName = FName(*FString::Printf(TEXT("%s_%d"), *DialogNode->NodeID.ToString(), RowNameSuffix++));
DialogTextRowData.bIsChoice = false;
DialogTextRowData.Source = DialogNode->NodeID;
DialogTextRowData.DialogText = DialogNode->DialogText;
NewTable->AddRow(DialogTextRowName, DialogTextRowData);
```
编辑器中构造选项文本行:
```cpp
// Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp:523-529
FDialogTextData ChoiceTextRowData;
ChoiceTextRowData.bIsChoice = true;
ChoiceTextRowData.Source = DialogNode->NodeID;
ChoiceTextRowData.ChoiceText = ChoiceIndex >= 0 && ChoiceIndex < DialogNode->ChoicePinValue.Num() ? DialogNode->ChoicePinValue[ChoiceIndex++] : FText::GetEmpty();
ChoiceTextRowData.Target = LinkedNode->NodeID;
NewTable->AddRow(ChoiceTextRowName, ChoiceTextRowData);
```
运行时通过子系统查询:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:76
FDialogTextData* Result = DialogTextIndex.Find(Source);
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 71,76,84,90,109-122 | 在 DialogTextIndex/ChoiceTextIndex 中索引和查找文本行 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 135-148 | UPresentationJsonLibrary 的 GetDialogText/GetChoicesText 直接读取字段 |
| Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp | 500-533 | 编辑器从对话图节点导出 MapTable 时构造文本行数据 |
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h | 19,50,53,64 | FHelperArray 的 Data 成员类型;子系统查询方法返回类型 |

View File

@@ -0,0 +1,54 @@
# FHelperArray
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (无)
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h
- **模块**: Dialog
## 功能概述
简单的包装结构体,持有 TArray<FDialogTextData>。仅因 Unreal 的 TMap 无法将裸 TArray 作为 UPROPERTY 值类型而存在。用作 ChoiceTextIndex 中的 Map 值类型。
## 设计用意
UE 引擎限制的变通方案。将数组包装在 USTRUCT 中使其成为有效的 TMap 值类型,以满足 GC 追踪要求。无其他用途。
## 职责范围
技术性包装结构体。仅在 UDialogPresentationSubsystem 的 ChoiceTextIndex Map 中使用。单一 UPROPERTY 成员TArray<FDialogTextData> Data。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogTextData | 包含TArray成员 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
## 对外接口
BlueprintType 结构体,纯粹的技术性包装器,唯一用途是使 `TArray<FDialogTextData>` 能够作为 `TMap` 的值类型。
唯一字段:
- **Data** (TArray<FDialogTextData>): 被包装的 FDialogTextData 数组。
外部代码通过 `UDialogPresentationSubsystem::GetChoiceTextFromMapTable` 获取 `TArray<FDialogTextData>`,通常不会直接操作此结构体。该结构体仅在 `ChoiceTextIndex` 内部使用,作为 `TMap<FName, FHelperArray>` 的值类型。
## 使用方法
子系统内部索引构建时查找或创建键值对:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:120-121
FName Key = FName(*FString::Printf(TEXT("%s|%s"), *Row->Source.ToString(), *Row->Target.ToString()));
ChoiceTextIndex.FindOrAdd(Key).Data.Add(*Row);
```
子系统内部查询时解包数组:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:90-92
if (FHelperArray* Result = ChoiceTextIndex.Find(Key))
{
return Result->Data; // O(1) 返回
}
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h | 15-20 | 结构体定义,位于子系统头文件中 |
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h | 66 | ChoiceTextIndex 的 Map 值类型:`TMap<FName, FHelperArray>` |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 90-92 | GetChoiceTextFromMapTable 中解包返回 Data 数组 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 121 | BuildIndexes 中通过 FindOrAdd 追加选项行到对应复合键的数组 |

View File

@@ -0,0 +1,64 @@
# IPresentationScriptExecutor
## 基本信息
- **类型**: UINTERFACE + C++ 接口
- **父类**: UInterface
- **源文件**: Plugins/Dialog/Source/Dialog/Public/PresentationScriptExecutor.h
- **模块**: Dialog
## 功能概述
对话表现系统的协调器接口。方法ExecutePresentationScript运行脚本、PlugStrategyIn/UnplugStrategy按名称管理策略注册表。所有方法均为 BlueprintNativeEvent。
## 设计用意
门面/协调器模式。将"执行什么"FDialogPresentationScriptData与"谁执行"(策略插件)分离。作为接口与框架无关——可与任何 UI 系统配合使用。UDialogPresentationSubsystem 在脚本查找后调用 ExecutePresentationScript。
## 职责范围
编排对话脚本执行。管理策略插件注册表。由 UDialogPresentationSubsystem::ShowDialog 调用。不存储对话数据,也不解析 JSON。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogPresentationScriptData | 前向声明(参数) | Plugins/Dialog/Source/Dialog/Public/PresentationScriptExecutor.h:26 |
| IPresentationStrategyPlugin | 前向声明(参数) | Plugins/Dialog/Source/Dialog/Public/PresentationScriptExecutor.h:28 |
## 对外接口
UInterface / C++ 接口,供蓝图或 C++ 类实现。作为对话表现系统的协调器/门面,负责编排脚本执行和管理策略插件注册表。所有方法均为 BlueprintNativeEvent可蓝图重写
三个方法:
- **ExecutePresentationScript** (BlueprintNativeEvent, BlueprintCallable): 接收 `const FDialogPresentationScriptData& ScriptStruct`(无返回值)。
-`UDialogPresentationSubsystem::ShowDialog` 调用。
- 实现者应解析 ScriptStruct 中的脚本数据,按顺序执行命令,将每条命令通过 CommandName 路由到已注册的 IPresentationStrategyPlugin。
- **PlugStrategyIn** (BlueprintNativeEvent, BlueprintCallable): 接收 `FName StrategyName``const TScriptInterface<IPresentationStrategyPlugin>& NewStrategy`
- 按名称注册一个策略插件。后续执行脚本时根据 CommandName 查找对应策略并调用其 PerformCommand。
- **UnplugStrategy** (BlueprintNativeEvent, BlueprintCallable): 接收 `FName StrategyName`
- 按名称注销一个策略插件。
典型实现者UI Widget如 WBP_TestUI它既是显示层也是执行协调器。
## 使用方法
在蓝图中实现此接口。典型调用链:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:8-17
void UDialogPresentationSubsystem::ShowDialog(const TScriptInterface<IPresentationScriptExecutor> Executor, const FName StructID)
{
UObject* ExecutorObject = Executor.GetObject();
if (!ExecutorObject)
{
UE_LOG(LogTemp, Warning, TEXT("ShowDialog failed: Executor does not implement IPresentationScriptExecutor interface."));
return;
}
IPresentationScriptExecutor::Execute_ExecutePresentationScript(ExecutorObject, GetDialogPresentationScriptData(StructID));
}
```
子系统查找脚本数据、注入 ScriptID 后,通过 `Execute_ExecutePresentationScript` 静态方法调用蓝图实现的接口函数。
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/PresentationScriptExecutor.h | 19-31 | 接口定义UInterface 包装类 + C++ 接口类含三个方法声明 |
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h | 32 | UDialogPresentationSubsystem::ShowDialog 以此接口的 TScriptInterface 为参数 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 8-17 | ShowDialog 实现:先校验接口,再调用 Execute_ExecutePresentationScript |
| Plugins/Dialog/Source/Dialog/Private/PresentationScriptExecutor.cpp | 6 | 接口默认实现占位文件 |
| Document/Content/Blueprints/WBP_TestUI.md | 10 | WBP_TestUI蓝图 Widget实现此接口接收并执行脚本

View File

@@ -0,0 +1,56 @@
# IPresentationStrategyPlugin
## 基本信息
- **类型**: UINTERFACE + C++ 接口
- **父类**: UInterface
- **源文件**: Plugins/Dialog/Source/Dialog/Public/PresentationStrategyPlugin.h
- **模块**: Dialog
## 功能概述
用于处理对话表现命令的策略接口。单一方法 PerformCommand 接收 FDialogPresentationScriptCommand返回 booltrue 表示已处理。BlueprintNativeEvent支持蓝图实现。
## 设计用意
策略模式,支持多个独立的对话表现插件。一个策略处理 "SetSpeaker",另一个处理 "PlayAnimation"。布尔返回值支持回退路由。可扩展——新的表现能力只需添加新策略,无需修改核心代码。
## 职责范围
处理特定命名命令。按名称注册到 IPresentationScriptExecutor 中。不管理策略注册表,也不编排执行流程。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogPresentationScriptCommand | 参数类型 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
## 对外接口
UInterface / C++ 接口,供蓝图或 C++ 类实现,用于处理特定命名类别的对话脚本命令。
单一方法:
- **PerformCommand** (BlueprintNativeEvent, BlueprintCallable): 接收 `const FDialogPresentationScriptCommand& InCommand`,返回 `bool`
- 返回 `true` 表示该策略识别并成功处理了此命令。
- 返回 `false` 表示未处理(可用于回退路由链)。
设计模式:策略模式。每个策略实现负责一个命令名称空间(如 "SetSpeaker"、"PlayAnimation")。策略通过 `IPresentationScriptExecutor::PlugStrategyIn` 按名称注册到执行器。执行器根据 `CommandName` 将命令路由到对应策略。
注意:策略自身不管理注册,不决定何时被调用。注册和调度由实现 `IPresentationScriptExecutor` 的类负责。
## 使用方法
在 C++ 或蓝图中实现此接口。蓝图中可 override `PerformCommand` 事件:
1. 检查 `InCommand.CommandName` 是否为自己能处理的命令。
2. 读取 `InCommand.Params`,按 Type 字段分发读取正确的值。
3. 执行相应的游戏逻辑(设置角色、播放动画等)。
4. 返回 `true` 表示已处理,或 `false` 表示不处理。
策略类通过执行器的 `PlugStrategyIn(StrategyName, StrategyObject)` 注册自身:
```cpp
// Plugins/Dialog/Source/Dialog/Public/PresentationScriptExecutor.h:28
void PlugStrategyIn(FName StrategyName, const TScriptInterface<class IPresentationStrategyPlugin>& NewStrategy);
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/PresentationStrategyPlugin.h | 19-27 | 接口定义UInterface 包装类 + C++ 接口类含 PerformCommand 声明 |
| Plugins/Dialog/Source/Dialog/Public/PresentationScriptExecutor.h | 28 | IPresentationScriptExecutor::PlugStrategyIn 以此接口的 TScriptInterface 为参数 |
| Document/Content/Blueprints/WBP_TestUI.md | 10 | WBP_TestUI 实现 IPresentationScriptExecutor负责将命令分发给对应的策略插件 |
注:`PerformCommand` 在 Dialog 插件源码中仅有声明PresentationStrategyPlugin.h:26无 C++ 调用实现;调用发生在实现 `IPresentationScriptExecutor` 的蓝图/运行时中。搜索 `PerformCommand` 仅匹配到接口声明行。

View File

@@ -0,0 +1,85 @@
# UDialogPresentationSubsystem
## 基本信息
- **类型**: UCLASS
- **父类**: UGameInstanceSubsystem
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h
- **模块**: Dialog
## 功能概述
GameInstance 级别的对话数据中心注册表。管理脚本数据FDialogPresentationScriptData和映射表FDialogTextData的 DataTable 注册。构建 O(1) 文本查找索引。ShowDialog 将所有环节串联起来:通过 StructID 查找脚本、注入 ScriptID、调用执行器的 ExecutePresentationScript。
## 设计用意
对话系统的数据访问层。使用 UGameInstanceSubsystem 以获得持久生命周期。支持多个 DataTable按章节、按 NPC 等)。通过预构建的哈希映射实现 O(1) 文本查找,满足频繁的游戏查询需求。将数据与执行解耦。
## 职责范围
对话数据中心注册表与查找。注册/注销 DataTable。构建文本索引。通过 StructID 解析脚本。不执行脚本(委托给执行器),也不解析 JSON使用 UPresentationJsonLibrary
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogTextData | 前向声明 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h:11 |
| IPresentationScriptExecutor | 前向声明(参数) | Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h:32 |
| FDialogPresentationScriptData | #include(在 .cpp 中) | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
## 对外接口
UGameInstanceSubsystem 派生类,所有方法均为 UFUNCTION(BlueprintCallable),可从 C++ 和蓝图调用。UE 引擎自动创建实例(每个 GameInstance 一个),无需手动实例化。
脚本执行:
- **ShowDialog(const TScriptInterface<IPresentationScriptExecutor> Executor, const FName StructID)**: 核心入口。在所有已注册的 DataTable 中按 StructID 查找 FDialogPresentationScriptData注入 ScriptID然后调用 Executor 的 ExecutePresentationScript 接口方法。
数据查询:
- **GetDialogPresentationScriptData(const FName StructID)**: 遍历 RegisteredDataTables返回第一个匹配 StructID 的行。返回时会注入 ScriptID 字段。
- **GetDialogTextFromMapTable(FName Source)**: 从 DialogTextIndex 中按 Source 查找对话文本O(1) 复杂度。首次调用时若索引未构建则自动触发 BuildIndexes()。
- **GetChoiceTextFromMapTable(FName Source, FName Target)**: 从 ChoiceTextIndex 中按复合键 `Source|Target` 查找选项文本数组O(1) 复杂度。首次调用时若索引未构建则自动触发 BuildIndexes()。
DataTable 管理:
- **RegisterDataTable(const FName TableName, UDataTable* DataTable)**: 注册脚本 DataTable。TableName 仅供标识,查找时遍历所有已注册表。
- **UnregisterDataTable(const FName TableName)**: 注销脚本 DataTable。
- **RegisterMapTable(const FName TableName, UDataTable* DataTable)**: 注册文本映射 DataTableFDialogTextData。注册时自动调用 BuildIndexes() 重建索引。
- **UnregisterMapTable(const FName TableName)**: 注销文本映射 DataTable 并重建索引。
内部状态(对外不可见):
- RegisteredDataTables (TMap<FName, UDataTable*>): 已注册的脚本 DataTable。
- RegisteredMapTables (TMap<FName, UDataTable*>): 已注册的文本映射 DataTable。
- DialogTextIndex (TMap<FName, FDialogTextData>): 按 Source 索引的对话文本映射。
- ChoiceTextIndex (TMap<FName, FHelperArray>): 按 `Source|Target` 复合键索引的选项文本映射。
## 使用方法
通过 GameInstance 获取子系统实例:
```cpp
UGameInstance* GI = GetGameInstance();
UDialogPresentationSubsystem* DialogSubsystem = GI->GetSubsystem<UDialogPresentationSubsystem>();
```
调用 ShowDialog 触发脚本执行:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:8-17
DialogSubsystem->ShowDialog(ExecutorWidget, FName("StartNode"));
```
注册 DataTable 和 MapTable
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:43,56
DialogSubsystem->RegisterDataTable(FName("Chapter1"), ScriptTable);
DialogSubsystem->RegisterMapTable(FName("Chapter1"), MapTable);
```
查询对话文本:
```cpp
// Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp:71-81
FDialogTextData Text = DialogSubsystem->GetDialogTextFromMapTable(FName("Node_01"));
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationSubsystem.h | 26-70 | 类定义:所有公共方法声明和私有成员 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 8-17 | ShowDialog 实现:校验接口 + 调用 Execute_ExecutePresentationScript |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 20-41 | GetDialogPresentationScriptData 实现:遍历已注册表查找行 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 43-53 | RegisterDataTable / UnregisterDataTable 实现 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 56-68 | RegisterMapTable / UnregisterMapTable 实现(含自动索引重建) |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 71-81 | GetDialogTextFromMapTable 实现O(1) 查找 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 84-96 | GetChoiceTextFromMapTable 实现:按复合键 O(1) 查找 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationSubsystem.cpp | 98-127 | BuildIndexes 实现:遍历 MapTable 构建 DialogTextIndex 和 ChoiceTextIndex |
| Document/Content/Blueprints/WBP_TestUI.md | 16,24 | WBP_TestUI 通过 UDialogPresentationSubsystem 管理 DataTable |

View File

@@ -0,0 +1,70 @@
# UPresentationJsonLibrary
## 基本信息
- **类型**: UCLASSBlueprintFunctionLibrary
- **父类**: UBlueprintFunctionLibrary
- **源文件**: Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h
- **模块**: Dialog
## 功能概述
用于对话脚本 JSON 解析和序列化的静态函数库。解析 Choices JSON 数组和 PresentationScript JSON 命令数组。提供获取命令数量、指定索引处的命令名称、参数数量、指定索引处的参数的函数。同时支持将命令序列化回 JSON。所有方法均为静态 UFUNCTION。
## 设计用意
蓝图与 JSON 对话数据之间的桥梁。FDialogPresentationScriptData 将命令存储为原始 JSON 字符串本库负责解析它们。BlueprintFunctionLibrary 使所有方法无需实例即可从蓝图中调用。私有辅助函数处理带正确转义的递归 JSON 值序列化。
## 职责范围
对话脚本的 JSON 与结构体之间的转换。将 FDialogPresentationScriptData 字段解析为类型化的访问接口。将 TArray<FDialogPresentationScriptCommand> 序列化回 JSON。不执行命令。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FDialogPresentationScriptData | 参数类型 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
| FDialogPresentationScriptCommand | 参数/返回类型 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
| FDialogPresentationScriptParam | 内部创建/解析 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
| FDialogTextData | 参数类型 | Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h |
## 对外接口
BlueprintFunctionLibrary所有方法均为静态 UFUNCTION(BlueprintCallable),可从 C++ 和蓝图调用。提供两大类功能:脚本数据解析和文本数据访问。
脚本数据解析(参数均为 FDialogPresentationScriptData
- **GetPresentationScriptComment**: 返回 Data.Comment设计者注释
- **GetPresentationScriptChoicesNum**: 解析 Data.Choices JSON 数组,返回选项数量;解析失败返回 -1。
- **GetPresentationScriptChoiceAt**: 解析 Data.Choices返回指定索引处的选项名称 (FName);越界或失败返回空 FName。
- **GetPresentationScriptCommandsNum**: 解析 Data.Choices JSON 数组,返回元素数量(注意:此函数存在已知 bug应解析 Data.PresentationScript 而非 Data.Choices参见 `DialogPresentationScript.cpp:53`)。
- **GetPresentationScriptCommandName**: 解析 Data.PresentationScript返回第 Index 条命令的名称 (FName)。
- **GetPresentationScriptCommandParaNum**: 解析 Data.PresentationScript返回第 Index 条命令的参数数量(总元素数减 1
- **GetPresentationScriptCommandParaAt**: 解析 Data.PresentationScript返回第 CommandIndex 条命令的第 ParaIndex 个参数 (FDialogPresentationScriptParam)。
- **BuildPresentationScriptCommandParaFromStr**: 从单字符串解析 JSON 值并构建 FDialogPresentationScriptParam。
- **GetPresentationScriptCommandParams**: 解析 Data.PresentationScript返回第 CommandIndex 条命令的所有参数 (TArray<FDialogPresentationScriptParam>)。
- **PresentationScriptToJson**: 将命令数组反向序列化为 JSON 字符串(编辑器导出用)。
文本数据访问:
- **GetDialogText**: 返回 FDialogTextData 的 DialogText 字段。
- **GetChoicesText**: 遍历 TArray<FDialogTextData>,提取每个元素的 ChoiceText 字段组成 TArray<FText>。
已知 bug`GetPresentationScriptChoicesNum``GetPresentationScriptCommandsNum` 的实现都解析了 `Data.Choices` 而非分别解析 Choices 和 PresentationScript参见 `DialogPresentationScript.cpp:14``DialogPresentationScript.cpp:53`)。调用时需注意此行为。
## 使用方法
在 C++ 中解析脚本命令:
```cpp
int NumCommands = UPresentationJsonLibrary::GetPresentationScriptCommandsNum(ScriptData);
for (int i = 0; i < NumCommands; i++)
{
FName CmdName = UPresentationJsonLibrary::GetPresentationScriptCommandName(ScriptData, i);
TArray<FDialogPresentationScriptParam> Params = UPresentationJsonLibrary::GetPresentationScriptCommandParams(ScriptData, i);
// 根据 CmdName 分发处理...
}
```
在编辑器中序列化命令数组为 JSON
```cpp
// Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp:478
auto ScriptString = UPresentationJsonLibrary::PresentationScriptToJson(DialogNode->PresentationScriptCommands);
```
## 用例
| 文件 | 行号 | 用途 |
|------|------|------|
| Plugins/Dialog/Source/Dialog/Public/DialogPresentationScript.h | 101-145 | 类声明,包含所有公共静态方法签名 |
| Plugins/Dialog/Source/Dialog/Private/DialogPresentationScript.cpp | 6-402 | 全部方法实现JSON 解析、参数构建、反序列化 |
| Plugins/Dialog/Source/DialogEditor/Private/DialogGraphDataAsset.cpp | 478 | 调用 PresentationScriptToJson 将编辑器命令数组序列化为 JSON |

View File

@@ -0,0 +1,21 @@
# Dialog 插件依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| DialogPresentationScript.cpp | #include | DialogPresentationScript.h | FDialogPresentationScriptData, FDialogTextData, EDialogPresentationScriptCommandParamType, FDialogPresentationScriptParam, FDialogPresentationScriptCommand, UPresentationJsonLibrary |
| PresentationStrategyPlugin.h | forward-declare | (inline) | FDialogPresentationScriptCommand |
| PresentationScriptExecutor.h | forward-declare | (inline) | FDialogPresentationScriptData, IPresentationStrategyPlugin |
| DialogPresentationSubsystem.h | forward-declare | (inline) | FDialogTextData, IPresentationScriptExecutor |
| DialogPresentationSubsystem.cpp | #include | DialogPresentationScript.h | FDialogPresentationScriptData, FDialogTextData, UPresentationJsonLibrary |
| DialogPresentationSubsystem.cpp | #include | PresentationScriptExecutor.h | IPresentationScriptExecutor |
| DialogPresentationScript.cpp | #include (Json module) | DialogPresentationScript.h | All structs from DialogPresentationScript.h |
## 关键依赖链
1. Dialog Execution: UDialogPresentationSubsystem::ShowDialog → GetDialogPresentationScriptData → FDialogPresentationScriptData → IPresentationScriptExecutor::ExecutePresentationScript
2. Text Lookup: UDialogPresentationSubsystem::GetDialogTextFromMapTable → DialogTextIndex (TMap<FName, FDialogTextData>)
3. Choice Lookup: UDialogPresentationSubsystem::GetChoiceTextFromMapTable → ChoiceTextIndex (TMap<FName, FHelperArray>) → TArray<FDialogTextData>
4. JSON Parsing: FDialogPresentationScriptData::PresentationScript → UPresentationJsonLibrary (parses) → parses individual FDialogPresentationScriptCommand → FDialogPresentationScriptParam
5. Strategy Dispatch: IPresentationScriptExecutor → IPresentationStrategyPlugin::PerformCommand(FDialogPresentationScriptCommand)

97
Plugins/Item/FItemDef.md Normal file
View File

@@ -0,0 +1,97 @@
# FItemDef
## 基本信息
- **类型**: USTRUCT, 继承 FTableRowBase
- **父类**: FTableRowBase
- **源文件**: Plugins/Item/Source/Item/Public/ItemFactory.h
- **模块**: Item
## 功能概述
物品类型的DataTable行定义。存储设计师创作元数据显示名称、图标、描述、掉落Actor类、编辑器注释、默认动态属性TArray<FItemPropertyEntry>以及bHasTracer标记。
## 设计用意
设计时数据契约。每行代表一种物品类型(如"Sword"、"Apple"。运行时由ItemFactory读取以创建FItemInstance。FTableRowBase继承支持UDataTable::FindRow查找。
## 职责范围
定义某种物品类型的模板/蓝图。不包含运行时状态运行时状态由FItemInstance持有。由设计师在DataTable资产中编辑。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FItemPropertyEntry | 包含 (UPROPERTY成员) | Plugins/Item/Source/Item/Public/ItemFactory.h |
## 对外接口
FItemDef 继承自 FTableRowBase作为 DataTable 的行结构体,由设计师在 DataTable 资产中编辑。运行时通过 UItemRegistrySubsystem::GetItemDef(FName ItemType) 获取。
**可编辑字段:**
- **ItemName** (FText): 物品显示名称。复制到 FItemView::ItemName。
- **DefaultIcon** (TObjectPtr<UTexture2D>): 物品默认图标。复制到 FItemView::Icon。
- **ItemDescription** (FText): 物品描述文本。复制到 FItemView::ItemDescription。
- **DropActorClass** (TSoftClassPtr<AActor>): 物品掉落时生成的 Actor 类(软引用,不强制加载)。
- **EditorComment** (FString): 编辑器注释,仅供设计时参考,不影响运行时行为。
- **DefaultItemProps** (TArray<FItemPropertyEntry>): 物品类型的默认动态属性数组。由 ItemFactory::ConvertDefaultProps 在物品创建时转换并写入 FItemInstance::ItemData。
- **bHasTracer** (bool): 是否为此类型的物品创建 UItemTracer。为 true 时ItemFactory 会在属性包中添加 Internal_ItemTracer 对象,用于追踪物品位置和广播移动事件。
**运行时读取方式:**
调用 UItemRegistrySubsystem::GetItemDef(ItemType) 返回 FItemDef 的副本(值语义)。在使用前应检查 ItemName 是否为空来判断查找是否成功(参见 DefaultContainer.cpp:118 的检查逻辑)。
## 使用方法
FItemDef 作为 DataTable 行结构体使用。设计师在 DataTable 中为每种物品类型创建一行。
**从注册表获取物品定义:**
```cpp
// ItemRegistrySubsystem.cpp:32-42
FItemDef UItemRegistrySubsystem::GetItemDef(FName ItemType) const
{
for (const auto& Pair : ItemDefMap)
{
if (Pair.Value)
{
const FItemDef* Found = Pair.Value->FindRow<FItemDef>(
ItemType, TEXT("GetItemDef"));
if (Found) return *Found; // 返回副本
}
}
return FItemDef(); // 空定义ItemName 为空)
}
```
**在物品创建前验证定义有效性:**
```cpp
// DefaultContainer.cpp:117-118
FItemDef Def = Registry->GetItemDef(ItemType);
if (Def.ItemName.IsEmpty()) return false; // 检查 ItemName 非空
```
**传递给 ItemFactory 创建实例:**
```cpp
// DefaultContainer.cpp:121
TUniquePtr<FItemInstance> NewItem =
ItemFactory::CreateItemInstance(Def, ItemType);
```
**构建视图时使用:**
FItemViewFactory::CreateView 从 FItemDef 复制 ItemName、ItemDescription、DefaultIcon 到 FItemViewItemViewFactory.cpp:11-19
```cpp
static void PopulateCommon(FItemView& View, const FItemDef& Def,
const FItemInstance& Instance,
const TScriptInterface<const IItemContainer>& ItemContainer)
{
View.ItemID = Instance.ItemID;
View.ItemType = Instance.ItemType;
View.ItemName = Def.ItemName;
View.ItemDescription = Def.ItemDescription;
View.Icon = Def.DefaultIcon;
View.Location = ItemContainer;
}
```
## 用例
- `Plugins/Item/Source/Item/Public/ItemFactory.h:103-122` -- FItemDef 结构体定义(所有 UPROPERTY 字段 + FTableRowBase 继承)。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:32-42` -- GetItemDef() 遍历所有已注册 DataTable通过 FindRow<FItemDef> 查找并返回物品定义。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:63` -- GetItemViews 中通过 Registry->GetItemDef(Item->ItemType) 获取物品的 FItemDef传递给 FItemViewFactory。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:77` -- GetItemViewByID 中获取物品定义并传递给工厂。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:117` -- CreateItem 中获取 FItemDef检查 ItemName 非空后将 Def 传递给 ItemFactory::CreateItemInstance。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:6-24` -- CreateItemInstance 接收 FItemDef 参数,读取 DefaultItemProps 和 bHasTracer 来构造物品。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:11-19` -- PopulateCommon 从 FItemDef 复制 ItemName、ItemDescription、DefaultIcon 到 FItemView。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:87-93` -- CreatePreviewView 仅使用 FItemDef 构造预览视图(无运行时数据)。

View File

@@ -0,0 +1,100 @@
# FItemInstance
## 基本信息
- **类型**: USTRUCT
- **父类**: (无)
- **源文件**: Plugins/Item/Source/Item/Public/ItemFactory.h
- **模块**: Item
## 功能概述
单个运行时物品的内部重量级表示。包含唯一GUID、类型键以及FInstancedPropertyBag中的动态属性。由容器通过TUniquePtr独占拥有。具有friend class ItemFactory用于受控构造。
## 设计用意
物品的运行时真值源。刻意不作为BlueprintType公开。TUniquePtr所有权确保清晰的单拥有者语义。FInstancedPropertyBag允许每个物品拥有任意的动态属性。友元控制构造防止产生孤立物品。
## 职责范围
持有单个物品实例的所有运行时状态。通过TUniquePtr在容器间经由InjectPayload传递。不向容器/工厂外部暴露修改接口。
## 项目内依赖
(无项目内依赖)
## 对外接口
FItemInstance 是内部使用的 USTRUCT非 BlueprintType不作为公共 API 直接暴露给外部代码。其字段均标记为 UPROPERTY()(无 BlueprintReadOnly供容器和工厂内部访问。
外部调用方不应直接创建或修改 FItemInstance。与 FItemInstance 交互的唯一入口是通过以下间接途径:
- **创建**: 通过 UDefaultContainer::CreateItem或 IItemContainer::CreateItem内部调用 ItemFactory::CreateItemInstance。
- **查询**: 通过 IItemContainer::GetItemViews() 获取 FItemView 快照,而非直接访问 FItemInstance。
- **属性读写**: 通过 IItemContainer::GetItemProperty / SetItemProperty而非直接操作 FItemInstance::ItemData。
内部字段供 ItemFactory 和容器实现者使用:
- **ItemID** (FGuid): 物品唯一标识,由工厂在创建时通过 FGuid::NewGuid() 生成。
- **ItemType** (FName): 物品类型名称,对应 DataTable 中的行名,用于在注册表中查找 FItemDef。
- **ItemData** (FInstancedPropertyBag): 物品的动态属性包。包含从 FItemDef::DefaultItemProps 转换来的属性,以及可选的 OverrideProps 合并值。当 bHasTracer 为 true 时还包含 Internal_ItemTracer 键。
所有权模型FItemInstance 只能通过 TUniquePtr 持有,禁止复制(拷贝构造 / 赋值运算符保留但标记为 default实际应避免使用。移动语义开放移动构造和移动赋值 = default支持在容器间转移所有权。
## 使用方法
FItemInstance 不直接被外部代码创建或操作。外部代码通过容器接口间接交互。
**创建(仅通过 ItemFactory**
```cpp
// ItemFactory.cpp:6-24
TUniquePtr<FItemInstance> ItemFactory::CreateItemInstance(
const FItemDef& ItemDef, FName ItemType,
const FInstancedPropertyBag* OverrideProps)
{
TUniquePtr<FItemInstance> Instance = MakeUnique<FItemInstance>();
Instance->ItemID = FGuid::NewGuid();
Instance->ItemType = ItemType;
ConvertDefaultProps(ItemDef.DefaultItemProps, Instance->ItemData);
if (OverrideProps)
MergePropertyBag(*OverrideProps, Instance->ItemData);
if (ItemDef.bHasTracer)
{
Instance->ItemData.AddProperty(UInternalItemProperty::ItemTracer(),
EPropertyBagPropertyType::Object, UItemTracer::StaticClass());
UItemTracer* NewTracer = NewObject<UItemTracer>();
Instance->ItemData.SetValueObject(
UInternalItemProperty::ItemTracer(), NewTracer);
}
return Instance;
}
```
**存储(由 UDefaultContainer 管理):**
```cpp
// DefaultContainer.h:33
TArray<TUniquePtr<struct FItemInstance>> Items;
// DefaultContainer.cpp:129-135 - 注入存储
void UDefaultContainer::InjectPayloadImpl(TUniquePtr<FItemInstance> Payload)
{
if (Payload.IsValid())
Items.Add(MoveTemp(Payload));
}
```
**读取属性(由容器内部处理):**
容器通过 ItemData 读取属性值和描述符。例如 DefaultContainer.cpp:137-187 的 Internal_GetPropertyRaw 中:
```cpp
const FPropertyBagPropertyDesc* Desc =
Bag->FindPropertyDescByName(FixedName);
void* BagMemory = FoundItem->ItemData.GetMutableValue().GetMemory();
void* SourceAddr = Desc->CachedProperty->ContainerPtrToValuePtr<void>(BagMemory);
```
**在所有容器间转移所有权:**
通过 MoveTemp + InjectPayload 组合操作。物品的 TUniquePtr 所有权从源容器移至目标容器DefaultContainer.cpp:104-108
## 用例
- `Plugins/Item/Source/Item/Public/ItemFactory.h:60-82` -- FItemInstance 结构体定义ItemID、ItemType、ItemData 三个字段 + 友元声明 + 构造/移动/拷贝控制)。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:6-24` -- ItemFactory::CreateItemInstance 创建 FItemInstance 的唯一入口MakeUnique + 填充字段 + 属性转换 + Tracer 创建)。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:60-64` -- GetItemViews 遍历 Items 数组中的 TUniquePtr<FItemInstance> 并解引用传递给 FItemViewFactory。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:73-78` -- GetItemViewByID 按 ID 匹配 Items 中的 FItemInstance 指针。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:96-108` -- MoveItem 通过 MoveTemp 转移 FItemInstance 所有权,从源数组 RemoveAt 后注入目标容器。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:121-124` -- CreateItem 接收工厂返回的 TUniquePtr<FItemInstance> 并注入自身。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:129-134` -- InjectPayloadImpl 接收 TUniquePtr<FItemInstance> 并 Add 到 Items 数组。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:140-186` -- Internal_GetPropertyRaw 从 FoundItem->ItemData 中按属性名称读取值。
- `Plugins/Item/Source/Item/Private/ItemContainer.cpp:45-67` -- InjectPayload NVI 从 Payload->ItemData 中读取 Tracer 并更新位置。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:11-19` -- PopulateCommon 从 Instance.ItemID / Instance.ItemType 读取字段填入 FItemView。

View File

@@ -0,0 +1,76 @@
# FItemPropertyEntry
## 基本信息
- **类型**: USTRUCT
- **父类**: (无)
- **源文件**: Plugins/Item/Source/Item/Public/ItemFactory.h
- **模块**: Item
## 功能概述
面向设计师的结构体将GameplayTag与类型无关的属性值选择器FInstancedPropertyBag配对。用于FItemDef::DefaultItemProps数组中以定义默认动态属性。构造函数自动向ValuePicker添加一个类型为Bool、名称为"Property"的字段。
## 设计用意
桥接GameplayTag语义属性标识与设计师编写的默认值。FInstancedPropertyBag提供类型安全的编辑器控件。"Property"子名称约定被ItemFactory::ConvertDefaultProps使用。
## 职责范围
作为FItemDef中的声明条目。定义某个物品类型应具有哪些属性及其默认值。不直接在运行时使用值会被复制到FItemInstance::ItemData中
## 项目内依赖
(无项目内依赖)
## 对外接口
FItemPropertyEntry 是面向设计师的 USTRUCT在 DataTable 编辑器中直接编辑。外部代码通常不直接构造此结构体,而是通过 FItemDef::DefaultItemProps 数组间接使用。
**可编辑字段:**
- **PropertyTag** (FGameplayTag): 语义化的属性标识。例如 "Attribute.Health"、"Weapon.Damage" 等。在运行时,其 TagName 被用作物品属性包中的属性名称(点号会替换为下划线,参见 ItemFactory::ConvertDefaultProps 中的处理)。
- **ValuePicker** (FInstancedPropertyBag): 属性值选择器。构造函数自动添加一个名为 "Property"、类型为 Bool 的默认字段。设计师在编辑器中可将其类型改为 Int32、Float、FName、FString 等并设置默认值。
**运行时读取方式:**
ItemFactory::ConvertDefaultProps 遍历 DefaultItemProps 数组,从每个 Entry 的 ValuePicker 中读取名为 "Property" 的属性描述和值,以 PropertyTag 的 TagName 为键写入 FItemInstance::ItemData。
**编辑器定制:**
ItemEditor 模块ItemEditorModule.cpp:23-26注册了 FItemPropertyEntryCustomization 作为 "ItemPropertyEntry" 的自定义属性布局,提供增强的编辑器体验。
## 使用方法
FItemPropertyEntry 在 DataTable 编辑器中作为 FItemDef::DefaultItemProps 数组的元素使用。
**DataTable 中的配置:**
设计师在物品的 FItemDef 行中添加 FItemPropertyEntry 元素:
1. 设置 PropertyTag如 GameplayTag "Attribute.Health")。
2. 在 ValuePicker 中将 "Property" 字段的类型从默认 Bool 改为所需类型Int32、Float、FName 等)并设置默认值。
**运行时转换:**
ItemFactory::ConvertDefaultProps 是唯一读取 FItemPropertyEntry 的地方ItemFactory.cpp:26-48
```cpp
void ItemFactory::ConvertDefaultProps(
const TArray<FItemPropertyEntry>& DefaultProps,
FInstancedPropertyBag& OutItemData)
{
for (const FItemPropertyEntry& Entry : DefaultProps)
{
const FPropertyBagPropertyDesc* SrcDesc =
Entry.ValuePicker.FindPropertyDescByName(TEXT("Property"));
if (!SrcDesc || !SrcDesc->CachedProperty) continue;
// 从 ValuePicker 中读内存、复制到 OutItemData
void* SrcPtr = SrcDesc->CachedProperty->ContainerPtrToValuePtr<void>(
const_cast<uint8*>(SrcMem));
const FName TargetName = FName(*Entry.PropertyTag.GetTagName()
.ToString().Replace(TEXT("."), TEXT("_")));
OutItemData.AddProperty(TargetName, SrcDesc->ValueType,
SrcDesc->ValueTypeObject);
// ... 复制值到 OutItemData
}
}
```
注意PropertyTag 中的点号会被替换为下划线,因为属性包名称不允许点号。
**编辑器定制:**
ItemEditor 模块注册了自定义属性布局ItemEditorModule.cpp:23-26使 FItemPropertyEntry 在编辑器中以定制方式展示,而非默认的结构体属性列表。
## 用例
- `Plugins/Item/Source/Item/Public/ItemFactory.h:85-100` -- FItemPropertyEntry 结构体定义PropertyTag、ValuePicker 字段 + 构造函数自动添加 "Property" Bool 字段)。
- `Plugins/Item/Source/Item/Public/ItemFactory.h:119` -- FItemDef::DefaultItemProps 声明为 TArray<FItemPropertyEntry>,是 FItemPropertyEntry 的唯一数组使用位置。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:26-48` -- ConvertDefaultProps 是唯一读取 FItemPropertyEntry 的函数,遍历 DefaultProps 数组并将每个 Entry 的属性复制到 FInstancedPropertyBag。
- `Plugins/Item/Source/ItemEditor/Private/ItemEditorModule.cpp:23-26` -- 注册 FItemPropertyEntryCustomization 作为 "ItemPropertyEntry" 的自定义属性类型布局(编辑器模块)。
- `Plugins/Item/Source/ItemEditor/Private/ItemPropertyEntryCustomization.cpp` -- 编辑器定制实现,定制 FItemPropertyEntry 在 DataTable 编辑器中的显示方式。

66
Plugins/Item/FItemView.md Normal file
View File

@@ -0,0 +1,66 @@
# FItemView
## 基本信息
- **类型**: USTRUCT(BlueprintType)
- **父类**: (无)
- **源文件**: Plugins/Item/Source/Item/Public/ItemContainer.h
- **模块**: Item
## 功能概述
一个轻量级的只读数据传输结构体用于向UI提供物品的快照。包含物品标识ID、Type、显示信息Name、Description、Icon、当前位置以TScriptInterface表示以及格式化后的属性文本。
## 设计用意
将可变状态FItemInstance与只读视图分离防止UI代码直接修改物品数据。快照模式确保即使底层物品发生移动UI仍持有稳定数据。作为IItemContainer::GetItemViews()的返回类型是容器到UI的数据桥梁。
## 职责范围
承载从容器到UI的物品显示数据。作为IItemContainer::GetItemViews()的返回类型使用。不拥有也不修改物品实例。
## 项目内依赖
(无项目内依赖 - 定义于ItemContainer.h中仅包含引擎头文件
## 对外接口
FItemView 是一个纯数据 USTRUCTBlueprintType所有字段标记为 BlueprintReadOnly外部代码只能读取不能写入。调用方通过 IItemContainer::GetItemViews() 或 GetItemViewByID() 获取 FItemView 数组/单例,然后在 Blueprint 或 C++ 中读取以下字段:
- **ItemID** (FGuid): 物品的唯一标识符,由 ItemFactory::CreateItemInstance 生成FGuid::NewGuid())。
- **Location** (TScriptInterface<const IItemContainer>): 物品当前所在容器的只读接口引用。UI 可通过此字段判断物品属于哪个容器。
- **ItemType** (FName): 物品类型名称,对应 DataTable 中的行名RowName
- **ItemName** (FText): 物品的显示名称,复制自 FItemDef::ItemName。
- **ItemDescription** (FText): 物品的描述文本,复制自 FItemDef::ItemDescription。
- **Icon** (TObjectPtr<UTexture2D>): 物品图标,复制自 FItemDef::DefaultIcon。
- **ItemDataText** (FText): 物品的动态属性格式化文本。由 FItemViewFactory::CreateView 遍历物品的 FInstancedPropertyBag通过 IItemViewStrategy 格式化每个属性值后拼接而成。包含换行符分隔的多行属性描述。
注意FItemView 是快照数据不随物品状态变化自动更新。UI 需要在库存变化时重新获取。
## 使用方法
FItemView 由 FItemViewFactory 创建、由 IItemContainer 的查询方法返回,外部代码通常不直接构造。
**从容器获取物品视图C++ 侧):**
```cpp
// DefaultContainer.cpp:54-66
TArray<FItemView> UDefaultContainer::GetItemViews() const
{
TArray<FItemView> Results;
UItemRegistrySubsystem* Registry = GetRegistry(this);
if (!Registry) return Results;
Results.Reserve(Items.Num());
for (const TUniquePtr<FItemInstance>& Item : Items)
{
if (!Item) continue;
FItemDef Def = Registry->GetItemDef(Item->ItemType);
Results.Add(FItemViewFactory::CreateView(Registry, Def, *Item,
TScriptInterface<const IItemContainer>(this)));
}
return Results;
}
```
**在 Blueprint 中消费 FItemView**
UI Widget如 WBP_InventoryView通过引用 BP_InventoryComp 组件,从其 ViewCache 读取 FItemView 数组,然后访问 ItemName、Icon、ItemDataText 等字段绑定到 UI 控件进行显示。FItemView 的所有字段均为 BlueprintReadOnly可以直接在 Blueprint 中读取但不可修改。
## 用例
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:54-66` -- GetItemViews() 遍历内部 Items 并为每个物品调用 FItemViewFactory::CreateView 构造 FItemView作为 TArray 返回。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:69-81` -- GetItemViewByID() 按 FGuid 查找单个物品并返回其 FItemView。
- `Plugins/Item/Source/Item/Public/Inventory.h:27` -- IInventory 接口声明 GetItemViews() 返回 TArray<FItemView>Bridge 到面向 Blueprint 的接口。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:11-19` -- PopulateCommon 填充 FItemView 的通用字段ItemID、ItemType、ItemName、ItemDescription、Icon、Location
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:57-85` -- CreateView 完整构造 FItemView包括通过策略格式化动态属性文本。
- `Document/Content/Blueprints/WBP_InventoryView.md` -- UI Widget 读取 FItemView 数组,绑定 ItemName、Icon、ItemDataText 到 UI 控件显示。

View File

@@ -0,0 +1,85 @@
# FItemViewFactory
## 基本信息
- **类型**: C++ 静态类
- **父类**: (无)
- **源文件**: Plugins/Item/Source/Item/Public/ItemViewFactory.h
- **模块**: Item
## 功能概述
从FItemDef + FItemInstance + 容器构造FItemView的静态工厂。三个方法CreateView含通过IItemViewStrategy的动态属性文本、CreatePreviewView不含动态数据、CreateEmptyView。使用UItemRegistrySubsystem获取策略。
## 设计用意
将视图构造与数据模型和容器分离。代码库中唯一调用IItemViewStrategy::GetPropertyText的地方。显示格式化流水线的单一装配点。
## 职责范围
构建FItemView结构体。遍历物品属性、从注册表查找策略、格式化值。不拥有数据。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ItemRegistrySubsystem.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemRegistrySubsystem.h |
| ItemFactory.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemFactory.h |
| ItemContainer.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemContainer.h |
| ItemViewStrategy.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemViewStrategy.h |
## 对外接口
FItemViewFactory 是纯 C++ 静态类,提供三个静态方法用于从不同数据源构造 FItemView。
- **CreateView(const UItemRegistrySubsystem* Registry, const FItemDef& Def, const FItemInstance& Instance, const TScriptInterface<const IItemContainer>& ItemContainer)** → FItemView: 从完整的物品数据构造视图。这是最常用的方法。
1. 调用内部 PopulateCommon 填充 ItemID、ItemType、ItemName、ItemDescription、Icon、Location。
2. 遍历 Instance.ItemData 中的所有属性描述。
3. 对每个属性,通过 Registry->GetViewStrategy 查找策略。
4. 调用 PropertyValueToString 将属性值转为字符串bool→是/否int/float→数字FName/FString→原值
5. 调用 Strategy->GetPropertyText(ValueString) 获取格式化文本。
6. 将所有非空行用换行符拼接为 ItemDataText。
- **CreatePreviewView(const FItemDef& Def)** → FItemView: 构造预览视图(不带运行时数据)。使用空 FItemInstance 和空容器指针填充通用字段。ItemDataText 为空。适用于 UI 预览如物品图鉴、Tooltip 预览),无需实际物品实例。
- **CreateEmptyView()** → FItemView: 返回默认构造的空 FItemView。所有字段为零值/空值。适用于初始化或错误返回场景。
**调用方:**
仅由 UDefaultContainer 调用DefaultContainer.cpp:64 和 :78在 GetItemViews 和 GetItemViewByID 中为每个物品创建视图快照。
## 使用方法
FItemViewFactory 只由 UDefaultContainer 调用,不对外部开放。
**典型用法 -- GetItemViewsDefaultContainer.cpp:64**
```cpp
Results.Add(FItemViewFactory::CreateView(Registry, Def, *Item,
TScriptInterface<const IItemContainer>(this)));
```
**典型用法 -- GetItemViewByIDDefaultContainer.cpp:78**
```cpp
return FItemViewFactory::CreateView(Registry, Def, *Item,
TScriptInterface<const IItemContainer>(this));
```
**CreateView 内部流程ItemViewFactory.cpp:57-85**
1. 创建空 FItemView。
2. PopulateCommon 填充通用字段ItemID、ItemType、ItemName、ItemDescription、Icon、Location。
3. 遍历 Instance.ItemData 中的每个属性描述:
- 通过 Registry->GetViewStrategy(Desc.Name) 查找策略。
- 策略不存在则跳过该属性。
- PropertyValueToString 将属性值转为字符串。
- 字符串为空则跳过。
- Strategy->GetPropertyText(ValueString) 获取格式化文本。
- 非空则追加到 ResultText换行分隔
4. 将 ResultText 赋值给 View.ItemDataText。
**CreatePreviewView 用法ItemViewFactory.cpp:87-93**
使用空实例和空容器构造视图仅含通用字段ItemDataText 为空。适用于无实际物品实例的预览场景。
**CreateEmptyView 用法ItemViewFactory.cpp:96-98**
返回默认构造的 FItemView。用于找不到物品或注册表不可用时的错误返回如 DefaultContainer.cpp:72,81
## 用例
- `Plugins/Item/Source/Item/Public/ItemViewFactory.h:16-26` -- FItemViewFactory 类声明三个静态方法CreateView、CreatePreviewView、CreateEmptyView
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:11-19` -- PopulateCommon 静态辅助函数:填充 FItemView 的通用字段ItemID、ItemType、ItemName、ItemDescription、Icon、Location
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:22-55` -- PropertyValueToString 静态辅助函数:将属性包值转换为字符串(支持 Bool、Int32、Float、Double、Name、String 类型)。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:57-85` -- CreateView 完整实现:遍历属性包 → 查找策略 → 格式化文本 → 拼接 ItemDataText。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:87-93` -- CreatePreviewView 实现使用空实例构造仅含通用字段的视图ItemDataText 为空。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:96-98` -- CreateEmptyView 实现:返回默认构造的空 FItemView。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:64` -- GetItemViews 中调用 FItemViewFactory::CreateView 为每个物品创建视图。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:78` -- GetItemViewByID 中调用 FItemViewFactory::CreateView 为查找到的物品创建视图。

View File

@@ -0,0 +1,68 @@
# IInventory
## 基本信息
- **类型**: UINTERFACE + C++ 接口 (Blueprintable)
- **父类**: UInterface
- **源文件**: Plugins/Item/Source/Item/Public/Inventory.h
- **模块**: Item
## 功能概述
位于IItemContainer之上的更高层、蓝图友好的物品栏接口。所有函数均使用BlueprintNativeEvent。提供RequestMoveItem/ReceiveItem握手模式用于物品栏间的物品转移。与IItemContainer不同此接口可以在Blueprint中实现。
## 设计用意
连接底层C++ IItemContainer与Blueprint游戏逻辑的桥梁。允许Blueprint Actor参与物品交换而无需实现完整的IItemContainer契约。双函数握手模式支持网络化的物品栏交互模式。
## 职责范围
提供Blueprint可访问的物品栏协议。实现者处理Request/Receive握手。不定义存储或属性访问这些委托给IItemContainer
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ItemContainer.h | #include | Plugins/Item/Source/Item/Public/ItemContainer.h |
## 对外接口
IInventory 是 Blueprint 友好的物品栏接口UINTERFACE 标记了 BlueprintType 和 Blueprintable所有方法使用 BlueprintNativeEvent允许在 Blueprint 中实现和覆写。
**查询方法BlueprintCallable, BlueprintNativeEvent**
- **GetItemViews()** → TArray<FItemView>: 返回物品栏中所有物品的视图快照。调用方(如 UI Widget通过 TScriptInterface<IInventory> 调用此方法获取物品列表数据。
- **GetItemViewByID(const FGuid&)** → FItemView: 按 ID 查找单个物品视图。
- **GetItemCount()** → int32: 返回当前物品栏中的物品数量。
**物品交换协议BlueprintCallable, BlueprintNativeEvent**
- **RequestMoveItem(const FGuid&, const TScriptInterface<IInventory>&)**: 请求将指定物品移动到目标物品栏。调用方(发送方)调用此方法。典型实现在内部委托给 IItemContainer::MoveItem。此方法无返回值移动成功或失败由调用方自行判断。
- **ReceiveItem(const FGuid&, const TScriptInterface<IItemContainer>&)** (BlueprintAuthorityOnly): 从来源容器接收指定物品。目标方(接收方)实现此方法。来源容器以 IItemContainer 接口形式传入,接收方可调用其 GetItemViewByID 获取物品信息后执行接收逻辑。
Request/Receive 握手模式使两个物品栏可以在 Blueprint 层完成物品交换,无需了解对方的内部实现。
## 使用方法
IInventory 在 Blueprint 中实现。典型实现者是 BP_InventoryComp 及其子类 BP_DropItemInvComp。
**Blueprint 实现模式:**
在 Blueprint 中,实现者覆写 BlueprintNativeEvent 方法。例如 BP_InventoryComp
- GetItemViews 返回缓存的 ViewCache 数组。
- GetItemViewByID 从 ViewCache 中按 ID 查找。
- GetItemCount 返回 ViewCache.Num()。
- RequestMoveItem 内部调用其持有的 DefaultContainer 的 IItemContainer::MoveItem。
- ReceiveItem 接收来源容器的物品后刷新本地视图缓存。
**物品栏间交换流程:**
```
发送方: RequestMoveItem(ItemID, TargetInventory)
→ 内部调用 DefaultContainer->MoveItem(ItemID, TargetContainer)
→ TargetContainer->InjectPayload(...) // C++ 层注入
→ 更新本地 ViewCache / 广播 OnViewChanged
接收方: ReceiveItem(ItemID, SourceContainer)
→ 从 SourceContainer 获取物品信息
→ 刷新本地 ViewCache / 广播 OnViewChanged
```
**持有容器:**
IInventory 实现者不直接存储物品,而是持有一个 UDefaultContainer或其子类 BP_DefaultContainer的引用将 IInventory 方法委托给 IItemContainer 实现。这保持了 Blueprint 逻辑层IInventory与存储层IItemContainer的分离。
## 用例
- `Plugins/Item/Source/Item/Public/Inventory.h:20-40` -- IInventory 接口的完整声明(所有 BlueprintNativeEvent 方法定义)。
- `Plugins/Item/Source/Item/Private/Inventory.cpp:6` -- Inventory.cpp 实现文件,目前为空(仅注释说明为非纯虚函数提供默认实现)。
- `Document/Content/Blueprints/BP_InventoryComp.md` -- BP_InventoryComp 是 IInventory 的 Blueprint 实现者,包装 UDefaultContainer 并持有 ViewCache/OnViewChanged。
- `Document/Content/Blueprints/BP_DropItemInvComp.md` -- BP_DropItemInvComp 继承 BP_InventoryComp是掉落物场景的 IInventory 实现,在 BeginPlay 时自动创建物品。
- `Document/Content/Blueprints/WBP_InventoryView.md` -- UI Widget 通过引用 BP_InventoryCompIInventory 实现者)获取 ViewCache 数据显示物品列表。

View File

@@ -0,0 +1,96 @@
# IItemContainer + UItemContainer
## 基本信息
- **类型**: UINTERFACE + C++ 接口
- **父类**: UInterface
- **源文件**: Plugins/Item/Source/Item/Public/ItemContainer.h
- **模块**: Item
## 功能概述
物品容器操作的主要C++接口。提供查询GetItemViews、GetItemCount、变更MoveItem、CreateItem和属性读写GetItemProperty、SetItemProperty使用CustomThunk。采用NVI模式通过InjectPayload/InjectPayloadImpl实现物品注入。UItemContainer带有meta=(CannotImplementInterfaceInBlueprint)标记限制实现仅限C++侧。
## 设计用意
插件的核心抽象层。将"可以对物品做什么"与"物品存储在何处"分离。属性读写使用CustomThunk使单个UFUNCTION支持任意Blueprint类型。NVI模式确保在注入时可自动更新Tracer。
## 职责范围
定义物品存储的完整契约。实现者如UDefaultContainer提供具体的存储方案。调用者通过此接口查询、移动、创建和修改物品无需了解容器类型。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ItemFactory.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemFactory.h |
| FItemInstance | forward-declare | Plugins/Item/Source/Item/Public/ItemFactory.h |
## 对外接口
IItemContainer 是 C++ 纯虚接口(其 UINTERFACE 标记了 meta=(CannotImplementInterfaceInBlueprint)),只允许在 C++ 侧实现。调用方通过 TScriptInterface<IItemContainer> 引用容器,可调用以下方法:
**查询方法BlueprintCallable**
- **GetItemViews()** → TArray<FItemView>: 返回容器中所有物品的只读视图快照。用于 UI 显示物品列表。
- **GetItemViewByID(const FGuid&)** → FItemView: 根据物品 ID 查找并返回单个物品视图。未找到时返回默认构造的空视图。
- **GetItemCount()** → int32: 返回容器当前持有的物品数量。
**变更方法BlueprintCallable, BlueprintAuthorityOnly**
- **MoveItem(const FGuid&, const TScriptInterface<IItemContainer>&)** → bool: 将指定物品从当前容器移动到目标容器。内部通过 InjectPayload 传递给目标容器。移动失败返回 false。
- **CreateItem(FName, TArray<FGuid>&, int32 Count = 1)** → bool: 根据物品类型名称创建指定数量的物品并加入本容器。新创建的物品 ID 会追加到 NewItemIDs 数组中。需要物品类型在注册表中已注册。
**属性读写BlueprintCallable, BlueprintAuthorityOnly, CustomThunk**
- **GetItemProperty(FGuid, FName, int32& Value)** → bool: 从物品的动态属性包中读取指定名称的属性值。CustomThunk 机制使 Value 参数自动匹配实际属性类型bool/int/float/FName/FString 等)。返回值表示读取是否成功。
- **SetItemProperty(FGuid, FName, int32 Value)**: 向物品的动态属性包写入值。类型由蓝图引脚自动推断CustomThunk 分发到 Internal_SetPropertyRaw。
**C++ 专有接口(非 UFUNCTION**
- **InjectPayload(TUniquePtr<FItemInstance>)**: 向容器注入一个物品的所有权移动语义。NVI 模式:公开的非虚方法负责更新物品 Tracer位置 + 广播 OnItemMoved然后调用 protected 虚方法 InjectPayloadImpl 完成实际存储。
- **Internal_GetPropertyRaw / Internal_SetPropertyRaw**: CustomThunk 的底层分发目标,由实现者覆写。接收原生 FProperty 指针和内存地址,完成类型兼容性检查后的值拷贝。
## 使用方法
IItemContainer 的实现者(如 UDefaultContainer在主模块中实现所有纯虚方法。调用方通过 TScriptInterface<IItemContainer> 操作容器。
**获取容器接口:**
持有 UDefaultContainer 或其子类(如 BP_DefaultContainer的对象可通过隐式转换获取 TScriptInterface
```cpp
// DefaultContainer.cpp:64 - 从 this 指针构造接口引用
TScriptInterface<const IItemContainer>(this)
```
**调用方获取注册表并查询物品:**
```cpp
// DefaultContainer.cpp:44-52 - 获取注册表的辅助函数
static UItemRegistrySubsystem* GetRegistry(const UObject* WorldContext)
{
if (!WorldContext) return nullptr;
UWorld* World = WorldContext->GetWorld();
if (!World) return nullptr;
UGameInstance* GI = World->GetGameInstance();
if (!GI) return nullptr;
return GI->GetSubsystem<UItemRegistrySubsystem>();
}
```
**移动物品示例:**
```cpp
// DefaultContainer.cpp:89-111 - MoveItem 实现
bool UDefaultContainer::MoveItem(const FGuid& ItemID,
const TScriptInterface<IItemContainer>& TargetContainer)
{
IItemContainer* TargetInterface = TargetContainer.GetInterface();
if (!TargetInterface) return false;
// 查找并取出物品...
TUniquePtr<FItemInstance> MovedItem = MoveTemp(Items[Index]);
Items.RemoveAt(Index);
// 通过接口注入目标容器
TargetInterface->InjectPayload(MoveTemp(MovedItem));
return true;
}
```
**CustomThunk 属性读写:**
GetItemProperty / SetItemProperty 通过 exec 函数分发到底层的 Internal_GetPropertyRaw / Internal_SetPropertyRawItemContainer.cpp:11-43。在 Blueprint 中调用时Value 引脚自动根据属性类型展开,无需手动指定类型。
## 用例
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:54-66` -- GetItemViews() 实现:遍历物品并调用 FItemViewFactory 构造视图,通过 TScriptInterface<const IItemContainer>(this) 传递自身引用。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:69-81` -- GetItemViewByID() 实现:按 ID 查找并返回单物品视图。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:89-111` -- MoveItem() 实现:从 TArray 中取出 TUniquePtr调用目标容器的 InjectPayload 注入。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:113-127` -- CreateItem() 实现:通过注册表获取 FItemDef调用 ItemFactory::CreateItemInstance 创建,通过 InjectPayload 注入。
- `Plugins/Item/Source/Item/Private/ItemContainer.cpp:7-43` -- GetItemProperty / SetItemProperty 的 CustomThunk exec 函数实现,分发到 Internal_GetPropertyRaw / Internal_SetPropertyRaw。
- `Plugins/Item/Source/Item/Private/ItemContainer.cpp:45-67` -- InjectPayload NVI 实现:查找 Tracer → 更新位置 → 广播 OnItemMoved → 调用 InjectPayloadImpl。
- `Plugins/Item/Source/Item/Public/Inventory.h:39` -- IInventory::ReceiveItem 的参数使用 TScriptInterface<IItemContainer>Bridge IInventory 与 IItemContainer。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:57` -- FItemViewFactory::CreateView 接收 TScriptInterface<const IItemContainer> 参数,设置到 FItemView::Location。

View File

@@ -0,0 +1,81 @@
# IItemViewStrategy
## 基本信息
- **类型**: UINTERFACE + C++ 接口
- **父类**: UInterface
- **源文件**: Plugins/Item/Source/Item/Public/ItemViewStrategy.h
- **模块**: Item
## 功能概述
物品属性显示格式化的策略接口。每个策略处理一个属性名称。方法GetPropertyName、GetPropertyText将值字符串转换为显示文本、GetPriority排序顺序。全部为BlueprintNativeEvent。
## 设计用意
属性显示的策略模式。不同属性需要不同的格式化方式(例如"15 ATK" vs "Defense: 10"。设计师创建Blueprint策略在UItemRegistrySubsystem中按属性名称注册。
## 职责范围
为单个属性的值进行格式化以供显示。在构建FItemView::ItemDataText时由FItemViewFactory调用。不了解物品或容器。
## 项目内依赖
(无项目内依赖)
## 对外接口
IItemViewStrategy 是物品属性显示格式化的策略接口BlueprintNativeEvent通常在 Blueprint 中实现。每个策略对象处理一个特定的属性名称。
**BlueprintNativeEvent 方法(在 Blueprint 中覆写):**
- **GetPropertyName()** → FName: 返回此策略处理的属性名称。注册表按此名称将策略注册到 UItemRegistrySubsystemRegisterViewStrategy 的 PropertyName 参数。运行时FItemViewFactory 用物品属性的 Desc.Name 去注册表查找对应策略。
- **GetPropertyText(const FString& ValueString)** → FText: 将属性的原始值字符串转换为显示文本。ValueString 是 FItemViewFactory 通过 PropertyValueToString 从属性包中提取的字符串bool→"是/否"int/float→数字字符串FName→名称字符串FString→原值。策略可返回任何格式的 FText如 "攻击力: 15" 或 "+10 DEF"。返回空文本时FItemViewFactory 跳过该属性行。
- **GetPriority()** → int32: 返回显示优先级。数值越大排在越前面。(注意:当前实现中 FItemViewFactory::CreateView 按属性包遍历顺序迭代,未按优先级排序——需要验证是否在蓝图侧实现排序,或者此方法为预留接口。)
**注册方式:**
策略实现为 Blueprint 类后,通过 UItemRegistrySubsystem::RegisterViewStrategy 按属性名称注册,或在 UItemRegistrySettings::ViewStrategies 中配置自动注册。
**调用链:**
FItemViewFactory::CreateView → Registry->GetViewStrategy(Desc.Name) → Strategy->GetPropertyText(ValueStr)。
## 使用方法
IItemViewStrategy 在 Blueprint 中实现,在项目设置或代码中注册。
**Blueprint 实现模式:**
创建继承 ItemViewStrategy 接口的 Blueprint 类,覆写三个方法:
- GetPropertyName: 返回此策略处理的属性名(如 "Attribute_Health")。
- GetPropertyText: 接收 ValueString如 "100"),返回格式化文本(如 "生命值: 100")。
- GetPriority: 返回排序优先级(数值越大排在越前面)。
**注册策略(两种方式):**
1. **项目设置自动注册**:在 Project Settings > Game > Item Registry Settings 的 ViewStrategies 映射中,以属性名为键、策略 Blueprint 类为值配置ItemRegistrySubsystem.cpp:70-78 在 Initialize 中自动加载并实例化)。
```cpp
// ItemRegistrySubsystem.cpp:70-78
for (const auto& Pair : Settings->ViewStrategies)
{
if (UClass* StrategyClass = Pair.Value.LoadSynchronous())
{
TScriptInterface<IItemViewStrategy> NewStrategy =
NewObject<UObject>(this, StrategyClass);
RegisterViewStrategy(Pair.Key, NewStrategy);
}
}
```
2. **运行时手动注册**:调用 UItemRegistrySubsystem::RegisterViewStrategy(PropertyName, Strategy)。
**调用链FItemViewFactory 中ItemViewFactory.cpp:70-79**
```cpp
TScriptInterface<IItemViewStrategy> Strategy =
Registry->GetViewStrategy(Desc.Name);
if (!Strategy.GetInterface()) continue;
FString ValueStr = PropertyValueToString(Bag, Desc);
if (ValueStr.IsEmpty()) continue;
FText Line = Strategy->GetPropertyText(ValueStr);
if (!Line.IsEmpty())
{
if (!ResultText.IsEmpty()) ResultText += TEXT("\n");
ResultText += Line.ToString();
}
```
## 用例
- `Plugins/Item/Source/Item/Public/ItemViewStrategy.h:19-34` -- IItemViewStrategy 接口声明GetPropertyName、GetPropertyText、GetPriority 三个 BlueprintNativeEvent
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:70-79` -- CreateView 中通过 Registry->GetViewStrategy(Desc.Name) 获取策略,调用 Strategy->GetPropertyText(ValueString) 格式化显示文本。
- `Plugins/Item/Source/Item/Public/ItemRegistrySubsystem.h:28` -- RegisterViewStrategy 的方法声明,接收 TScriptInterface<IItemViewStrategy> 参数。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:20-25` -- RegisterViewStrategy 实现:将 (PropertyName, Strategy) 添加到 ViewStrategyMap。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:45-52` -- GetViewStrategy 实现:从 ViewStrategyMap 查找并返回 TScriptInterface<IItemViewStrategy>。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:75` -- Initialize 中 NewObject 创建策略实例,注册到 ViewStrategyMap。

View File

@@ -0,0 +1,85 @@
# ItemFactory
## 基本信息
- **类型**: C++ 静态类非UCLASS
- **父类**: (无)
- **源文件**: Plugins/Item/Source/Item/Public/ItemFactory.h
- **模块**: Item
## 功能概述
用于创建FItemInstance对象的纯静态工厂。唯一公开方法CreateItemInstance生成GUID、将FItemDef::DefaultItemProps转换为属性包、可选合并覆盖项并在bHasTracer为true时添加UItemTracer。为FItemInstance的友元类。
## 设计用意
集中化、单一入口的物品创建。确保每个物品都有有效GUID、已填充的默认值和一致的Tracer设置。友元访问阻止外部代码构造未初始化的物品。
## 职责范围
仅负责物品实例化。接收FItemDef + 类型名称返回TUniquePtr<FItemInstance>。私有辅助方法处理属性转换和合并。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| FItemInstance | friend class | Plugins/Item/Source/Item/Public/ItemFactory.h |
| FItemDef | 参数类型 | Plugins/Item/Source/Item/Public/ItemFactory.h |
| UInternalItemProperty | 调用 | Plugins/Item/Source/Item/Public/ItemFactory.h |
| UItemTracer | 创建 (NewObject) | Plugins/Item/Source/Item/Public/ItemFactory.h |
## 对外接口
ItemFactory 是纯 C++ 静态类(非 UCLASS仅提供一个公开静态方法作为物品创建的唯一入口
- **CreateItemInstance(const FItemDef& ItemDef, FName ItemType, const FInstancedPropertyBag* OverrideProps = nullptr)** → TUniquePtr<FItemInstance>: 根据物品定义创建并返回一个新的物品实例。
**参数说明:**
- ItemDef: 物品的 DataTable 行定义,提供默认属性、显示信息等。
- ItemType: 物品类型名称(通常与 DataTable 行名一致),写入 FItemInstance::ItemType。
- OverrideProps: 可选的特化属性集。传入 nullptr 则仅使用 ItemDef 中的 DefaultItemProps。非空时会在 DefaultItemProps 基础上合并覆盖值(同一属性名会被覆盖)。
**返回值:**
返回 TUniquePtr<FItemInstance>。创建的物品实例具有以下特征:
- 自动生成唯一 ItemIDFGuid::NewGuid())。
- ItemData 已填充 DefaultItemProps + OverrideProps 的合并属性。
- 如果 ItemDef.bHasTracer 为 trueItemData 中额外包含 Internal_ItemTracer 键的 UItemTracer 对象。
**调用限制:**
此方法是创建 FItemInstance 的唯一入口。FItemInstance 的构造函数是 private/default通过 friend class ItemFactory 控制),外部代码无法直接构造 FItemInstance。
## 使用方法
ItemFactory 的唯一使用场景是通过 UDefaultContainer::CreateItem 创建物品。
**调用 CreateItemInstanceDefaultContainer.cpp:121**
```cpp
bool UDefaultContainer::CreateItem(FName ItemType,
TArray<FGuid>& NewItemIDs, int32 Count)
{
UItemRegistrySubsystem* Registry = GetRegistry(this);
if (!Registry) return false;
FItemDef Def = Registry->GetItemDef(ItemType);
if (Def.ItemName.IsEmpty()) return false;
for (int32 i = 0; i < Count; ++i)
{
TUniquePtr<FItemInstance> NewItem =
ItemFactory::CreateItemInstance(Def, ItemType);
if (!NewItem) return false;
NewItemIDs.Add(NewItem->ItemID);
this->InjectPayload(MoveTemp(NewItem));
}
return true;
}
```
**调用链:**
1. 外部通过 IItemContainer::CreateItem 请求创建。
2. UDefaultContainer::CreateItem 从注册表获取 FItemDef验证有效性。
3. 调用 ItemFactory::CreateItemInstance(Def, ItemType) → TUniquePtr<FItemInstance>。
4. 工厂内部:生成 GUID → 转换默认属性 → 合并覆盖属性 → 按需创建 Tracer。
5. 容器通过自身的 InjectPayload 将物品注入存储。
**不直接调用的场景:**
ItemFactory 不应被直接调用。所有物品创建都应通过容器的 CreateItem 接口,确保物品被正确注入容器并更新 Tracer。
## 用例
- `Plugins/Item/Source/Item/Public/ItemFactory.h:127-140` -- ItemFactory 类声明CreateItemInstance 公开方法 + ConvertDefaultProps / MergePropertyBag 私有方法)。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:6-24` -- CreateItemInstance 完整实现:生成 GUID → 转换默认属性 → 合并覆盖属性 → 按需创建 Tracer。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:26-48` -- ConvertDefaultProps 实现:遍历 FItemPropertyEntry 数组,从 ValuePicker 读取值并写入 FInstancedPropertyBag。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:51-84` -- MergePropertyBag 实现:将源属性包的所有属性合并到目标属性包(同名覆盖)。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:121` -- UDefaultContainer::CreateItem 调用 ItemFactory::CreateItemInstance 的唯一外部调用点。

View File

@@ -0,0 +1,79 @@
# UDefaultContainer
## 基本信息
- **类型**: UCLASS(BlueprintType, Blueprintable)
- **父类**: UObject, 实现 IItemContainer
- **源文件**: Plugins/Item/Source/Item/Public/DefaultContainer.h
- **模块**: Item
## 功能概述
IItemContainer的参考实现。将物品存储在TArray<TUniquePtr<FItemInstance>>中。实现所有接口方法查询、移动、创建、带类型检查的属性读写。Blueprintable允许设计师扩展。TODO计划切换为TMap以提升性能。
## 设计用意
默认的具体容器实现。简单的扁平数组存储。Blueprintable标记允许子类添加约束槽位、堆叠。为其他容器实现提供参考。
## 职责范围
完整的IItemContainer实现。处理物品存储、检索、移动、创建和属性访问。使用UItemRegistrySubsystem进行物品定义查找使用FItemViewFactory生成视图。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ItemContainer.h | #include (实现) | Plugins/Item/Source/Item/Public/ItemContainer.h |
| ItemRegistrySubsystem.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemRegistrySubsystem.h |
| ItemViewFactory.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemViewFactory.h |
| ItemFactory.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemFactory.h |
## 对外接口
UDefaultContainer 实现了 IItemContainer 的全部接口,对外暴露以下方法(全部继承自 IItemContainer此处列出的均为 override 实现):
**查询接口:**
- **GetItemViews() const** → TArray<FItemView>: 遍历内部 Items 数组,通过 UItemRegistrySubsystem 获取每个物品的 FItemDef调用 FItemViewFactory::CreateView 构造视图。注册表不可用时返回空数组DefaultContainer.cpp:54-66
- **GetItemViewByID(const FGuid&) const** → FItemView: 按 ID 查找。未找到返回空 FItemViewDefaultContainer.cpp:69-81
- **GetItemCount() const** → int32: 返回 Items.Num()DefaultContainer.cpp:84-86
**变更接口:**
- **MoveItem(const FGuid&, const TScriptInterface<IItemContainer>&)** → bool: 从内部数组取出物品MoveTemp调用 RemoveAt然后通过目标容器的 InjectPayload 注入。目标容器无效或物品不存在时返回 falseDefaultContainer.cpp:89-111
- **CreateItem(FName, TArray<FGuid>&, int32 Count = 1)** → bool: 从注册表获取 FItemDef检查 ItemName 非空,循环调用 ItemFactory::CreateItemInstance 创建指定数量物品,每个物品通过自身的 InjectPayload 添加。新物品 ID 追加到 NewItemIDsDefaultContainer.cpp:113-127
**属性接口CustomThunk 底层实现):**
- **Internal_GetPropertyRaw**: 按 ItemID 线性查找物品 → 从属性包中查找属性描述 → 类型兼容性检查后从属性包内存拷贝到蓝图引脚内存DefaultContainer.cpp:137-187
- **Internal_SetPropertyRaw**: 相同的查找和类型检查流程反向拷贝从蓝图引脚写入属性包DefaultContainer.cpp:189-230
**内部接口:**
- **InjectPayloadImpl(TUniquePtr<FItemInstance>)**: 接收物品所有权,简单的 Items.AddDefaultContainer.cpp:129-135
**存储:**
内部使用 TArray<TUniquePtr<FItemInstance>> Items 存储物品。当前为线性查找O(n)TODO 计划切换为 TMap 以提升性能。类标记为 BlueprintType 和 Blueprintable可在编辑器中创建 BP_DefaultContainer 子类。
## 使用方法
UDefaultContainer 作为 IItemContainer 的参考实现,可直接在 C++ 中实例化,或创建 Blueprint 子类(如 BP_DefaultContainer在编辑器中引用。
**在 C++ 中使用:**
容器需要绑定到 GameInstance 的 World 上下文才能获取注册表(通过 GetRegistry 辅助函数DefaultContainer.cpp:44-52。创建物品和查询都依赖注册表。
**在 Blueprint 中使用:**
BP_DefaultContainer 是 UDefaultContainer 的蓝图子类无额外逻辑可在编辑器中作为资产创建。BP_InventoryComp 在编辑器中持有 BP_DefaultContainer 的引用,通过它对容器进行操作:
- 调用 CreateItem 创建物品(需在 Authority 侧执行)。
- 调用 GetItemViews 获取物品列表用于 UI 显示。
- 调用 MoveItem 将物品移动到其他容器。
**实现自定义容器:**
继承 UDefaultContainer 并覆写虚方法即可创建自定义容器(如带槽位限制、堆叠功能的容器)。由于类标记了 Blueprintable也可以在 Blueprint 中继承。
**当前实现的限制:**
- 存储使用 TArray 线性查找O(n)TODO 计划切换为 TMap 以提升性能DefaultContainer.h:32
- 属性读写中的名称清理(点号→下划线)当前在 Internal_GetPropertyRaw / Internal_SetPropertyRaw 中处理TODO 计划提取到 NVI 层。
## 用例
- `Plugins/Item/Source/Item/Public/DefaultContainer.h:15-34` -- UDefaultContainer 类声明(继承 UObject + IItemContainer声明所有接口方法和 Items 成员)。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:44-52` -- GetRegistry 静态辅助函数,从 WorldContext 获取 UItemRegistrySubsystem。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:54-66` -- GetItemViews 实现:遍历 Items 并为每个物品调用 FItemViewFactory::CreateView。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:69-81` -- GetItemViewByID 实现。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:84-86` -- GetItemCount 实现。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:89-111` -- MoveItem 实现MoveTemp + RemoveAt + InjectPayload。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:113-127` -- CreateItem 实现:获取定义 → 工厂创建 → InjectPayload。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:129-134` -- InjectPayloadImpl 实现。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:137-187` -- Internal_GetPropertyRaw 实现(属性包读取 + 类型兼容性检查)。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:189-230` -- Internal_SetPropertyRaw 实现(属性包写入 + 类型兼容性检查)。
- `Document/Content/Blueprints/BP_DefaultContainer.md` -- BP_DefaultContainer 是 UDefaultContainer 的 Blueprint 子类,在编辑器中作为可引用资产使用。
- `Document/Content/Blueprints/BP_InventoryComp.md` -- BP_InventoryComp 持有 BP_DefaultContainer 引用并包装其操作。

View File

@@ -0,0 +1,63 @@
# UInternalItemProperty
## 基本信息
- **类型**: UCLASS (BlueprintFunctionLibrary)
- **父类**: UBlueprintFunctionLibrary
- **源文件**: Plugins/Item/Source/Item/Public/ItemFactory.h
- **模块**: Item
## 功能概述
静态函数库提供单一常量FName "Internal_ItemTracer"。用作属性包中UItemTracer对象的键。同时在C++和Blueprint中可用。
## 设计用意
集中的键注册表模式。防止硬编码字符串在代码库中出现漂移。被ItemFactory::CreateItemInstance和IItemContainer::InjectPayload引用。
## 职责范围
提供物品Tracer的标准属性包键。单一职责 - 名称常量。
## 项目内依赖
(无项目内依赖)
## 对外接口
UInternalItemProperty 是 BlueprintFunctionLibrary对外仅提供一个静态方法
- **ItemTracer()** (BlueprintPure, Category = "Constants|Names") → FName: 返回常量 FName("Internal_ItemTracer")。这是物品属性包中 UItemTracer 对象的键名。
此方法在 Blueprint 和 C++ 中均可调用。其设计目的是集中管理内部键名,避免字符串散落在代码各处。
**调用方视角:**
调用方通常不需要直接使用此类。它被 ItemFactory 和 IItemContainer::InjectPayload 内部使用:
- 创建时ItemFactory::CreateItemInstance 使用此键向 ItemData 添加 UItemTracer 属性ItemFactory.cpp:19,21
- 注入时IItemContainer::InjectPayload 使用此键查找 Tracer 并更新其位置ItemContainer.cpp:49
## 使用方法
UInternalItemProperty 是一个集中管理的键名注册表。调用方通常在需要访问物品属性包中的 Tracer 时使用。
**在 C++ 中使用ItemFactory.cpp:19,21**
```cpp
// 添加 Tracer 属性
Instance->ItemData.AddProperty(UInternalItemProperty::ItemTracer(),
EPropertyBagPropertyType::Object, UItemTracer::StaticClass());
// 设置 Tracer 值
Instance->ItemData.SetValueObject(
UInternalItemProperty::ItemTracer(), NewTracer);
```
**在 InjectPayload 中使用ItemContainer.cpp:49**
```cpp
const FName PropName = UInternalItemProperty::ItemTracer();
if (const FPropertyBagPropertyDesc* Desc =
Payload->ItemData.FindPropertyDescByName(PropName))
{
// ... 获取 Tracer 并更新位置
}
```
**在 Blueprint 中使用:**
作为 BlueprintFunctionLibrary 的 BlueprintPure 函数,在 Blueprint 中可以直接调用 ItemTracer() 节点获取 FName 常量,用于从物品的属性包中查找 Tracer 对象。
## 用例
- `Plugins/Item/Source/Item/Public/ItemFactory.h:13-24` -- UInternalItemProperty 类定义ItemTracer() 返回 static const FName("Internal_ItemTracer")。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:19` -- CreateItemInstance 调用 UInternalItemProperty::ItemTracer() 作为键名向属性包添加 Object 类型属性。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:21` -- CreateItemInstance 调用 UInternalItemProperty::ItemTracer() 作为键名向属性包设置 Tracer 对象值。
- `Plugins/Item/Source/Item/Private/ItemContainer.cpp:49` -- InjectPayload NVI 调用 UInternalItemProperty::ItemTracer() 作为键名从属性包查找 Tracer 属性描述。

View File

@@ -0,0 +1,83 @@
# UItemRegistrySettings
## 基本信息
- **类型**: UCLASS(Config=Game, DefaultConfig)
- **父类**: UDeveloperSettings
- **源文件**: Plugins/Item/Source/Item/Public/ItemRegistrySettings.h
- **模块**: Item
## 功能概述
Item插件的项目设置类。可在编辑器中的 Project Settings > Game > Item Registry Settings 下配置。包含DataTable软引用和视图策略类软引用的TMap。软引用避免启动时急切加载。
## 设计用意
设计时配置的契约。UDeveloperSettings将配置存储在DefaultGame.ini中。软引用避免在启动时加载所有内容。标准的UE项目设置模式。
## 职责范围
配置数据容器。将名称映射到DataTable/策略类资产。由UItemRegistrySubsystem::Initialize在游戏启动时读取。无运行时逻辑。
## 项目内依赖
(无项目内依赖)
## 对外接口
UItemRegistrySettings 是 UDeveloperSettings 子类Config=Game, DefaultConfig作为项目配置容器本身没有可调用的方法仅有默认构造函数。外部通过以下两个 UPROPERTY 读取配置:
- **ItemDataTables** (TMap<FName, TSoftObjectPtr<UDataTable>>): 物品 DataTable 的映射表。键为逻辑名称(如 "Default"),值为 DataTable 资产的软引用路径。在编辑器中通过 Project Settings > Game > Item Registry Settings 配置。
- **ViewStrategies** (TMap<FName, TSoftClassPtr<UObject>>): 视图策略类的映射表。键为属性名称(对应 IItemViewStrategy::GetPropertyName值为实现了 ItemViewStrategy 接口的 Blueprint 类软引用。
**读取方式:**
由 UItemRegistrySubsystem::Initialize 通过 GetDefault<UItemRegistrySettings>() 获取单例,遍历两个 TMap 调用 LoadSynchronous 加载软引用并注册ItemRegistrySubsystem.cpp:58-80
**配置存储:**
标记 Config=Game 使这些字段存储在 DefaultGame.ini 中。设计师不需要修改代码,只需在项目设置编辑器中配置 DataTable 和策略类的映射关系即可。软引用TSoftObjectPtr / TSoftClassPtr避免在游戏启动时急切加载所有资产。
## 使用方法
UItemRegistrySettings 是纯配置数据容器,由研发人员在项目设置中配置。
**配置路径:**
编辑器中Project Settings > Game > Item Registry Settings由 meta = (DisplayName = "Item Registry Settings") 定义)。
**配置 ItemDataTables**
在 "General" 分类下,添加键值对:
-DataTable 的逻辑名称(如 "Items"、"Equipment")。
- 值:指向 DataTable 资产(其行结构体为 FItemDef的软引用。
**配置 ViewStrategies**
在 "Strategies" 分类下,添加键值对:
- 键:属性名称(与 IItemViewStrategy::GetPropertyName() 返回值一致,如 "Attribute_Health")。
- 值:指向实现了 ItemViewStrategy 接口的 Blueprint 类的软引用。
**运行时加载ItemRegistrySubsystem.cpp:58-80**
```cpp
void UItemRegistrySubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
const UItemRegistrySettings* Settings =
GetDefault<UItemRegistrySettings>();
for (const auto& Pair : Settings->ItemDataTables)
{
if (UDataTable* LoadedTable = Pair.Value.LoadSynchronous())
RegisterDataTable(Pair.Key, LoadedTable);
}
for (const auto& Pair : Settings->ViewStrategies)
{
if (UClass* StrategyClass = Pair.Value.LoadSynchronous())
{
TScriptInterface<IItemViewStrategy> NewStrategy =
NewObject<UObject>(this, StrategyClass);
RegisterViewStrategy(Pair.Key, NewStrategy);
}
}
}
```
**配置存储位置:**
标记 Config=Game 使配置写入 DefaultGame.ini。多个平台可共享同一配置也可按平台覆写。
## 用例
- `Plugins/Item/Source/Item/Public/ItemRegistrySettings.h:13-23` -- UItemRegistrySettings 类声明ItemDataTables + ViewStrategies 两个 UPROPERTY + 构造函数)。
- `Plugins/Item/Source/Item/Private/ItemRegistrySettings.cpp:6-8` -- 构造函数实现(空实现,所有工作在 UPROPERTY 初始化器中完成)。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:59` -- Initialize 中通过 GetDefault<UItemRegistrySettings>() 获取配置单例。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:62-68` -- Initialize 中遍历 Settings->ItemDataTablesLoadSynchronous 每个软引用并注册。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:70-78` -- Initialize 中遍历 Settings->ViewStrategiesLoadSynchronous 每个类NewObject 实例化策略并注册。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:79-80` -- Initialize 完成后通过 UE_LOG 输出注册的 DataTable 和策略数量。

View File

@@ -0,0 +1,95 @@
# UItemRegistrySubsystem
## 基本信息
- **类型**: UCLASS
- **父类**: UGameInstanceSubsystem
- **源文件**: Plugins/Item/Source/Item/Public/ItemRegistrySubsystem.h
- **模块**: Item
## 功能概述
GameInstance作用域的中心注册表。管理物品定义DataTable的注册以及按属性名称注册视图策略。Initialize()从UItemRegistrySettings自动加载。提供GetItemDef和GetViewStrategy查找。
## 设计用意
物品元数据的运行时服务定位器。UGameInstanceSubsystem确保全局生命周期。从项目设置自动加载设计师配置无需代码变更。提供动态内容的运行时注册API。
## 职责范围
物品定义和视图策略的中心查找。注册/注销DataTable和策略。不创建物品或视图委托给工厂类
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ItemFactory.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemFactory.h |
| ItemRegistrySettings.h | #include (在.cpp中) | Plugins/Item/Source/Item/Public/ItemRegistrySettings.h |
## 对外接口
UItemRegistrySubsystem 是 UGameInstanceSubsystem通过 GetGameInstance()->GetSubsystem<UItemRegistrySubsystem>() 获取(参见 DefaultContainer.cpp:44-52 的静态辅助函数 GetRegistry
**DataTable 管理BlueprintCallable**
- **RegisterDataTable(FName Key, UDataTable*)**: 以指定键名注册一个 DataTable。初始化时自动从 UItemRegistrySettings::ItemDataTables 加载ItemRegistrySubsystem.cpp:8-13
- **UnregisterDataTable(FName Key)**: 移除指定键名的 DataTableItemRegistrySubsystem.cpp:15-17
**视图策略管理BlueprintCallable**
- **RegisterViewStrategy(FName PropertyName, const TScriptInterface<IItemViewStrategy>&)**: 为指定属性名注册视图策略。初始化时自动从 UItemRegistrySettings::ViewStrategies 加载并实例化ItemRegistrySubsystem.cpp:20-25
- **UnregisterViewStrategy(FName PropertyName)**: 移除指定属性名的视图策略ItemRegistrySubsystem.cpp:27-29
**查询接口BlueprintCallable**
- **GetItemDef(FName ItemType) const** → FItemDef: 遍历所有已注册的 DataTable通过 FindRow 查找指定类型名称的物品定义。返回 FItemDef 值副本。未找到时返回默认构造的空 FItemDef其 ItemName 为空调用方可据此判断结果有效性ItemRegistrySubsystem.cpp:32-42
- **GetViewStrategy(FName PropertyName) const** → TScriptInterface<IItemViewStrategy>: 从策略映射中查找指定属性名的策略。未找到时返回空的 TScriptInterfaceItemRegistrySubsystem.cpp:45-52
**生命周期:**
- **Initialize(FSubsystemCollectionBase&)**: GameInstance 子系统初始化时自动调用。读取 UItemRegistrySettings 单例,加载所有软引用的 DataTable 和策略类并注册。加载完成后输出日志ItemRegistrySubsystem.cpp:54-81
## 使用方法
UItemRegistrySubsystem 作为 GameInstance 子系统,在游戏启动时自动初始化。访问方式如下。
**获取注册表DefaultContainer.cpp:44-52**
```cpp
static UItemRegistrySubsystem* GetRegistry(const UObject* WorldContext)
{
if (!WorldContext) return nullptr;
UWorld* World = WorldContext->GetWorld();
if (!World) return nullptr;
UGameInstance* GI = World->GetGameInstance();
if (!GI) return nullptr;
return GI->GetSubsystem<UItemRegistrySubsystem>();
}
```
在任何需要的地方调用此辅助函数即可获取注册表单例。
**查询物品定义:**
```cpp
UItemRegistrySubsystem* Registry = GetRegistry(this);
FItemDef Def = Registry->GetItemDef(ItemType);
if (Def.ItemName.IsEmpty()) { /* 未找到 */ }
```
**查询视图策略:**
```cpp
// ItemViewFactory.cpp:70
TScriptInterface<IItemViewStrategy> Strategy =
Registry->GetViewStrategy(Desc.Name);
if (!Strategy.GetInterface()) continue; // 策略不存在则跳过
```
**自动初始化流程ItemRegistrySubsystem.cpp:54-81**
1. 游戏启动时GameInstance 创建所有子系统。
2. Initialize 被调用,读取 UItemRegistrySettings 单例。
3. 遍历 ItemDataTablesLoadSynchronous 每个软引用并注册。
4. 遍历 ViewStrategiesLoadSynchronous 每个类NewObject 实例化并注册。
5. 输出日志报告注册的 DataTable 和策略数量。
**运行时动态注册:**
除了自动初始化外,也可在运行时调用 RegisterDataTable / RegisterViewStrategy 动态注册(如 DLC 内容加载后补充物品定义)。
## 用例
- `Plugins/Item/Source/Item/Public/ItemRegistrySubsystem.h:15-43` -- UItemRegistrySubsystem 类声明(公开方法 + 私有 TMap 成员)。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:8-13` -- RegisterDataTable 实现。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:15-17` -- UnregisterDataTable 实现。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:20-25` -- RegisterViewStrategy 实现。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:27-29` -- UnregisterViewStrategy 实现。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:32-42` -- GetItemDef 实现:遍历所有 DataTable 查找物品定义。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:45-52` -- GetViewStrategy 实现:从 ViewStrategyMap 查找策略。
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:54-81` -- Initialize 实现:从 UItemRegistrySettings 加载配置并自动注册 DataTable 和策略。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:44-52` -- GetRegistry 辅助函数,通过 GI->GetSubsystem<UItemRegistrySubsystem>() 获取注册表。
- `Plugins/Item/Source/Item/Private/DefaultContainer.cpp:57,71,115` -- GetItemViews / GetItemViewByID / CreateItem 中获取注册表并调用 GetItemDef。
- `Plugins/Item/Source/Item/Private/ItemViewFactory.cpp:70` -- CreateView 中调用 Registry->GetViewStrategy(Desc.Name) 获取属性格式化策略。

View File

@@ -0,0 +1,87 @@
# UItemTracer
## 基本信息
- **类型**: UCLASS(BlueprintType)
- **父类**: UObject
- **源文件**: Plugins/Item/Source/Item/Public/ItemFactory.h
- **模块**: Item
## 功能概述
嵌入在FItemInstance属性包中的轻量级追踪对象。存储当前容器位置并暴露OnItemMoved多播委托。提供Blueprint查询函数用于查询物品位置。
## 设计用意
解决"物品在哪"的问题无需全局注册表。存在于物品的属性包中随物品迁移。由IItemContainer::InjectPayload NVI包装自动更新。OnItemMoved委托使UI/任务系统能响应物品移动事件。
## 职责范围
追踪和广播物品位置变化。由InjectPayload在插入时更新位置。UPROPERTY位置使用TObjectPtr非强引用避免泄漏。
## 项目内依赖
(无项目内依赖)
## 对外接口
UItemTracer 是 BlueprintType嵌入在物品的属性包中键名为 Internal_ItemTracer随物品在容器间迁移。外部代码通过属性包获取 Tracer 引用后,可调用以下接口:
**Blueprint 可调用:**
- **GetItemLocation()** (BlueprintPure, BlueprintAuthorityOnly) → const UObject*: 返回物品当前所在的 UObject 位置。在 InjectPayload NVI 中此位置被设置为容器对象Cast<UObject>(this))。返回值可能为空(物品尚未被注入任何容器)。
- **FindActorInOuterChain()** (BlueprintPure, BlueprintAuthorityOnly) → const AActor*: 从物品位置出发,沿 Outer 链向上查找第一个 AActor。如果物品容器是 Actor 的组件/子对象,此方法可追溯到所属 Actor。找不到则返回 nullptr。
**委托:**
- **OnItemMoved** (FOnItemMoved, BlueprintAssignable): DYNAMIC_MULTICAST_DELEGATE_OneParam 类型的动态多播委托。在 IItemContainer::InjectPayload 中被广播(参数为 Tracer 自身。UI 或任务系统可绑定此委托以响应物品移动事件。
**C++ 专用:**
- **SetItemLocation(UObject*)**: 设置 Tracer 的位置引用。不暴露到 Blueprint。仅由 IItemContainer::InjectPayload NVI 调用ItemContainer.cpp:59确保位置更新与移动广播的原子性。
## 使用方法
UItemTracer 由 ItemFactory 创建并嵌入物品属性包,由 InjectPayload 自动更新。外部代码(如 Blueprint UI从物品属性包中获取 Tracer 引用后使用。
**创建ItemFactory.cpp:17-22**
```cpp
if (ItemDef.bHasTracer)
{
Instance->ItemData.AddProperty(UInternalItemProperty::ItemTracer(),
EPropertyBagPropertyType::Object, UItemTracer::StaticClass());
UItemTracer* NewTracer = NewObject<UItemTracer>();
Instance->ItemData.SetValueObject(
UInternalItemProperty::ItemTracer(), NewTracer);
}
```
**位置更新ItemContainer.cpp:45-67 - InjectPayload NVI**
```cpp
void IItemContainer::InjectPayload(TUniquePtr<struct FItemInstance> Payload)
{
if (!Payload.IsValid()) return;
const FName PropName = UInternalItemProperty::ItemTracer();
if (const FPropertyBagPropertyDesc* Desc =
Payload->ItemData.FindPropertyDescByName(PropName))
{
if (UObject* SelfAsObject = Cast<UObject>(this))
{
TValueOrError<UObject*, EPropertyBagResult> Result =
Payload->ItemData.GetValueObject(PropName);
if (Result.HasValue())
{
if (UItemTracer* Tracer = Cast<UItemTracer>(Result.GetValue()))
{
Tracer->SetItemLocation(SelfAsObject);
Tracer->OnItemMoved.Broadcast(Tracer); // 广播移动事件
}
}
}
}
InjectPayloadImpl(MoveTemp(Payload));
}
```
**在 Blueprint 中绑定移动事件:**
UI 或任务系统通过获取物品属性包中的 Internal_ItemTracer 对象,绑定其 OnItemMoved 委托来响应物品移动。当容器调用 InjectPayload 时,委托被广播,绑定的监听方收到通知。
**查询位置:**
在 Blueprint 中调用 GetItemLocation 获取物品当前所在 UObject或调用 FindActorInOuterChain 追溯到所属 Actor。
## 用例
- `Plugins/Item/Source/Item/Public/ItemFactory.h:27` -- FOnItemMoved 委托类型声明DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam
- `Plugins/Item/Source/Item/Public/ItemFactory.h:29-57` -- UItemTracer 类定义OnItemMoved 成员 + GetItemLocation / FindActorInOuterChain / SetItemLocation 方法)。
- `Plugins/Item/Source/Item/Private/ItemFactory.cpp:17-22` -- CreateItemInstance 中当 bHasTracer 为 true 时创建 UItemTracer 并添加到属性包。
- `Plugins/Item/Source/Item/Private/ItemContainer.cpp:49` -- InjectPayload NVI 中通过 UInternalItemProperty::ItemTracer() 键名查找 Tracer。
- `Plugins/Item/Source/Item/Private/ItemContainer.cpp:57-60` -- InjectPayload 中 Cast 到 UItemTracer调用 SetItemLocation 更新位置Broadcast OnItemMoved 通知监听方。

View File

@@ -0,0 +1,27 @@
# Item 插件依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| ItemContainer.cpp | #include | ItemFactory.h | ItemFactory |
| DefaultContainer.cpp | #include | ItemRegistrySubsystem.h | UItemRegistrySubsystem |
| DefaultContainer.cpp | #include | ItemViewFactory.h | FItemViewFactory |
| DefaultContainer.cpp | #include | ItemFactory.h | ItemFactory |
| DefaultContainer.h | #include | ItemContainer.h | IItemContainer |
| Inventory.h | #include | ItemContainer.h | IItemContainer |
| ItemViewFactory.cpp | #include | ItemRegistrySubsystem.h | UItemRegistrySubsystem |
| ItemViewFactory.cpp | #include | ItemFactory.h | ItemFactory |
| ItemViewFactory.cpp | #include | ItemContainer.h | IItemContainer |
| ItemViewFactory.cpp | #include | ItemViewStrategy.h | IItemViewStrategy |
| ItemViewFactory.h | forward-declare | (header-only) | FItemView, FItemInstance, FItemDef, UItemRegistrySubsystem |
| ItemRegistrySubsystem.cpp | #include | ItemFactory.h | ItemFactory |
| ItemRegistrySubsystem.cpp | #include | ItemRegistrySettings.h | UItemRegistrySettings |
## 关键依赖链
1. Item Creation: UItemRegistrySettings -> UItemRegistrySubsystem::Initialize -> ItemFactory::CreateItemInstance -> FItemInstance -> IItemContainer::InjectPayload -> UItemTracer
2. Item View: UDefaultContainer::GetItemViews -> FItemViewFactory::CreateView -> UItemRegistrySubsystem::GetViewStrategy -> IItemViewStrategy::GetPropertyText -> FItemView
3. Item Movement: IItemContainer::MoveItem -> InjectPayload (NVI) -> UItemTracer::SetItemLocation -> OnItemMoved delegate
4. Blueprint Inventory: IInventory::RequestMoveItem -> IInventory::ReceiveItem -> IItemContainer::InjectPayload
5. Item Lookup: UDefaultContainer::CreateItem -> UItemRegistrySubsystem::GetItemDef -> FItemDef -> ItemFactory::CreateItemInstance

121
README.md
View File

@@ -1,2 +1,121 @@
# loneseDocument # lonese 项目文档
> **维护者必读**[HOWTO_REGENERATE_DOCS.md](HOWTO_REGENERATE_DOCS.md) — 文档是怎么生成的、踩过什么坑、如何复现。修改本文档之前请先看这篇。
## 文档索引
### 一、Source/lonese/4 单位)
| 单位 | 文件 | 类型 | 父类 |
|------|------|------|------|
| AMyActor | [AMyActor.md](Source/lonese/AMyActor.md) | UCLASS | AActor |
| AMyCharacter | [AMyCharacter.md](Source/lonese/AMyCharacter.md) | UCLASS | ACharacter |
| ACameraPawn | [ACameraPawn.md](Source/lonese/ACameraPawn.md) | UCLASS | APawn |
| AMyPlayerController | [AMyPlayerController.md](Source/lonese/AMyPlayerController.md) | UCLASS | APlayerController |
依赖关系:[Source/lonese/_relationships.md](Source/lonese/_relationships.md)
### 二、Plugins/Item/14 单位)
| 单位 | 文件 | 类型 | 父类 |
|------|------|------|------|
| FItemView | [FItemView.md](Plugins/Item/FItemView.md) | USTRUCT | — |
| IItemContainer | [IItemContainer.md](Plugins/Item/IItemContainer.md) | UINTERFACE | UInterface |
| IInventory | [IInventory.md](Plugins/Item/IInventory.md) | UINTERFACE | UInterface |
| FItemInstance | [FItemInstance.md](Plugins/Item/FItemInstance.md) | USTRUCT | — |
| FItemPropertyEntry | [FItemPropertyEntry.md](Plugins/Item/FItemPropertyEntry.md) | USTRUCT | — |
| FItemDef | [FItemDef.md](Plugins/Item/FItemDef.md) | USTRUCT | FTableRowBase |
| UItemTracer | [UItemTracer.md](Plugins/Item/UItemTracer.md) | UCLASS | UObject |
| UInternalItemProperty | [UInternalItemProperty.md](Plugins/Item/UInternalItemProperty.md) | UCLASS | UBlueprintFunctionLibrary |
| ItemFactory | [ItemFactory.md](Plugins/Item/ItemFactory.md) | C++ 静态类 | — |
| IItemViewStrategy | [IItemViewStrategy.md](Plugins/Item/IItemViewStrategy.md) | UINTERFACE | UInterface |
| FItemViewFactory | [FItemViewFactory.md](Plugins/Item/FItemViewFactory.md) | C++ 静态类 | — |
| UDefaultContainer | [UDefaultContainer.md](Plugins/Item/UDefaultContainer.md) | UCLASS | UObject |
| UItemRegistrySubsystem | [UItemRegistrySubsystem.md](Plugins/Item/UItemRegistrySubsystem.md) | UCLASS | UGameInstanceSubsystem |
| UItemRegistrySettings | [UItemRegistrySettings.md](Plugins/Item/UItemRegistrySettings.md) | UCLASS | UDeveloperSettings |
依赖关系:[Plugins/Item/_relationships.md](Plugins/Item/_relationships.md)
### 三、Plugins/Dialog/10 单位)
| 单位 | 文件 | 类型 | 父类 |
|------|------|------|------|
| FDialogPresentationScriptData | [FDialogPresentationScriptData.md](Plugins/Dialog/FDialogPresentationScriptData.md) | USTRUCT | FTableRowBase |
| FDialogTextData | [FDialogTextData.md](Plugins/Dialog/FDialogTextData.md) | USTRUCT | FTableRowBase |
| EDialogPresentationScriptCommandParamType | [EDialogPresentationScriptCommandParamType.md](Plugins/Dialog/EDialogPresentationScriptCommandParamType.md) | UENUM | uint8 |
| FDialogPresentationScriptParam | [FDialogPresentationScriptParam.md](Plugins/Dialog/FDialogPresentationScriptParam.md) | USTRUCT | — |
| FDialogPresentationScriptCommand | [FDialogPresentationScriptCommand.md](Plugins/Dialog/FDialogPresentationScriptCommand.md) | USTRUCT | — |
| UPresentationJsonLibrary | [UPresentationJsonLibrary.md](Plugins/Dialog/UPresentationJsonLibrary.md) | UCLASS | UBlueprintFunctionLibrary |
| IPresentationStrategyPlugin | [IPresentationStrategyPlugin.md](Plugins/Dialog/IPresentationStrategyPlugin.md) | UINTERFACE | UInterface |
| IPresentationScriptExecutor | [IPresentationScriptExecutor.md](Plugins/Dialog/IPresentationScriptExecutor.md) | UINTERFACE | UInterface |
| FHelperArray | [FHelperArray.md](Plugins/Dialog/FHelperArray.md) | USTRUCT | — |
| UDialogPresentationSubsystem | [UDialogPresentationSubsystem.md](Plugins/Dialog/UDialogPresentationSubsystem.md) | UCLASS | UGameInstanceSubsystem |
依赖关系:[Plugins/Dialog/_relationships.md](Plugins/Dialog/_relationships.md)
### 四、Plugins/CharacterControl/13 单位)
| 单位 | 文件 | 类型 | 父类 |
|------|------|------|------|
| FCommandMeta | [FCommandMeta.md](Plugins/CharacterControl/FCommandMeta.md) | USTRUCT | — |
| FContinuousPayload | [FContinuousPayload.md](Plugins/CharacterControl/FContinuousPayload.md) | USTRUCT | — |
| FCommandPacket | [FCommandPacket.md](Plugins/CharacterControl/FCommandPacket.md) | USTRUCT | — |
| FDiscreteMeta | [FDiscreteMeta.md](Plugins/CharacterControl/FDiscreteMeta.md) | USTRUCT | — |
| FEndpointState | [FEndpointState.md](Plugins/CharacterControl/FEndpointState.md) | USTRUCT | — |
| UEndpointDispatcher | [UEndpointDispatcher.md](Plugins/CharacterControl/UEndpointDispatcher.md) | UCLASS | UObject |
| ICommandEndpoint | [ICommandEndpoint.md](Plugins/CharacterControl/ICommandEndpoint.md) | UINTERFACE | UInterface |
| UCommandRouter | [UCommandRouter.md](Plugins/CharacterControl/UCommandRouter.md) | UCLASS | UObject |
| UCommandRouterComponent | [UCommandRouterComponent.md](Plugins/CharacterControl/UCommandRouterComponent.md) | UCLASS | UActorComponent |
| UEndpointComponent | [UEndpointComponent.md](Plugins/CharacterControl/UEndpointComponent.md) | UCLASS | UActorComponent |
| UCommandInputComponent | [UCommandInputComponent.md](Plugins/CharacterControl/UCommandInputComponent.md) | UCLASS | UEndpointComponent |
| FInputCommand | [FInputCommand.md](Plugins/CharacterControl/FInputCommand.md) | USTRUCT | — |
| UInputCommandData | [UInputCommandData.md](Plugins/CharacterControl/UInputCommandData.md) | UCLASS | UDataAsset |
依赖关系:[Plugins/CharacterControl/_relationships.md](Plugins/CharacterControl/_relationships.md)
### 五、Content/Blueprints/15 单位)
| 单位 | 文件 | 类型 | 父类 |
|------|------|------|------|
| BP_TestChar | [BP_TestChar.md](Content/Blueprints/BP_TestChar.md) | Blueprint | AMyCharacter |
| BP_TestCtl | [BP_TestCtl.md](Content/Blueprints/BP_TestCtl.md) | Blueprint | AMyPlayerController |
| BP_CameraPawn | [BP_CameraPawn.md](Content/Blueprints/BP_CameraPawn.md) | Blueprint | ACameraPawn |
| BP_TestMode | [BP_TestMode.md](Content/Blueprints/BP_TestMode.md) | Blueprint | GameModeBase |
| BP_Hud | [BP_Hud.md](Content/Blueprints/BP_Hud.md) | Blueprint | HUD |
| BP_UniversalEndpointComp | [BP_UniversalEndpointComp.md](Content/Blueprints/BP_UniversalEndpointComp.md) | Blueprint | UEndpointComponent |
| BP_MoveInput | [BP_MoveInput.md](Content/Blueprints/BP_MoveInput.md) | Blueprint | UEndpointComponent |
| BP_ControllerComp | [BP_ControllerComp.md](Content/Blueprints/BP_ControllerComp.md) | Blueprint | UEndpointComponent |
| BP_SayHello | [BP_SayHello.md](Content/Blueprints/BP_SayHello.md) | Blueprint | UEndpointComponent |
| BP_DefaultContainer | [BP_DefaultContainer.md](Content/Blueprints/BP_DefaultContainer.md) | Blueprint | UDefaultContainer |
| BP_InventoryComp | [BP_InventoryComp.md](Content/Blueprints/BP_InventoryComp.md) | Blueprint | ActorComponent |
| BP_DropItemInvComp | [BP_DropItemInvComp.md](Content/Blueprints/BP_DropItemInvComp.md) | Blueprint | BP_InventoryComp |
| BP_Bomb | [BP_Bomb.md](Content/Blueprints/BP_Bomb.md) | Blueprint | Actor |
| WBP_TestUI | [WBP_TestUI.md](Content/Blueprints/WBP_TestUI.md) | Blueprint | UserWidget |
| WBP_InventoryView | [WBP_InventoryView.md](Content/Blueprints/WBP_InventoryView.md) | Blueprint | UserWidget |
依赖关系:[Content/Blueprints/_relationships.md](Content/Blueprints/_relationships.md)
---
## 文档模板说明
每个单位的文档包含以下章节:
1. **基本信息** — 类型、父类、源文件、模块
2. **功能概述** — 一句话概括单位的用途和使用场景
3. **设计用意** — 为什么这样设计,解决什么问题,在系统中的定位
4. **职责范围** — 自然语言描述该单位承担的工作范围
5. **项目内依赖** — 文件级依赖表格
6. **对外接口** — 从外部调用者视角描述的接口说明
7. **使用方法** — 引用项目真实代码位置说明典型用法
8. **用例** — 项目中实际使用本单位的文件和上下文
每个模块目录下还有一个 `_relationships.md` 文件,描述该模块内文件间的引用关系和关键数据/控制流依赖链。
---
## 统计
- **总单位数**: 564 + 14 + 10 + 13 + 15
- **总文档文件**: 62含 5 个 `_relationships.md` + 1 个 `README.md`
- **覆盖模块**: 5Source/lonese + 3 插件 + Content/Blueprints

View File

@@ -0,0 +1,60 @@
# ACameraPawn
## 基本信息
- **类型**: UCLASS
- **父类**: APawn
- **源文件**: Source/lonese/CameraPawn.h
- **模块**: lonese
## 功能概述
ACameraPawn 是使用 SpringArmComponent + CameraComponent 构建的摄像机 Pawn挂接到 FollowTarget 的 Actor 上。支持缩放(臂长范围 300-3000、旋转绕目标 Yaw 旋转)、平移(相对偏移,限制在 ±800 范围内和重置恢复初始状态。BeginPlay 将玩家控制器的视图切换至自身并挂接到目标。
## 设计用意
将所有摄像机行为封装在独立 Pawn 中与角色类完全解耦实现独立创作、独立测试和灵活替换。FollowTarget + AttachToActor 机制使其可复用于任意 Actor。AMyCharacter 上的 TSubclassOf 属性允许在蓝图中注入自定义摄像机类型。
## 职责范围
摄像机变换管理,包括缩放、旋转、平移和重置操作,以及视口目标设置。不处理角色移动,也不直接绑定输入(输入由 AMyCharacter 转发)。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| SpringArmComponent.h | #include(在 .cpp 中) | Engine |
| CameraComponent.h | #include(在 .cpp 中) | Engine |
## 对外接口
**C++ / BlueprintUFUNCTION全部 BlueprintCallable**
- `CameraZoom(FInputActionValue)` — 缩放控制。通过调整 SpringArmComponent 的 TargetArmLength 在 MinArmLength(300) 到 MaxArmLength(3000) 之间缩放,速度由 ZoomSpeed(10.0) 控制。
- `CameraRotate(FInputActionValue)` — 旋转控制。通过修改 SpringArmComponent 绕 FollowTarget 的相对 Yaw 旋转实现绕目标旋转,速度由 RotateSpeed(2.0) 控制。
- `CameraMove(FInputActionValue)` — 平移控制。通过修改 SpringArmComponent 的 SocketOffset 实现摄像机偏移,限制在 ±CameraMoveClamp(800.0) 范围内,速度由 MoveSpeed(10.0) 控制。
- `CameraReset(FInputActionValue)` — 重置摄像机。将 SpringArmComponent 的 TargetArmLength 和 SocketOffset 恢复至初始值 (InitialArmLength, 零偏移),旋转恢复至 InitialRotation。
**C++ / BlueprintUPROPERTY全部 BlueprintReadOnly**
- `SpringArmComponent` (USpringArmComponent*) — 弹簧臂组件,控制摄像机臂长和延迟。
- `CameraComponent` (UCameraComponent*) — 摄像机组件。
- `FollowTarget` (APawn*, VisibleInstanceOnly) — 跟随目标 PawnTick 中更新 SpringArm 位置跟踪。
- `InitialRotation` (FRotator, EditDefaultsOnly) — 初始旋转,默认 (-60, 0, 0)。
- `InitialArmLength` (float, EditDefaultsOnly) — 初始臂长,默认 1200。
- `RotateSpeed` / `ZoomSpeed` / `MoveSpeed` (float, EditDefaultsOnly) — 旋转/缩放/平移速度。
- `MaxArmLength` / `MinArmLength` (float, EditDefaultsOnly) — 最大/最小臂长,默认 3000/300。
- `CameraMoveClamp` (double, EditDefaultsOnly) — 平移限制范围,默认 800。
## 使用方法
- 类声明:`Source/lonese/CameraPawn.h:10-62`
- 构造函数:`Source/lonese/CameraPawn.cpp:58-73` — 创建 SpringArmComponent 和 CameraComponent配置臂长、延迟、旋转等默认值。
- BeginPlay`Source/lonese/CameraPawn.cpp:76-96` — 将控制器视图切换至自身,若 FollowTarget 已设置则 AttachToActor。
- Tick`Source/lonese/CameraPawn.cpp:99-103` — 调用 Super无额外逻辑。
- CameraZoom 实现:`Source/lonese/CameraPawn.cpp:10-14`
- CameraRotate 实现:`Source/lonese/CameraPawn.cpp:17-28`
- CameraMove 实现:`Source/lonese/CameraPawn.cpp:31-40`
- CameraReset 实现:`Source/lonese/CameraPawn.cpp:43-49`
- 由 AMyCharacter 生成并配置 FollowTarget`Source/lonese/MyCharacter.cpp:111-132`
## 用例
- **BP_CameraPawn** — 项目摄像机蓝图,继承自 ACameraPawn在蓝图层配置摄像机默认参数。
- `Document/Content/Blueprints/BP_CameraPawn.md:5`(父类为 ACameraPawn
- `Document/Content/Blueprints/_relationships.md:9`
- **BP_TestChar** — 通过 AMyCharacter 的 CameraActorClass 属性生成 ACameraPawn 实例。
- `Document/Content/Blueprints/BP_TestChar.md:22`(间接依赖 ACameraPawn
- `Document/Content/Blueprints/_relationships.md:27`CameraActorClass = BP_CameraPawn → ACameraPawn
- **BP_ControllerComp** — 摄像机控制端点组件,通过指令系统调用 ACameraPawn 的 SpringArm 变换能力。
- `Document/Content/Blueprints/BP_ControllerComp.md:13,16`(依赖 ACameraPawn 的摄像机变换能力)

35
Source/lonese/AMyActor.md Normal file
View File

@@ -0,0 +1,35 @@
# AMyActor
## 基本信息
- **类型**: UCLASS
- **父类**: AActor
- **源文件**: Source/lonese/MyActor.h
- **模块**: lonese
## 功能概述
AMyActor 是项目中的基础占位 Actor 类仅包含标准生命周期函数BeginPlay/Tick无任何自定义逻辑。构造函数设置 bCanEverTick 为 true两个覆写函数均仅调用 Super。
## 设计用意
由 UE 类向导生成的样板起步类,作为骨架 Actor 准备后续自定义。当前阶段尚未解决具体的游戏性问题,仅作为新 Actor 类型的可扩展基类存在。
## 职责范围
提供 BeginPlay 和 Tick 生命周期钩子,不含自定义逻辑。可作为新 Actor 类型的扩展基类使用。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
|(无)| — | 不依赖任何项目内类,仅引用引擎头文件 |
## 对外接口
AMyActor 未定义任何 UFUNCTION 或 UPROPERTY对外暴露的能力全部来自 AActor 基类。构造函数将 bCanEverTick 设为 true。由于不包含自定义逻辑C++ 和 Blueprint 调用者无法通过该类调用任何项目特定的方法。
## 使用方法
作为新 Actor 类型的 C++ 基类或 Blueprint 父类使用。因其仅包含空覆写的 BeginPlay/Tick 且无自定义成员,适合用作任意新 Actor 的起点。
- 类声明:`Source/lonese/MyActor.h:10-26` — 继承自 AActor无自定义 UFUNCTION/UPROPERTY。
- 构造函数:`Source/lonese/MyActor.cpp:7-12` — 设置 `bCanEverTick = true`
- BeginPlay 覆写:`Source/lonese/MyActor.cpp:15-19` — 仅调用 `Super::BeginPlay()`
- Tick 覆写:`Source/lonese/MyActor.cpp:22-25` — 仅调用 `Super::Tick(DeltaTime)`
## 用例
项目中无实际调用方。AMyActor 未被任何项目 C++ 类或 Blueprint 继承或引用Plugins/UnrealClaude 中的引用为文档示例,非项目代码)。

View File

@@ -0,0 +1,57 @@
# AMyCharacter
## 基本信息
- **类型**: UCLASS
- **父类**: ACharacter
- **源文件**: Source/lonese/MyCharacter.h
- **模块**: lonese
## 功能概述
AMyCharacter 是玩家可控的主角类,通过 Enhanced Input 支持 WASD 移动(世界空间坐标)、走/跑切换600/1200 速度)、蹲下切换,并将摄像机控制委托给 ACameraPawn。SetupPlayerInputComponent 添加输入映射上下文并绑定 8 个输入动作。摄像机输入转发至 BeginPlay 中生成的 CameraActor 子对象。
## 设计用意
作为项目主玩家角色,采用关注点分离策略:将摄像机逻辑委托给独立的 ACameraPawn自身专注于角色移动和输入绑定。使用 Enhanced Input 实现现代化的输入处理。通过状态查询方法将角色状态暴露给蓝图。
## 职责范围
角色移动控制(走、跑、蹲)、输入动作绑定、摄像机 Pawn 的生成与所有权管理。在 BeginPlay 中生成并持有 CameraActor将摄像机相关输入转发给该 Actor。不处理摄像机变换逻辑委托给 ACameraPawn
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| ACameraPawn | 创建/引用UPROPERTY 成员BeginPlay 中 Spawn | Source/lonese/CameraPawn.h |
| EnhancedInputSubsystems.h | #include(在 .cpp 中) | Engine |
| EnhancedInputComponent.h | #include(在 .cpp 中) | Engine |
## 对外接口
**C++ / BlueprintUFUNCTION全部 BlueprintCallable**
- `Move(FInputActionValue)` — 接收移动输入(世界空间坐标),根据走/跑状态设置速度为 600 或 1200。
- `BeginRun(FInputActionValue)` / `StopRun(FInputActionValue)` — 开始/停止跑步状态。跑步时 MaxWalkSpeed 设为 1200走路为 600。
- `BeginCrouch(FInputActionValue)` / `StopCrouch(FInputActionValue)` — 开始/停止蹲下,调用 ACharacter 的 Crouch/UnCrouch。
- `IsRunning() -> bool` — 查询当前是否处于跑步状态。
- `IsCrouching() -> bool` — 查询当前是否处于蹲下状态。
**C++ / BlueprintUPROPERTYEditAnywhere/BlueprintReadOnly**
- `DefaultMapping` (UInputMappingContext*) — 默认输入映射上下文。
- `MoveAction` / `RunAction` / `CrouchAction` (UInputAction*) — 角色移动相关输入动作。
- `CameraMoveAction` / `CameraZoomAction` / `CameraResetAction` / `CameraRotateAction` (UInputAction*) — 摄像机相关输入动作(转发至 CameraActor
- `CameraActorClass` (TSubclassOf<ACameraPawn>) — 摄像机 Pawn 类BeginPlay 中以此类生成 CameraActor。
**C++private供内部输入绑定使用**
- `CameraZoom` / `CameraRotate` / `CameraMove` / `CameraReset` — 将输入值转发给当前 CameraActor 的对应方法。
## 使用方法
- 类声明:`Source/lonese/MyCharacter.h:10-74`
- 构造函数:`Source/lonese/MyCharacter.cpp:92-103` — 设置初始移动速度等默认属性。
- BeginPlay`Source/lonese/MyCharacter.cpp:106-133` — 生成 CameraActor通过 CameraActorClass 或默认 ACameraPawn 类),挂接到自身。
- SetupPlayerInputComponent`Source/lonese/MyCharacter.cpp:143-196` — 添加 DefaultMapping 到 Enhanced Input 子系统,绑定全部 8 个输入动作到对应方法。
- Tick`Source/lonese/MyCharacter.cpp:136-140` — 仅调用 Super。
- Move 实现:`Source/lonese/MyCharacter.cpp:12-29`
- BeginRun/StopRun 实现:`Source/lonese/MyCharacter.cpp:32-41`
- BeginCrouch/StopCrouch 实现:`Source/lonese/MyCharacter.cpp:43-56`
- 摄像机输入转发:`Source/lonese/MyCharacter.cpp:59-89`
## 用例
- **BP_TestChar** — 项目主角色蓝图,继承自 AMyCharacter。在 BeginPlay 中添加 CharacterControl 插件组件UCommandRouterComponent 及各端点组件)。配置 CameraActorClass 指向 BP_CameraPawn。
- 声明:`Document/Content/Blueprints/BP_TestChar.md:5`(父类为 AMyCharacter
- 关系:`Document/Content/Blueprints/_relationships.md:7`
- 蓝图继承类通过编辑 CameraActorClass 属性替换摄像机实现,通过配置 Input 类别下的 8 个 UInputAction 属性定制键位绑定。

View File

@@ -0,0 +1,45 @@
# AMyPlayerController
## 基本信息
- **类型**: UCLASS
- **父类**: APlayerController
- **源文件**: Source/lonese/MyPlayerController.h
- **模块**: lonese
## 功能概述
AMyPlayerController 是自定义 PlayerController提供三种行为GetOwnedPawnGetPawn 的蓝图包装、CalcCamera 覆写(调用 AActor::CalcCamera 以禁用内置视口目标混合、AcknowledgePossession 覆写(触发 OnClientPossessFinished BlueprintImplementableEvent供蓝图实现 Possess 后逻辑)。
## 设计用意
作为 C++ 与蓝图之间的桥梁。CalcCamera 覆写阻止默认摄像机系统干扰 ACameraPawn 的工作。OnClientPossessFinished 提供蓝图钩子,满足 Possess 后初始化(如 UI 设置、游戏性逻辑)的常见需求。
## 职责范围
PlayerController 的自定义层:禁用默认摄像机混合计算、提供蓝图 Possess 事件通知。不实现摄像机逻辑(委托给 ACameraPawn 通过视口目标机制处理)。
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
|(无)| — | 不依赖任何项目内类,仅引用引擎头文件 |
## 对外接口
**C++ / BlueprintUFUNCTION**
- `GetOwnedPawn() -> APawn*` (BlueprintCallable) — 返回当前控制的 Pawn。对 APlayerController::GetPawn() 的蓝图友好包装。
**BlueprintUFUNCTIONBlueprintImplementableEvent**
- `OnClientPossessFinished(APawn* P)` — 当 Possess 完成时由 C++ 层触发的蓝图事件。在 AcknowledgePossession 中调用,参数为被 Possess 的 Pawn。蓝图可覆写此事件实现 Possess 后初始化逻辑(如 UI 设置、状态重置)。
**C++(覆写基类)**
- `CalcCamera(float DeltaTime, FMinimalViewInfo& OutResult)` — 覆写 APlayerController::CalcCamera调用 `AActor::CalcCamera` 以禁用内置视口目标混合viewport blending确保 ACameraPawn 的视口目标设置不被干扰。
- `AcknowledgePossession(APawn* P)` — 覆写 APlayerController::AcknowledgePossession在调用 Super 后触发 OnClientPossessFinished 蓝图事件。
## 使用方法
- 类声明:`Source/lonese/MyPlayerController.h:13-29`
- GetOwnedPawn 实现:`Source/lonese/MyPlayerController.cpp:6-8` — 调用并返回 `GetPawn()`
- BeginPlay 实现:`Source/lonese/MyPlayerController.cpp:11-13` — 仅调用 Super。
- CalcCamera 覆写:`Source/lonese/MyPlayerController.cpp:16-18` — 调用 `AActor::CalcCamera` 禁用摄像机混合。
- AcknowledgePossession 覆写:`Source/lonese/MyPlayerController.cpp:21-24` — Super 后触发 OnClientPossessFinished。
## 用例
- **BP_TestCtl** — 项目玩家控制器蓝图,继承自 AMyPlayerController在蓝图中实现 OnClientPossessFinished 事件处理 Possess 完成后逻辑,并定义 SetupControllingPawn 自定义函数处理控制权切换。
- `Document/Content/Blueprints/BP_TestCtl.md:5`(父类为 AMyPlayerController
- `Document/Content/Blueprints/BP_TestCtl.md:10,13`(实现 OnClientPossessFinished利用禁用摄像机计算特性
- `Document/Content/Blueprints/_relationships.md:8,38`(输入管线中的角色)

View File

@@ -0,0 +1,29 @@
# lonese 模块依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| MyCharacter.h | #include | GameFramework/Character.h | ACharacter |
| MyCharacter.h | forward-declare | -- | UInputMappingContext |
| MyCharacter.h | forward-declare | -- | UInputAction |
| MyCharacter.h | forward-declare | -- | ACameraPawn |
| MyCharacter.cpp | #include | MyCharacter.h | AMyCharacter |
| MyCharacter.cpp | #include | CameraPawn.h | ACameraPawn |
| MyCharacter.cpp | #include | EnhancedInputSubsystems.h | UEnhancedInputLocalPlayerSubsystem |
| MyCharacter.cpp | #include | EnhancedInputComponent.h | UEnhancedInputComponent |
| MyCharacter.cpp | #include | GameFramework/CharacterMovementComponent.h | UCharacterMovementComponent |
| CameraPawn.cpp | #include | CameraPawn.h | ACameraPawn |
| CameraPawn.cpp | #include | GameFramework/SpringArmComponent.h | USpringArmComponent |
| CameraPawn.cpp | #include | Camera/CameraComponent.h | UCameraComponent |
| CameraPawn.cpp | #include | InputActionValue.h | FInputActionValue |
| MyPlayerController.cpp | #include | MyPlayerController.h | AMyPlayerController |
| MyActor.cpp | #include | MyActor.h | AMyActor |
## 关键依赖链
1. Character Movement: Enhanced Input -> AMyCharacter::SetupPlayerInputComponent -> binds Move/Run/Crouch actions -> AMyCharacter::Move/BeginRun/StopRun/BeginCrouch/StopCrouch
2. Camera Control: Enhanced Input -> AMyCharacter -> CameraMove/CameraZoom/CameraRotate/CameraReset -> forwards to ACameraPawn methods
3. Camera View: AMyCharacter::BeginPlay -> spawns ACameraPawn (deferred) -> ACameraPawn::BeginPlay -> SetViewTarget(self) on PlayerController + AttachToActor(FollowTarget)
4. Camera Override: AMyPlayerController::CalcCamera -> calls AActor::CalcCamera (bypassing default view target blending) -> camera driven by ACameraPawn transform
5. Possession: AMyPlayerController::AcknowledgePossession -> Super + OnClientPossessFinished (BlueprintImplementableEvent in BP_TestCtl)