Files
loneseDocument/Plugins/Item/IItemContainer.md
meishibiezb 29a3f77908 init
2026-06-04 21:44:13 +08:00

97 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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。