前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UE4 ReplicationGraph分析

UE4 ReplicationGraph分析

原创
作者头像
小伏羲
发布2018-12-18 19:21:00
4.2K0
发布2018-12-18 19:21:00
举报
文章被收录于专栏:UE4技术专场UE4技术专场

UE4 ReplicationGraph分析

老版本网络系统

总体思路

  • 所有Actor都会添加到网络列表中,每次更新的时候都是从这个Actor列表中遍历,根据不同的条件,确定每个链接的客户端需要同步的Actor列表,然后同步数据

流程图

老流程图.png
老流程图.png

添加

  • 生成Actor的时候直接调用AddNetworkActor函数,添加进workObjectlist中
代码语言:txt
复制
void UWorld::AddNetworkActor( AActor\* Actor )

{

    if ( Actor == nullptr )

    {

        return;

    }



    if ( Actor->IsPendingKill() ) 

    {

        return;

    }



    if ( !ContainsLevel(Actor->GetLevel()) )

    {

        return;

    }



    ForEachNetDriver(GEngine, this, [Actor](UNetDriver\* const Driver)

    {

        if (Driver != nullptr)

        {

            // Special case the demo net driver, since actors currently only have one associated NetDriverName.

            Driver->GetNetworkObjectList().FindOrAdd(Actor, Driver->NetDriverName);

        }

    });

}

详细同步过程

  • 每个服务器的tick调用同步函数
代码语言:txt
复制
// 总入口函数

int32 UNetDriver::ServerReplicateActors(float DeltaSeconds)

{

    // 确定当前的连接客户端

    ServerReplicateActors\_PrepConnections



    //确定同步列表(性能瓶颈点)

    ServerReplicateActors\_BuildConsiderList



    //对Actor列表进行优先级排序(性能瓶颈点)

    ServerReplicateActors\_PrioritizeActors    



    //处理排序过后的Actor列表(这里会进行最终的同步操作)

    ServerReplicateActors\_ProcessPrioritizedActors

}

优化方式

  • 裁剪距离降低
  • 降低更新频率

核心问题

  • 每次同步的计算量是ConnectionList*NetObjList,Actor列表庞大,比对计算的时候性能消耗是主要瓶颈点
  • 优化困难,上述优化都是有损优化,降低了客户

新版本网络系统

总体思路

  • 根据类型和状态对Actor进行节点划分,每次针对当前Connection只检查所在Grid内对象的信息来大大降低整个Replication的计算量,节省CPU时间

流程图

新流程图.png
新流程图.png

结构图

同步图表.jpg
同步图表.jpg
  • UReplicationGraph: 同步图表,NetDriver的网络分发入口
  • GridSpatialization2D:按照2D空间划分的同步节点
  • Playerstate:与状态有关的节点,比如限制更新频率的对象
  • Always Relevant(All): 与所有的链接都有相关性
  • Tear off:类似于OwnerRelevant,只同步给主客户端,不同步给模拟客户端
  • Always Relevant(Connection):只与某一个链接具有相关性,比如走进某一个区域点亮一盏灯,只对这个链接有作用
  • Always Relevant(team):和队友都有关的链接行,比如复活次数等等
  • Static:静态同步对象(可破坏物件)
  • Dynamic:可以移动的同步对象
  • Dormant: 休眠对象
  • Streaming:和关卡有关的对象,只有关卡加载了才会进行同步

添加

  • 生成Actor的时候通过调用各个类型节点的AddNetworkActor,分发Actor的存储
代码语言:txt
复制
void UNetDriver::AddNetworkActor(AActor\* Actor)

{

    GetNetworkObjectList().FindOrAdd(Actor, NetDriverName);

    if (ReplicationDriver)

    {

        ReplicationDriver->AddNetworkActor(Actor);

    }

}
  • Gird 2D结构
2D结构.png
2D结构.png
代码语言:txt
复制
- GridSpatialization2D 会按照cellsize划分为多个网格
代码语言:txt
复制
- AddActo
代码语言:txt
复制
    - 存在
代码语言:txt
复制
        - 根据种类添加到对应的列表中,静态,动态,休眠等等
代码语言:txt
复制
        - 静态动态中有划分为是否是属于streamingLevel中等
代码语言:txt
复制
    - 不存在网格
代码语言:txt
复制
        - 创建网格(和裁剪距离,Cellsize都有关系),重复网格存在的添加步骤
代码语言:txt
复制
- 更新
代码语言:txt
复制
    - 超过现有的网格边界
代码语言:txt
复制
        - 创建新的网格
代码语言:txt
复制
        - 刷新网格中的Actorlist
代码语言:txt
复制
    - 根据动态Actor位置刷新网格中的actorList

详细同步过程

  • 服务器tick调用网络同步,直接分发到同步图表类中,在这个类里面做同步的Actor同步前的处理操作,但是最终的Actor同步过程还是走原有的逻辑
代码语言:txt
复制
// 总入口函数

int32 UNetDriver::ServerReplicateActors(float DeltaSeconds)

{

    // 转发到同步图表类中

    ReplicationDriver->ServerReplicateActors(DeltaSeconds);

    int32 UReplicationGraph::ServerReplicateActors(float DeltaSeconds)

    {

        //各个节点的前期同步准备工作(对于空间划分的Grid主要是刷新各个网格中的Actor列表)

        for (UReplicationGraphNode\* Node : PrepareForReplicationNodes)

        {

            Node->PrepareForReplication();

        }

        // 处理各个链接的客户端

        for (UNetReplicationGraphConnection\* ConnectionManager: Connections)

        {

            //公共节点中需要的Actor列表

            for (UReplicationGraphNode\* Node : GlobalGraphNodes)

            {

                Node->GatherActorListsForConnection(Parameters);

            }

            //相关联的节点中的Actor列表

            for (UReplicationGraphNode\* Node : ConnectionManager->ConnectionGraphNodes)

            {

                Node->GatherActorListsForConnection(Parameters);

            }

            //真正Actor进行同步的地方,剩下的和老版本网络流程一致

            ReplicateSingleActo

        }

    }

}

新版本网络使用步骤

  • 自己重写ReplicationGraph
  • 添加自己的网格节点划分类型
  • 两种启动方式
代码语言:txt
复制
- DefaultEngine.ini 配置自己重写的同步图表类
代码语言:txt
复制
    - [/Script/OnlineSubsystemUtils.IpNetDriver]

ReplicationDriverClassName="/Script/ProjectName.ClassName"

代码语言:txt
复制
- 创建委托 
代码语言:txt
复制
        UReplicationDriver::CreateReplicationDriverDelegate().BindLambda([](UNetDriver\* ForNetDriver, const FURL& URL, UWorld\* World) -> UReplicationDriver\*

            {

                return NewObject<UMyReplicationDriverClass>(GetTransientPackage());

            });

优化方式

  • 通过详细的网格划分减少每次需要进行计算的Actor数量,提升性能

核心问题

  • 对于小场景来说用处不大,很有可能所有的Actor都在同一个网格中
  • 每次同步前都需要刷新网格中acto

新老版本对比(数据相关的需要自己搭建场景获取)

| Type | 标准网络 | Graph网络 |

|---------- |:-------------:|:-------------:|

| ActorList | 全量存储 | 按类型划分存储|

| 自定义网络同步 | 不支持 | 支持|

| 优化方式 | 有损效果,降频,降低裁剪距离 | 细致的网格种类划分|

| 性能 | 性能较差,随着客户端和actor的增长,呈现几何增长的性能消耗 | 性能和网格的划分密切相关(fortnite官方数据此处性能消耗降低75%)|

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • UE4 ReplicationGraph分析
    • 老版本网络系统
      • 总体思路
      • 流程图
      • 添加
      • 详细同步过程
      • 优化方式
      • 核心问题
    • 新版本网络系统
      • 总体思路
      • 流程图
      • 结构图
      • 添加
      • 详细同步过程
      • 新版本网络使用步骤
      • 优化方式
      • 核心问题
    • 新老版本对比(数据相关的需要自己搭建场景获取)
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档