Actor 复制流程详述
Actor 复制流程详述
官方文档转载加以理解
大多数 actor 复制操作都发生在 UNetDriver::ServerReplicateActors
内。在这里,服务器将收集所有被认定与各个客户端相关的 actor,并发送那些自上次(已连接的)客户端更新后出现变化的所有属性。
这里还定义了一个专门流程,指定了 actor 的更新方式、要调用的特定框架回调,以及在此过程中使用的特定属性。其中最重要的包括:
-
AActor::NetUpdateFrequency
- 用于确定 actor 的复制频度 -
AActor::PreReplication
- 在复制发生前调用 -
AActor::bOnlyRelevantToOwner
- 如果此 actor 仅复制到所有者,则值为 true -
AActor::IsRelevancyOwnerFor
- 用于确定 bOnlyRelevantToOwner 为 true 时的相关性 -
AActor::IsNetRelevantFor
- 用于确定 bOnlyRelevantToOwner 为 false 时的相关性
相应的高级流程如下:
- 循环每一个主动复制的 actor(
AActor::SetReplicates( true )
)- 确定这个 actor 是否在一开始出现休眠(
DORM_Initial
),如果是这样,则立即跳过。 - 通过检查 NetUpdateFrequency 的值来确定 actor 是否需要更新,如果不需要就跳过
- 如果
AActor::bOnlyRelevantToOwner
为 true,则检查此 actor 的所属连接以寻找相关性(对所属连接的观察者调用AActor::IsRelevancyOwnerFor
)。如果相关,则添加到此连接的已有相关列表。 - 此时,这个 actor 只会发送到单个连接。
- 对于任何通过这些初始检查的 actor,都将调用
AActor::PreReplication
。 - PreReplication 可以让您决定是否针对连接来复制属性。这时要使用
DOREPLIFETIME_ACTIVE_OVERRIDE
。 - 如果同过了以上步骤,则添加到所考虑的列表。
- 确定这个 actor 是否在一开始出现休眠(
- 对于每个连接(connection):
- 对于每个所考虑的上述 actor
- 确定是否休眠
- 是否还没有通道
- 确定客户端是否加载了 actor 所处的场景
- 如未加载则跳过
- 针对连接调用
AActor::IsNetRelevantFor
,以确定 actor 是否相关 - 如不相关则跳过
- 在归连接所有的相关列表上添加上述任意 actor
- 这时,我们拥有了一个针对此连接的相关 actor 列表
- 按照优先级对 actor 排序
- 对于每个排序的 actor:
- 如果连接没有加载此 actor 所在的关卡,则关闭通道(channel)(如存在)并继续
- 每 1 秒钟调用一次 AActor::IsNetRelevantFor,确定 actor 是否与连接相关
- 如果不相关的时间达到 5 秒钟,则关闭通道(channel)
- 如果相关且没有通道打开,则立即打开一个通道(channel)
- 如果此连接出现饱和 (饱和处理)
- 对于剩下的 actor
- 如果保持相关的时间不到 1 秒,则强制在下一时钟单位进行更新
- 如果保持相关的时间超过 1 秒,则调用
AActor::IsNetRelevantFor
以确定是否应当在下一时钟单位更新
- 对于通过了以上这几点的 actor,将调用
UChannel::ReplicateActor
将其复制到连接
将 Actor 复制到连接
UChannel::ReplicateActor
将负责把 actor 及其所有组件复制到连接中。其大致流程如下:
- 确定这是不是此 actor 通道打开后的第一次更新
- 如果是,则将所需的特定信息(初始方位、旋转等)序列化
- 确定该连接是否拥有这个 actor
- 如果没有,而且这个 actor 的角色是
ROLE_AutonomousProxy
,则降级为ROLE_SimulatedProxy
- 如果没有,而且这个 actor 的角色是
- 复制这个 actor 中已更改的属性
- 复制每个组件中已更改的属性
- 对于已经删除的组件,发送专门的删除命令