init
This commit is contained in:
97
Plugins/Item/FItemDef.md
Normal file
97
Plugins/Item/FItemDef.md
Normal 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 到 FItemView(ItemViewFactory.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 构造预览视图(无运行时数据)。
|
||||
100
Plugins/Item/FItemInstance.md
Normal file
100
Plugins/Item/FItemInstance.md
Normal 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。
|
||||
76
Plugins/Item/FItemPropertyEntry.md
Normal file
76
Plugins/Item/FItemPropertyEntry.md
Normal 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
66
Plugins/Item/FItemView.md
Normal 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 是一个纯数据 USTRUCT(BlueprintType),所有字段标记为 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 控件显示。
|
||||
85
Plugins/Item/FItemViewFactory.md
Normal file
85
Plugins/Item/FItemViewFactory.md
Normal 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 调用,不对外部开放。
|
||||
|
||||
**典型用法 -- GetItemViews(DefaultContainer.cpp:64):**
|
||||
```cpp
|
||||
Results.Add(FItemViewFactory::CreateView(Registry, Def, *Item,
|
||||
TScriptInterface<const IItemContainer>(this)));
|
||||
```
|
||||
|
||||
**典型用法 -- GetItemViewByID(DefaultContainer.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 为查找到的物品创建视图。
|
||||
68
Plugins/Item/IInventory.md
Normal file
68
Plugins/Item/IInventory.md
Normal 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_InventoryComp(IInventory 实现者)获取 ViewCache 数据显示物品列表。
|
||||
96
Plugins/Item/IItemContainer.md
Normal file
96
Plugins/Item/IItemContainer.md
Normal 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_SetPropertyRaw(ItemContainer.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。
|
||||
81
Plugins/Item/IItemViewStrategy.md
Normal file
81
Plugins/Item/IItemViewStrategy.md
Normal 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: 返回此策略处理的属性名称。注册表按此名称将策略注册到 UItemRegistrySubsystem(RegisterViewStrategy 的 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。
|
||||
85
Plugins/Item/ItemFactory.md
Normal file
85
Plugins/Item/ItemFactory.md
Normal 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>。创建的物品实例具有以下特征:
|
||||
- 自动生成唯一 ItemID(FGuid::NewGuid())。
|
||||
- ItemData 已填充 DefaultItemProps + OverrideProps 的合并属性。
|
||||
- 如果 ItemDef.bHasTracer 为 true,ItemData 中额外包含 Internal_ItemTracer 键的 UItemTracer 对象。
|
||||
|
||||
**调用限制:**
|
||||
此方法是创建 FItemInstance 的唯一入口。FItemInstance 的构造函数是 private/default(通过 friend class ItemFactory 控制),外部代码无法直接构造 FItemInstance。
|
||||
|
||||
## 使用方法
|
||||
ItemFactory 的唯一使用场景是通过 UDefaultContainer::CreateItem 创建物品。
|
||||
|
||||
**调用 CreateItemInstance(DefaultContainer.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 的唯一外部调用点。
|
||||
79
Plugins/Item/UDefaultContainer.md
Normal file
79
Plugins/Item/UDefaultContainer.md
Normal 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 查找。未找到返回空 FItemView(DefaultContainer.cpp:69-81)。
|
||||
- **GetItemCount() const** → int32: 返回 Items.Num()(DefaultContainer.cpp:84-86)。
|
||||
|
||||
**变更接口:**
|
||||
- **MoveItem(const FGuid&, const TScriptInterface<IItemContainer>&)** → bool: 从内部数组取出物品(MoveTemp),调用 RemoveAt,然后通过目标容器的 InjectPayload 注入。目标容器无效或物品不存在时返回 false(DefaultContainer.cpp:89-111)。
|
||||
- **CreateItem(FName, TArray<FGuid>&, int32 Count = 1)** → bool: 从注册表获取 FItemDef,检查 ItemName 非空,循环调用 ItemFactory::CreateItemInstance 创建指定数量物品,每个物品通过自身的 InjectPayload 添加。新物品 ID 追加到 NewItemIDs(DefaultContainer.cpp:113-127)。
|
||||
|
||||
**属性接口(CustomThunk 底层实现):**
|
||||
- **Internal_GetPropertyRaw**: 按 ItemID 线性查找物品 → 从属性包中查找属性描述 → 类型兼容性检查后从属性包内存拷贝到蓝图引脚内存(DefaultContainer.cpp:137-187)。
|
||||
- **Internal_SetPropertyRaw**: 相同的查找和类型检查流程,反向拷贝(从蓝图引脚写入属性包)(DefaultContainer.cpp:189-230)。
|
||||
|
||||
**内部接口:**
|
||||
- **InjectPayloadImpl(TUniquePtr<FItemInstance>)**: 接收物品所有权,简单的 Items.Add(DefaultContainer.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 引用并包装其操作。
|
||||
63
Plugins/Item/UInternalItemProperty.md
Normal file
63
Plugins/Item/UInternalItemProperty.md
Normal 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 属性描述。
|
||||
83
Plugins/Item/UItemRegistrySettings.md
Normal file
83
Plugins/Item/UItemRegistrySettings.md
Normal 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->ItemDataTables,LoadSynchronous 每个软引用并注册。
|
||||
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:70-78` -- Initialize 中遍历 Settings->ViewStrategies,LoadSynchronous 每个类,NewObject 实例化策略并注册。
|
||||
- `Plugins/Item/Source/Item/Private/ItemRegistrySubsystem.cpp:79-80` -- Initialize 完成后通过 UE_LOG 输出注册的 DataTable 和策略数量。
|
||||
95
Plugins/Item/UItemRegistrySubsystem.md
Normal file
95
Plugins/Item/UItemRegistrySubsystem.md
Normal 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)**: 移除指定键名的 DataTable(ItemRegistrySubsystem.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>: 从策略映射中查找指定属性名的策略。未找到时返回空的 TScriptInterface(ItemRegistrySubsystem.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. 遍历 ItemDataTables,LoadSynchronous 每个软引用并注册。
|
||||
4. 遍历 ViewStrategies,LoadSynchronous 每个类,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) 获取属性格式化策略。
|
||||
87
Plugins/Item/UItemTracer.md
Normal file
87
Plugins/Item/UItemTracer.md
Normal 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 通知监听方。
|
||||
27
Plugins/Item/_relationships.md
Normal file
27
Plugins/Item/_relationships.md
Normal 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
|
||||
Reference in New Issue
Block a user