数据混入系统

概述
通用库存系统(GIS)提供了一个灵活的机制——数据混入系统,允许为任意对象(如 UObject)附加动态运行时数据(通过 FInstancedStruct),并支持序列化(有限制)和网络复制。这种设计非常适合在不修改原始对象的情况下,为目标对象扩展动态数据。
本文仅针对希望利用该系统开发其它游戏系统的C++用户,蓝图用户不需要阅读此文章。
使用场景
数据混入系统适用于需要动态管理对象状态的场景。例如,在一个游戏任务(Quest)系统中,使用不同的 DataAsset 定义每个任务(Task,如目标、描述、奖励等静态数据)。通过接入数据混入系统,每个任务类型可使用任意数据结构管理其内部状态。
示例
假设有一个任务要求“杀死5个强盗和3匹狼”,可以定义一个结构体 TaskState_KillEnemy(包含 BanditKilled 和 WolfKilled 字段)来跟踪任务状态。
数据混入容器
GIS 通过 GIS_MixinContainer 提供数据混入功能,内部维护 Object->InstancedStruct 键值对数组,由 FastArraySerializer 实现,支持高效的网络复制和简化的数据变化通知回调。
使用数据混入容器的步骤
以道具实例(ItemInstance)为例:
1.在类中添加 GIS_MixinContainer 字段。
1 /**2 * Container for each fragment's runtime state.3 * 每个片段运行时状态的容器。4 */5 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="ItemInstance", meta=(DisplayName="Fragment States", ShowOnlyInnerProperties))6 FGIS_MixinContainer FragmentStates;
2.在构造函数中设置容器的 OwningObject 为当前对象(对于 ActorComponent,在 InitializeComponent 中设置)。
1UGIS_ItemInstance::UGIS_ItemInstance(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer), FragmentStates(this)
3.确保该字段支持网络同步,同步条件可根据项目需求自定义。
1void UGIS_ItemInstance::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const2{3 Super::GetLifetimeReplicatedProps(OutLifetimeProps);4 // FastArray don't need push model.5 DOREPLIFETIME(ThisClass, FragmentStates);
4.使类实现 IGIS_MixinOwnerInterface 接口,并通过接口函数响应数据变化。
1class GENERICINVENTORYSYSTEM_API UGIS_ItemInstance : public UObject, public IGIS_MixinOwnerInterface
1 /**2 * Called when mixin data is added to the owning object.3 * 当混合数据被添加到拥有对象时调用。4 * @param Target The target object to which the data is attached. 数据附加到的对象。5 * @param Data The added instanced struct data. 添加的实例化结构数据。6 */7 virtual void OnMixinDataAdded(const TObjectPtr<const UObject>& Target, const FInstancedStruct& Data) = 0;89 /**10 * Called when mixin data is updated in the owning object.11 * 当拥有对象中的混合数据被更新时调用。12 * @param Target The target object to which the data is attached. 数据附加到的对象。13 * @param Data The updated instanced struct data. 更新的实例化结构数据。14 */15 virtual void OnMixinDataUpdated(const TObjectPtr<const UObject>& Target, const FInstancedStruct& Data) = 0;1617 /**18 * Called when mixin data is removed from the owning object.19 * 当从拥有对象中移除混合数据时调用。20 * @param Target The target object to which the data was attached. 数据附加到的对象。21 * @param Data The removed instanced struct data. 移除的实例化结构数据。22 */23 virtual void OnMixinDataRemoved(const TObjectPtr<const UObject>& Target, const FInstancedStruct& Data) = 0;
5.目标对象(如 Item_Fragment)需实现 IGIS_MixinTargetInterface 接口,以定义运行时数据类型、默认值及序列化需求。
1class GENERICINVENTORYSYSTEM_API UGIS_ItemFragment : public UObject, public IGIS_MixinTargetInterface
1 /**2 * Determines if the mixin data can be serialized.3 * 确定混合数据是否可以序列化。4 * @return True if the data is serializable, false otherwise. 如果数据可序列化则返回true,否则返回false。5 */6 virtual bool IsMixinDataSerializable() const = 0;78 /**9 * Retrieves the compatible data structure for the mixin.10 * 获取混合数据的兼容数据结构。11 * @return The script struct compatible with the mixin data. 与混合数据兼容的脚本结构。12 */13 virtual TObjectPtr<const UScriptStruct> GetCompatibleMixinDataType() const = 0;1415 /**16 * Generate default runtime data for compatible data type.17 * 生成兼容的混合数据类型的默认值。18 * @param DefaultState The default value of compatible data. 兼容的数据结构默认值。19 * @return If has default value. 是否具备默认值?20 */21 virtual bool MakeDefaultMixinData(FInstancedStruct& DefaultState) const = 0;
API
GIS_MixinContainer 提供添加、移除、保存和加载等 API,具体用法请参考项目源码。
使用限制
数据混入容器支持为任意对象附加运行时状态,但若需支持存档,请注意:
- 目标对象(TargetObject)必须是从 Package 加载的资产(如 DataAsset)。
- 自定义结构体需支持 SaveGame 属性,且字段类型必须为虚幻引擎支持的可序列化类型。
示例
参考下图,注意查看字段的工具提示(Tooltip)。
