虚幻网络同步机制中的两个核心类

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、将数据从 GameMode 迁移到 GameState
    • 1.1 在 GameState 中定义同步变量
    • 1.2 在 GameMode 中操作 GameState 变量
  • 二、在客户端访问 GameState 数据
    • 2.1 在客户端读取同步变量
    • 2.2 使用 RepNotify 实现客户端回调
  • 三、通过 RPC 同步数据(补充方案)
    • 3.1 服务器通过多播 RPC 通知客户端
  • 四、关键总结
  • 五、注意事项
    • 5.1权限检查
    • 5.2 带宽优化
    • 5.3 同步延迟
  • 总结


前言

GameMode是服务器端的权威对象,负责游戏规则和逻辑,而GameState是同步到所有客户端的,用来保存游戏的状态信息。所以,如果客户端需要访问某些数据,比如分数、时间等,这些数据应该存储在GameState中,而不是GameMode,因为GameMode只在服务器存在,客户端无法直接访问。
在虚幻引擎中,GameMode 和 GameState 是网络同步机制中的两个核心类。它们的核心区别在于:

GameMode:仅在服务器端存在,负责管理游戏规则和逻辑(如生成玩家、判断胜负)。客户端无法直接访问 GameMode。

GameState:存在于服务器和所有客户端,保存需要同步的全局游戏状态(如比分、剩余时间、玩家列表)。服务器更新 GameState 后会自动同步到客户端。

因此,如果客户端需要访问某些数据(例如 GameMode 中的变量),必须将这些数据转移到 GameState 中,并通过网络同步机制(属性复制或 RPC)实现客户端访问。

一、将数据从 GameMode 迁移到 GameState

假设你的 GameMode 中有一个需要同步的变量 GlobalScore,以下是迁移步骤:

1.1 在 GameState 中定义同步变量

// MyGameState.h
#pragma once
#include "GameFramework/GameStateBase.h"
#include "MyGameState.generated.h"

UCLASS()
class MYPROJECT_API AMyGameState : public AGameStateBase
{
    GENERATED_BODY()

public:
    // 需要同步的变量
    UPROPERTY(Replicated, BlueprintReadOnly, Category = "GameState")
    int32 GlobalScore;

    // 设置属性复制规则
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};
// MyGameState.cpp
#include "MyGameState.h"
#include "Net/UnrealNetwork.h"

void AMyGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyGameState, GlobalScore); // 注册 GlobalScore 为同步变量
}

1.2 在 GameMode 中操作 GameState 变量

// MyGameMode.h
#pragma once
#include "GameFramework/GameModeBase.h"
#include "MyGameMode.generated.h"

UCLASS()
class MYPROJECT_API AMyGameMode : public AGameModeBase
{
    GENERATED_BODY()

public:
    virtual void StartPlay() override;

    // 更新全局分数(服务器逻辑)
    void UpdateGlobalScore(int32 NewScore);
};
// MyGameMode.cpp
#include "MyGameMode.h"
#include "MyGameState.h"

void AMyGameMode::StartPlay()
{
    Super::StartPlay();

    // 初始化全局分数(示例)
    UpdateGlobalScore(100);
}

void AMyGameMode::UpdateGlobalScore(int32 NewScore)
{
    // 获取 GameState 并更新分数
    AMyGameState* MyGameState = GetGameState<AMyGameState>();
    if (MyGameState)
    {
        MyGameState->GlobalScore = NewScore;
    }
}

二、在客户端访问 GameState 数据

无论是服务器还是客户端,都可以通过 GetWorld()->GetGameState() 获取 GameState 实例:

2.1 在客户端读取同步变量

// 任意客户端或服务器代码中
void AMyPlayerController::Client_ShowScore()
{
    // 获取 GameState
    AMyGameState* MyGameState = GetWorld()->GetGameState<AMyGameState>();
    if (MyGameState)
    {
        // 直接读取同步后的变量
        int32 CurrentScore = MyGameState->GlobalScore;
        UE_LOG(LogTemp, Warning, TEXT("Global Score: %d"), CurrentScore);
    }
}

2.2 使用 RepNotify 实现客户端回调

如果需要变量变化时触发客户端逻辑(如更新 UI),可以使用 ReplicatedUsing:

// MyGameState.h
UCLASS()
class AMyGameState : public AGameStateBase
{
    GENERATED_BODY()

    UPROPERTY(ReplicatedUsing=OnRep_GlobalScore)
    int32 GlobalScore;

    UFUNCTION()
    void OnRep_GlobalScore(); // 变量同步时客户端回调
};
// MyGameState.cpp
void AMyGameState::OnRep_GlobalScore()
{
    // 客户端收到 GlobalScore 更新
    UE_LOG(LogTemp, Warning, TEXT("Score Updated: %d"), GlobalScore);
    // 更新 UI 或其他客户端逻辑
}

三、通过 RPC 同步数据(补充方案)

如果某些数据不适合直接通过 GameState 同步(例如需要即时触发的动作),可使用 RPC:

3.1 服务器通过多播 RPC 通知客户端

// MyGameMode.cpp
void AMyGameMode::UpdateGlobalScore(int32 NewScore)
{
    AMyGameState* MyGameState = GetGameState<AMyGameState>();
    if (MyGameState)
    {
        MyGameState->GlobalScore = NewScore;
        // 多播 RPC 通知所有客户端
        MyGameState->Multicast_OnScoreUpdated(NewScore);
    }
}
// MyGameState.h
UCLASS()
class AMyGameState : public AGameStateBase
{
    GENERATED_BODY()

    UFUNCTION(NetMulticast, Unreliable)
    void Multicast_OnScoreUpdated(int32 NewScore);
};
// MyGameState.cpp
void AMyGameState::Multicast_OnScoreUpdated_Implementation(int32 NewScore)
{
    // 客户端执行逻辑(如播放音效)
    UGameplayStatics::PlaySound2D(this, ScoreUpdateSound);
}

四、关键总结

场景 推荐方案 代码示例
客户端需要读取全局状态 将数据迁移到 GameState + 属性复制 DOREPLIFETIME + OnRep 回调
客户端需要即时响应事件 服务器调用多播 RPC UFUNCTION(NetMulticast)
服务器需要权威修改数据 在 GameMode 中操作 GameState GetGameState() + 直接赋值

五、注意事项

5.1权限检查

在修改 GameState 中的变量时,确保逻辑仅在服务器执行:

if (HasAuthority()) // 或 GetOwnerRole() == ROLE_Authority
{
    // 安全修改变量
}

5.2 带宽优化

对高频更新的变量使用 Replicated + RepNotify 代替 RPC。
对非关键数据使用 Unreliable RPC 减少带宽占用。

5.3 同步延迟

客户端可能稍晚于服务器收到数据更新,需通过插值或预测机制提升体验(参考前文回滚机制)。

总结

通过以上方法,你可以将原本只能在服务器访问的 GameMode 数据安全同步到客户端,并确保多人游戏的逻辑一致性。

你可能感兴趣的:(虚幻,网络,游戏引擎)