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

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