init
This commit is contained in:
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。
|
||||
Reference in New Issue
Block a user