PushModel属性同步

优势
PushModel
是对 Replicate
的一个优化。
对于属性的同步,原有的 Replicated
属性,在 Replicate
之前需要对比所有的属性是否发生变化。
而用上了 PushModel
,则会在 Set
时主动将属性设置为脏,省去了 “比较” 这个高消耗的操作。
UE5 中还对 PushModel
做了更进一步的优化,添加了一些新的宏以及对更多基础组件的支持。
实践
设置依赖
首先,我们需要在 Build.cs
中添加 NetCore
模块的依赖。
1
| PublicDependcyModuleNames.AddRange(new string[] {"NetCore"})
|
设置属性
我们对于一个需要同步的属性,得先加上 Replicated
标签;有必要的话,需要加上RepilicatedUsing
来设置属性为脏时执行的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13
| UCLASS() class ATest : public AActor { GENERATED_BODY() public: UPROPERTY(Replicated, EditAnywhere, ReplicatedUsing = OnRep_Param) float Param; UFUNCITON() void OnRep_Param(); ... }
|
1
| void ATest::OnRep_Param() {...}
|
声明同步
接着我们需要实现 GetLifetimeReplicatedProps
,在这个接口中添加宏标记同步的 Property。
注意
1 2 3 4 5 6 7 8 9
| void ATest::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); FDoRepLifetimeParams SharedParams; SharedParams.bIsPushBased = true; DOREPLIFETIME_WITH_PARAMS_FAST(ATest, Param, SharedParams); }
|
FDoRepLifetimeParams
中设置了同步的条件,其中bIsPushBased
表明是否使用 PushModel
。
其中 DOREPLIFETIME_WITH_PARAMS_FAST
的宏实现:
1 2 3 4 5 6 7
| define DOREPLIFETIME_WITH_PARAMS_FAST(c,v,params) \ { \ static const bool bIsValid_##c_##v = ValidateReplicatedClassInheritance(StaticClass(), c::StaticClass(), TEXT(#v)); \ const TCHAR* DoRepPropertyName_##c_##v(TEXT(#v)); \ const NetworkingPrivate::FRepPropertyDescriptor PropertyDescriptor_##c_##v(DoRepPropertyName_##c_##v, (int32)c::ENetFields_Private::v, 1); \ RegisterReplicatedLifetimeProperty(PropertyDescriptor_##c_##v, OutLifetimeProps, params); \ }
|
修改属性
最后,我们在修改属性的时候,可以使用 MARK_PROPERTY_DIRTY_FROM_NAME
来将属性设脏。
1 2 3 4 5
| void ATest::SetParam(const float NewParam) { MARK_PROPERTY_DIRTY_FROM_NAME(ATest, Param, this); Param = NewParam; }
|
现在,Server
只需要检测属性是否被 MARK_DIRTY
就可以知道是否需要同步。
当然,除了 MARK_PROPERTY_DIRTY_FROM_NAME
以外,UE 还实现了一些作用类似的其它的宏:
1 2 3 4 5 6
| #define MARK_PROPERTY_DIRTY(Object, Property) #define MARK_PROPERTY_DIRTY_STATIC_ARRAY_INDEX(Object, RepIndex, ArrayIndex) #define MARK_PROPERTY_DIRTY_STATIC_ARRAY(Object, RepIndex, ArrayIndex) #define MARK_PROPERTY_DIRTY_FROM_NAME(ClassName, PropertyName, Object) #define MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY_INDEX(ClassName, PropertyName, ArrayIndex, Object) #define MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY(ClassName, PropertyName, ArrayIndex, Object)
|
改进
当然,我们也可以不这么复杂,我们可以自己设置一个新的标签。
对于打上这个标签的属性,让其在 CodeGenerator
中自动生成 Setter
与 Getter
,然后通过 Setter
与 Getter
来进行修改与访问。
1 2 3 4 5 6 7 8 9 10 11 12
| template<typename _T> \ void SetParam(_T&& NewParam) \ { \ MARK_PROPERTY_DIRTYFROM_NAME(ATest, Param, this); \ Param = Forward<_T>(NewParam); \ } \ const float& GetParam() const {return Param;} \ float& GetParam_Mutable() \ { \ MARK_PROPERTY_DIRTYFROM_NAME(ATest, Param, this); \ return Param; } \
|
参考
Unreal Engine 4. New network model: PushModel:https://tech-en.netlify.app/articles/en539604/index.html