前言

网络相关性是虚幻引擎为了提升网络传输效率而提供的一种设计机制。在庞大的世界中,其实我们每个参与游戏的玩家在网络中并不需要关心所有人的一举一动,相关性就是从某种角度上描述了对方的网络信息是否需要同步给你。

简而言之,与你相关的,他的网络数据会同步给你,与你无关的,他的网络数据不会同步给你。

说明

对于相关性的配置,可以在Actor的操作面板中找到,参照如图

图一
图一

  • Only Relevant to Owner 只跟所有者相关
  • Always Relevant 永远和所有人相关
  • Net Use Owner Relevancy 使用所有者相关性
  • Net Cull Distance Squared 与距离相关

关于相关性的逻辑代码,可以在AActor.cpp找到,摘抄源码如下:

c++
 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
bool AActor::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
    if (bAlwaysRelevant || IsOwnedBy(ViewTarget) || IsOwnedBy(RealViewer) || this == ViewTarget || ViewTarget == GetInstigator())
    {
        return true;
    }
    else if (bNetUseOwnerRelevancy && Owner)
    {
        return Owner->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
    }
    else if (bOnlyRelevantToOwner)
    {
        return false;
    }
    else if (RootComponent && RootComponent->GetAttachParent() && RootComponent->GetAttachParent()->GetOwner() && (Cast<USkeletalMeshComponent>(RootComponent->GetAttachParent()) || (RootComponent->GetAttachParent()->GetOwner() == Owner)))
    {
        return RootComponent->GetAttachParent()->GetOwner()->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
    }
    else if(IsHidden() && (!RootComponent || !RootComponent->IsCollisionEnabled()))
    {
        return false;
    }
 
    if (!RootComponent)
    {
        UE_LOG(LogNet, Warning, TEXT("Actor %s / %s has no root component in AActor::IsNetRelevantFor. (Make bAlwaysRelevant=true?)"), *GetClass()->GetName(), *GetName() );
        return false;
    }
 
    return !GetDefault<AGameNetworkManager>()->bUseDistanceBasedRelevancy ||
            IsWithinNetRelevancyDistance(SrcLocation);
}

解读

在服务器上,服务器需要处理所有人的数据,所以所有人在服务器是不检查相关性的,测试时尽量使用DS模式测试。

相关性会影响渲染,例如已知A玩家,当B玩家进入A玩家相机拍摄范围,但是B玩家和A玩家不相关,则A玩家终端不会渲染B玩家(不要使用相关性做什么隐身技能,这不是相关性要处理的)。

最终的测试,建议大家可以启用引擎的DS(专享服务器)模式,完成测试。

测试通过动态生成Actor后,调整Owner来测试,一定要动态生成,Owner可以设置为某个终端控制的Pawn,通过渲染剔除来测试效果

1、Only Relevant to Owner(只和所有者相关)

所有者:虚幻引擎有个概念,即,你生成的对象Actor,添加的组件,都需要有个归属,一般Actor没有指认关系则归属于关卡。组件如果在构造函数中使用CreateDefaultSubobject创建,则归属所在类实例的对象。如果在运行时态,使用NewObject创建,则归属NewObject时填入的Outer。

为什么要有所有者? 主要是为了溯源,解决对象释放依赖问题。

网络中的所有者:虚幻网络所有者还有另外一层意图,在客户端,只有属于当前客户端的对象才能向服务器发送Server标记消息。一般的Actor是不归属任何终端的,但是如果某个终端有个玩家拾取一把枪械,枪械应归属拾取角色所在终端,以便后期从所属角色终端向服务器发送消息。修改方法是,必须在服务器,将枪械的Owner设置为目标终端的PlayerController,Pawn,PlayerState,三者之一即可,调用SetOwner函数。

当Actor勾选了Only Relevant to Owner,则只会将网络消息发送给他的Onwer。不会发送给任何人,并且在其他人的终端不会进行渲染(如果Actor是运行时生成会被隐藏,如果是拜访到世界中则不会隐藏渲染,这又是另外一个话题了)。

2、Always Relevant 永远和所有人相关

顾名思义,就是无论在任何地方,任何角落都能接收到此对象的网络数据信息。例如绝对求生中的空投。

3、Net Use Owner Relevancy 使用所有者相关性

使用所有者相关其实就是当你有所有者的时候,相关性检查不走你的规则,而是使用你所有者的规则。一个简单的例子大家就能够懂了:在网络中,一个玩家驾驶一辆载具,载具如何驾驶者的相关性规则不一致,可能会导致车子和驾驶者进入另外一个玩家视野时,另外一个玩家只看到载具或是只看到车辆。这是很奇怪的,所以应该将载具的Owner设置为它的驾驶者,并且勾选使用Owner的相关性。

4、Net Cull Distance Squared 通过距离管控相关性

所有的Actor都是默认使用距离进行相关性检测的。例如A和B设置的相关性距离是10米,那么超过10米,在网络中就会不会向对方发送网络消息了。

注意,此参数需要设置距离的平方,例如你希望超过10米就不要发送数据,需要填入1000×1000

结语

相关性总体来说不是非常复杂,通过贴出来的代码,大家也可以看到检测的基本逻辑。但是需要注意,相关性的检测不是每一帧都检查,所以不要依赖相关性的及时性设计任何逻辑。

官方也给出了相关性检查的规则,以上规则在设置时是有前后顺序的,即一层层检查的,上一层不成立,则继续向下检查。最后是距离剔除。

  • 如果 Actor 是 bAlwaysRelevant、归属于 Pawn 或 PlayerController、本身为 Pawn 或者 Pawn 是某些行为(如噪音或伤害)的发起者,则其具有相关性。
  • 如果 Actor 是 bNetUseOwnerRelevancy 且拥有一个所有者,则使用所有者的相关性。
  • 如果 Actor 是 bOnlyRelevantToOwner 且没有通过第一轮检查,则不具有相关性。
  • 如果 Actor 被附加到另一个 Actor 的骨架模型,它的相关性将取决于其所在基础的相关性。
  • 如果 Actor 是不可见的 (bHidden == true) 并且它的 Root Component 并没有碰撞,那么则不具有相关性,
  • 如果没有 Root Component 的话,AActor::IsNetRelevantFor() 会记录一条警告,提示是否要将它设置为 bAlwaysRelevant=true。
  • 如果 AGameNetworkManager 被设置为使用基于距离的相关性,则只要 Actor 低于净剔除距离,即被视为具有相关性。