CommonParams解决方案
在实现业务框架的时候,经常需要支持传参的功能(比如 Buff
系统中,AddBuff
的时候,通常需要支持外部传入一个 BuffParams
,在内部解析出各种 Param
使用。)
这个参数需要支持基本的数据保存、同步等。
初始的想法
一个最简单的想法,是打包一个结构体,支持各种类型的传入,比如:
1 2 3 4 5 6 7 8 9 10
| USTRUCT() struct Params { UPROPERTY() uint64 uVar0; UPROPERTY() int iVar0; UPROPERTY() float fVar0; }
|
这样虽然很简单,同时可以通过反射来同步。
但是有一个巨大的问题,那就是在解析参数的时候,只知道 Var0
这种没有名字的抽象的概念,使用者需要记住 Var0
对应的是什么数据,很不直观。
CommonVariantParams
维护一个 CommonVariantParams
,通过 TMap <FString, FVariant> ValueMap
来保存数据,同时自定义 NetSerialize
来支持网络同步。
为了方便使用,还可以自定义一些构造函数,比如 std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs
,这样就可以支持 { {Key0, Val0}, {Key1, Val1} }
形式的构造。
由于 TMap
以及 FVariant
实际上在引擎内部都已经有了合适的重载 operator << Archive
,所以自定义 NetSerialize
也很简单。
使用方法
1 2 3 4 5 6
| FCommonVariantParams Params = { {"ParamA", (float)A}, {"ParamsB", (int)B }, {"ParamsC", (FString)C } });
float A = BuffParams.GetValue<float>("ParamA"); int B = BuffParams.GetValue<int>("ParamB"); FString C = BuffParams.GetValue<FString>("ParamC");
|
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| USTRUCT(BlueprintType) struct FCommonVariantParams { GENERATED_BODY()
FCommonVariantParams() = default; FCommonVariantParams(const FString& Key, FVariant Value); FCommonVariantParams(std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs);
void SetValue(const FString& FieldName, FVariant Value); bool Contains(const FString& FieldName) const; bool IsEmpty() const; template <typename ValueType = float> ValueType GetValue(const FString& FieldName, ValueType Default = {}) const { if (!ValueMap.Contains(FieldName)) return Default; if (TVariantTraits<ValueType>::GetType() != ValueMap[FieldName].GetType()) return Default; return ValueMap[FieldName].GetValue<ValueType>(); } FString ToString() const; const TMap<FString, FVariant>& GetValueMap() const;
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
FCommonVariantParams operator+(const FCommonVariantParams& OtherParams); FCommonVariantParams& operator=(const FCommonVariantParams& OtherParams) { ValueMap = OtherParams.GetValueMap(); return *this; }
void Merge(const FCommonVariantParams& OtherParams); void Clear();
decltype(auto) begin() { return ValueMap.begin(); } decltype(auto) begin() const { return ValueMap.begin(); } decltype(auto) end() { return ValueMap.end(); } decltype(auto) end() const { return ValueMap.end(); }
protected: TMap<FString, FVariant> ValueMap; };
template<> struct TStructOpsTypeTraits<FCommonVariantParams> : TStructOpsTypeTraitsBase2<FCommonVariantParams> { enum { WithNetSerializer = true, }; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| FCommonVariantParams::FCommonVariantParams(const FString& Key, FVariant Value) { ValueMap.Add(Key, Value); }
FCommonVariantParams::FCommonVariantParams(std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs) { for (const auto& Pair : ValuePairs) { ValueMap.Add(Pair.Key, Pair.Value); } }
void FCommonVariantParams::SetValue(const FString& FieldName, FVariant Value) { ValueMap.Add(FieldName, FVariant(Value)); }
bool FCommonVariantParams::Contains(const FString& FieldName) const { return ValueMap.Contains(FieldName); }
bool FCommonVariantParams::IsEmpty() const { return ValueMap.IsEmpty(); }
const TMap<FString, FVariant>& FCommonVariantParams::GetValueMap() const { return ValueMap; }
bool FCommonVariantParams::NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) { bOutSuccess = true; Ar << ValueMap; return true; }
FCommonVariantParams FCommonVariantParams::operator+(const FCommonVariantParams& OtherParams) { FCommonVariantParams CombinedParams = *this; CombinedParams.Merge(OtherParams); return CombinedParams; }
void FCommonVariantParams::Merge(const FCommonVariantParams& OtherParams) { for (const auto& [Key, Value] : OtherParams.GetValueMap()) { SetValue(Key, Value); } }
void FCommonVariantParams::Clear() { ValueMap.Empty(); }
|
Lua
对于这个 CommonVariantParams
,还可以扩展一些方法,让这些参数可以通过 Unlua
在 Lua
脚本中设置与访问。
1 2 3 4 5 6 7
| void FCommonVariantParams::SetValue(const FString& FieldName, FVariant Value) { ValueMap.Add(FieldName, FVariant(Value)); }
void FCommonVariantParams::SetIntValue(const FString& FieldName, int32 Value) { SetValue(FieldName, Value); } int32 FCommonVariantParams::GetIntValue(const FString& FieldName, int32 Default ) { return GetValue<int32>(FieldName, Default); }
|
1 2 3 4 5 6 7
|
BEGIN_EXPORT_REFLECTED_CLASS(FCommonVariantParams) ADD_FUNCTION(SetIntValue) ADD_FUNCTION(GetIntValue) END_EXPORT_CLASS() IMPLEMENT_EXPORTED_CLASS(FCommonVariantParams)
|
具体业务
特别地,具体业务使用的时候,可以继承一个自己的 Params
来使用,以 FGameplayBuffParams
为例:
1 2 3 4 5 6
| USTRUCT() struct FGameplayBuffParams : public FCommonVariantParams { GENERATED_BODY() using FCommonVariantParams::FCommonVariantParams; }
|