UE代码-TArray的属性同步

1. 相关技术文档

2. 属性同步相关源码位置

  • NetDiver.h/cpp: 服务器在NetDiver的TickFlush里面,每一帧都会去执行ServerReplicateActors来同步Actor的相关内容。
  • UActorChannel.h/cpp: UActorChannel::ReplicateActor执行真正的Actor同步以及内部数据的同步。

3. TArray如何同步

对于一个数组的同步,会先对其进行一个判断,在初始化RepLayOut的Cmds数组的时候,就会判断当前的属性类型是否是动态数组(UArrayProperty),并会给其cmd.type做上标记REPCMD_DynamicArray。

  • 对于静态数组,是将每个元素都作为一个单独的属性,进行同步。
  • 对与动态数组,则是先进行一个长度的判断,比如服务器上数组长度发生变化,客户端在接收同步过来的数组时,会执行FRepLayout::ReceiveProperties_DynamicArray_r来处理动态数组,这个函数里面会矫正当前对象同步数组的大小。而若进行删除或插入操作时,由于TArray是一个连续的数组,所以可能导致后缀一长串的元素往后移或往前移动,而UE4的属性同步机制会认为,一连串的元素都发生改变,从而同步一长串的数据。

在这个影响下,使用插入删除TArray的元素时可能会导致一个效率的下降。

解决方法:

  1. 官方给的方案就是用FastTArray来替代TArray的属性同步了。FastTArray的使用方法见UE4引擎头文件源码:NetSerialization.h。
  2. 贪心策略:
    1. 可以减少使用插入删除操作,用其他方式代替;
    2. 还可以牺牲少部分时间对TArray进行一个优先级的排序,经常插入删除的元素尽量放在数组后面,这样受到其影响的后缀长度会尽可能少。

4. FastTArray相关

4.1 原理:

对TArray这种动态属性实现一个增量序列化,增量序列化是通过比较初始状态和当前状态并生成的,并生成一个差异状态与完全状态,并更新其为新的初始状态。

4.2 优缺点:

  • 优点:没有通过常规的TArray方式同步,避免了从中间删除元素,会使得后面所有元素都需要重新同步的情况,减少大量开销。
  • 缺点:当数据发生改变后,不能保证服务端与客户端中TArray元素顺序一致。

4.3 如何使用FastTArray:

/** Step 1: Make your struct inherit from FFastArraySerializerItem */
USTRUCT()
struct FExampleItemEntry : public FFastArraySerializerItem
{
  GENERATED_USTRUCT_BODY()
  // Your data:
  UPROPERTY()
  int32 ExampleIntProperty;
  UPROPERTY()
  float ExampleFloatProperty;
  /** Optional functions you can implement for client side notification of changes to items */
  void PreReplicatedRemove();
  void PostReplicatedAdd();
  void PostReplicatedChange();
};

/** Step 2: You MUST wrap your TArray in another struct that inherits from FFastArraySerializer */
USTRUCT()
struct FExampleArray: public FFastArraySerializer
{
  GENERATED_USTRUCT_BODY()
  UPROPERTY()
  TArray<FExampleItemEntry> Items; /** Step 3: You MUST have a TArray named Items of the struct you made in step 1. */
  /** Step 4: Copy this, replace example with your names */
  bool NetDeltaSerialize(FNetDeltaSerializeInfo & DeltaParms)
  {
  return FastArrayDeltaSerialize<FExampleItemEntry>( Items, DeltaParms );
  }
};

/** Step 5: Copy and paste this struct trait, replacing FExampleArray with your Step 2 struct. */
template<>
struct TStructOpsTypeTraits< FExampleArray > : public TStructOpsTypeTraitsBase
{
  enum
  {
  WithNetDeltaSerializer = true,
  };
};
posted @ 2022-04-18 11:15  ccsu_madoka  阅读(1727)  评论(3编辑  收藏  举报