跳转至

Week9 — 网络同步

计算机网络基础

TCP、UDP、Socket、P2P、C/S等。

UE 网络架构

UE 采用 客户端-服务器(Client-Server) 架构,且 服务器(Server) 是权威的(Authoritative)。即所有关键游戏逻辑(伤害判定、得分、状态变化)都在服务器上执行,客户端只负责表现和输入。

UE 网络角色

  • Listen Server:其中一个玩家同时充当服务器,适合小规模局域网对战
  • Dedicated Server:独立运行的无头服务器(无渲染),适合大规模在线游戏
  • Client:普通玩家客户端,向服务器发送输入,接收状态更新
Text Only
1
2
3
4
5
网络拓扑示意:

[Client A] ──┐
             ├──► [Server (Authority)] ◄── [Client B]
[Client C] ──┘

数据同步基础

RPC,远程过程调用,本地调用远端提供的函数/方法。

对象序列化,把本地结构体传输到远程服务器之前需要将其序列化。

属性同步,一个服务器的某个对象的某个属性修改后,其他端的对象属性也要同步性更新。

RPC(远程过程调用)

RPC 是网络通信的基本手段,UE 中 RPC 分为三种类型:

类型 调用端 执行端 典型用途
Server RPC Client Server 客户端请求服务器执行操作(如开火、移动)
Client RPC Server 特定 Client 服务器通知某个客户端(如播放特效、显示 UI)
Multicast RPC Server 所有 Client + Server 广播事件(如爆炸特效、全局音效)
C++
// Server RPC:客户端请求服务器执行
UFUNCTION(Server, Reliable, WithValidation)
void ServerFire(FVector AimDirection);
//_Implementation 后缀为服务器端执行逻辑
void AMyCharacter::ServerFire_Implementation(FVector AimDirection)
{
    // 服务器验证并执行开火
    PerformLineTrace(AimDirection);
}
//_Validate 后缀为服务器验证逻辑,返回 false 会踢出客户端
bool AMyCharacter::ServerFire_Validate(FVector AimDirection)
{
    return AimDirection.IsNormalized();  // 简单的反作弊校验
}

// Client RPC:服务器通知客户端
UFUNCTION(Client, Reliable)
void ClientShowDamageNumber(float Damage, FVector Location);

// Multicast RPC:广播给所有端
UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayHitEffect(FVector Location);

Reliable vs Unreliable

  • Reliable:保证送达,有序,有重发机制。用于关键逻辑(开火、死亡)
  • Unreliable:不保证送达,无重发。用于频繁的非关键更新(特效、音效)
  • 不要对高频操作使用 Reliable RPC,否则会导致 队列溢出 导致连接断开

属性复制(Property Replication)

属性复制 是 UE 网络同步的核心机制。在属性声明时添加 Replicated 标记,引擎会自动将服务器上的变化同步到所有客户端。

C++
// 头文件声明
UPROPERTY(Replicated)
float Health;

UPROPERTY(ReplicatedUsing = OnRep_TeamColor)
FLinearColor TeamColor;

// 实现 GetLifetimeReplicatedProps
void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyCharacter, Health);
    DOREPLIFETIME(AMyCharacter, TeamColor);
}

// RepNotify 回调:属性在客户端更新后触发
void AMyCharacter::OnRep_TeamColor()
{
    // 更新角色材质颜色
    UpdateMaterialColor(TeamColor);
}
复制条件 说明
COND_None 默认,始终复制
COND_OwnerOnly 仅复制给 Owner 客户端(如弹药数)
COND_SkipOwner 复制给除 Owner 外的所有客户端
COND_InitialOnly 仅在初次复制时发送(如队伍 ID)
COND_Custom 手动控制复制时机

属性复制 vs RPC

  • 属性复制 适合持续性状态(血量、位置、队伍),引擎自动管理,只在值变化时同步
  • RPC 适合瞬时事件(开火、爆炸),需要手动调用
  • 优先使用属性复制,因为引擎会自动处理 相关性(Relevancy)优先级,减少带宽

GameMode 与 GameState

UE 的网络框架将游戏逻辑分为几个关键类,各有明确的 网络存在范围

  • GameMode:定义游戏规则(胜负条件、玩家管理、重生逻辑),仅存在于服务器,客户端无法访问。这是权威规则所在
  • PlayerController:每个玩家的控制器,服务器上持有所有玩家的 Controller,客户端只持有自己的
  • GameState:全局游戏状态(比分、比赛时间),复制到所有客户端,适合存放需要所有人看到的数据
  • PlayerState:每个玩家的公开信息(名字、分数、队伍),复制到所有客户端
  • HUD / Widget:UI 元素,仅在客户端存在
  • PlayerCameraManager:摄像机管理,仅客户端
C++
// GameMode 服务器端示例
void AMyGameMode::OnPlayerKilled(AController* Killer, AController* Victim)
{
    // 仅服务器执行
    if (AMyPlayerState* KillerPS = Killer->GetPlayerState<AMyPlayerState>())
    {
        KillerPS->AddScore(10);  // 分数通过 PlayerState 复制到客户端
    }

    // 设置重生定时器
    FTimerHandle RespawnTimer;
    GetWorldTimerManager().SetTimer(RespawnTimer, [this, Victim]()
    {
        RestartPlayer(Victim);
    }, 5.0f, false);
}

不要在客户端写游戏逻辑

由于 GameMode 仅存在于服务器,任何涉及规则判定的代码(伤害计算、得分、物品拾取)必须在服务器端执行。客户端只负责发送输入和展示结果,这就是所谓的 服务器权威模型(Server-Authoritative Model)