UE Gameplay 主要的几个类

https://www.bilibili.com/video/BV1dtrpBoERt

省流

游戏运行时有一个GameEgnine的实例。他会创建一个GameInstance。

在加载地图的时候,调用到 Engine->Brose,会从磁盘加载一个UPackage 从中获得一个World对象。

World初始化流程中会创建地图中的Actor,包括GameMode也是Actor。然后执行三个初始化。

在GameMode的PreInit中会创建GameState。

在登录的时候,会触发GameMode的Login,如果成功,会创建 PlayerController,然后由PlayerControl创建PlayerState。然后同步给客户端。

然后尝试RestartPlayer流程。找到一个重生点,然后创建Pawn,然后Process。

客户端                          DS
  │──── NMT_Hello ────────────→│  发版本号
  │←─── NMT_Challenge ─────────│  返回挑战码(防伪造)
  │──── NMT_Login ─────────────→│  发玩家名、Options字符串
  │                              │  → GameMode::PreLogin()  ← 你的验证逻辑在这
  │←─── NMT_Welcome ────────────│  发当前地图名
  │──── NMT_Netspeed ──────────→│  发网速
  │                              │  → GameMode::Login()     ← 创建 PlayerController
  │                              │  → GameMode::PostLogin()
  │←─── 复制 PlayerController ──│  PC 开始同步给客户端
  │──── NMT_Join ──────────────→│  确认收到 PC
  │                              │  → GameMode::HandleStartingNewPlayer()
  │                              │  → RestartPlayer() → Spawn Pawn

GuardedMain

引擎初始化主流程

// Launch.cpp
int32 GuardedMain(const TCHAR* CmdLine)
{
    // 阶段1: 引擎预初始化
    int32 ErrorLevel = EnginePreInit(CmdLine);  // → FEngineLoop::PreInit()

    // 阶段2: 引擎初始化
    EngineInit();   // → FEngineLoop::Init()

    // 阶段3: 主循环(Tick)
    while (!IsEngineExitRequested())
    {
        EngineTick();   // → FEngineLoop::Tick()
    }

    // 阶段4: 引擎退出
    EngineExit();   // → FEngineLoop::Exit()
}

这一步主流程是 FEngineLoop 的 Init Tick Exit。

FEngineLoop::PreInit

加载模块,构建CDO

FEngineLoop::Init 到 GEngine

// LaunchEngineLoop.cpp
int32 FEngineLoop::Init()
{
    // 1. 从配置确定引擎类型
    // Game: UGameEngine  |  Editor: UEditorEngine
    UClass* EngineClass = StaticLoadClass(UEngine::StaticClass(), 
        nullptr, *ConfigEngineClassName);

    // 2. 创建 GEngine 单例
    GEngine = NewObject<UEngine>(GetTransientPackage(), EngineClass);

    // 3. 引擎初始化 → 创建 GameInstance
    GEngine->ParseCommandline();
    GEngine->Init(this);

    // 4. 加载 PostEngineInit 模块
    IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostEngineInit);
    IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostEngineInit);

    // 5. 启动 → 加载第一个地图
    GEngine->Start();

    return 0;
}

这里是EngineLoop::Init

我们的记忆点是,基于Game还是Editor初始化了引擎。UGameEngine 还是 UUnrealEdEngine

然后

GEngine->Init();

GEngine->Start(); // 第一个地图

UGameEngine::Init / Start

包括GameInstance的创建初始化

UGameViewportClient 的创建,代表屏幕本身,是渲染输入音频的顶层接口

LocalPlayer 的创建,代表本地玩家

// GameEngine.cpp
void UGameEngine::Init(IEngineLoop* InEngineLoop)
{
    // 1. 基类初始化 (UEngine::Init)
    UEngine::Init(InEngineLoop);

    // 2. 加载游戏用户设置
    GetGameUserSettings()->LoadSettings();

    // 3. 创建 GameInstance (核心!)
    // 从配置获取 GameInstanceClass (DefaultEngine.ini → GameMapsSettings)
    FSoftClassPath GameInstanceClassName = GetDefault<UGameMapsSettings>()->GameInstanceClass;
    UClass* GameInstanceClass = LoadObject<UClass>(...);

    GameInstance = NewObject<UGameInstance>(this, GameInstanceClass);

    // 4. 初始化 GameInstance (创建 WorldContext + 临时 World)
    GameInstance->InitializeStandalone();

    // 5. 创建 GameViewportClient
    UGameViewportClient* ViewportClient = NewObject<UGameViewportClient>(...);
    ViewportClient->Init(...);
    GameViewport = ViewportClient;

    // 6. 创建初始 LocalPlayer
    GameInstance->CreateInitialPlayer(Error);
}

GameEngine Init 中 完成了

  • GameUserSetting
  • GameInstance(World Context + 临时的World)
  • 创建GameViewportClient 视口的抽象
  • 创建LocalPlayer
void UGameEngine::Start()
{
    GameInstance->StartGameInstance(); // !!!这里是我们的兴趣点,一会看看
    // → 解析命令行获取初始地图
    // → Engine->Browse() → 加载地图 → InitializeActorsForPlay → BeginPlay
}

UGameInstance

UGameInstance 是整个游戏运行期间唯一不随 Map Travel 销毁的高级对象。

核心职责

  • 管理 WorldContext:关联当前 UWorld
  • 管理 LocalPlayer:本地玩家列表,非分屏一遍只有一个吧
  • 跨地图持久数据:全局设置、系统配置
  • 地图旅行控制:Browse / ServerTravel
  • 子系统宿主:UGameInstanceSubsystem

初始化流程

UGameEngine::Init()
  │
  ├── NewObject<UGameInstance>()           // 创建实例
  │
  ├── GameInstance->InitializeStandalone() // 创建 WorldContext + 临时 World
  │     ├── CreateNewWorldContext(EWorldType::Game)
  │     ├── UWorld::CreateWorld()           // 临时空世界
  │     ├── WorldContext->SetCurrentWorld()
  │     └── GameInstance->Init()            // ← 真正的初始化入口
  │           ├── ReceiveInit()             // Blueprint 事件
  │           ├── 创建 OnlineSession
  │           ├── 注册网络加密委托
  │           └── SubsystemCollection.Initialize(this) // 初始化子系统
  │
  └── GameInstance->CreateInitialPlayer()  // 创建首个 LocalPlayer(GIsClient判断)

关键方法

class UGameInstance : public UObject
{
    // === 生命周期 ===
    virtual void Init();                    // 初始化,创建子系统
    virtual void Shutdown();                // 关闭,清理资源
    virtual void StartGameInstance();       // 开始游戏,加载首个地图
    virtual void OnStart();                 // Browse 成功后回调

    // === LocalPlayer 管理 ===
    ULocalPlayer* CreateInitialPlayer(FString& OutError);
    ULocalPlayer* CreateLocalPlayer(int32 ControllerId, FString& OutError, bool bSpawnPlayerController);
    bool RemoveLocalPlayer(ULocalPlayer* ExistingPlayer);

    // === 地图旅行 ===
    // 通过 Engine->Browse() 执行地图切换

    // === 数据 ===
    TArray<ULocalPlayer*> LocalPlayers;     // 本地玩家列表
    UOnlineSession* OnlineSession;          // 在线会话
    FObjectSubsystemCollection<UGameInstanceSubsystem> SubsystemCollection;
    FWorldContext* WorldContext;             // 关联的世界上下文
};

StartGameInstance 加载首个地图

void UGameInstance::StartGameInstance()
{
    // 1. 创建默认 URL
    FURL DefaultURL;

    // 2. 解析命令行获取地图名
    const UGameMapsSettings* GameMapsSettings = GetDefault<UGameMapsSettings>();
    const FString& DefaultMap = GameMapsSettings->GetGameDefaultMap();
    // 命令行可覆盖默认地图

    // 3. 执行 Browse → 加载地图
    FURL URL(&DefaultURL, *PackageName, TRAVEL_Partial);
    BrowseRet = Engine->Browse(*WorldContext, URL, Error);
    // Browse 内部: LoadMap → InitWorld → InitializeActorsForPlay → BeginPlay

    // 4. 广播 OnStart
    BroadcastOnStart();
    // → FWorldDelegates::OnStartGameInstance
    // → OnStart() (可重写)
}

这一步Browse里面的 地图加载,并初始化一些东西。

World 初始化

在 UEngine::LoadMap 中,从磁盘加载Package地图包,此时World被创建。

UPackage* WorldPackage = LoadPackage(nullptr, URL.Map, LOAD_None);
NewWorld = UWorld::FindWorldInPackage(WorldPackage);

WorldContext.World()->SetGameMode(URL); // 这里会创建 GameMode

这样我们就有了一个world

UWorld::InitializeActorsForPlay

void UWorld::InitializeActorsForPlay(const FURL& InURL, bool bResetTime)
{
    // === 阶段1: 更新世界组件 ===
    UpdateWorldComponents(bRerunConstructionScript, true);

    // === 阶段2: 初始化网络 Actor ===
    for (ULevel* Level : Levels)
    {
        Level->InitializeNetworkActors();
        // 设置 Actor 的 Role/RemoteRole
        // 注册需要复制的 Actor
    }

    // === 阶段3: Spawn 服务端 Actor (DS/Listen) ===
    if (CurNetMode == NM_ListenServer || CurNetMode == NM_DedicatedServer)
    {
        GEngine->SpawnServerActors(this);
    }

    // === 阶段4: 初始化 GameMode ===
    if (AuthorityGameMode && !AuthorityGameMode->IsActorInitialized())
    {
        AuthorityGameMode->InitGame(MapName, Options, Error);
        // → 创建 GameSession
        // → 注册服务器会话
    }

    // === 阶段5: 路由 Actor 初始化 (核心!) === 
    // 这里面会跑很多重要流程
    for (ULevel* Level : Levels)
    {
        Level->RouteActorInitialize(0);  // 0 = 处理所有
        // → PreInitializeComponents()
        // → InitializeComponents()
        // → PostInitializeComponents()
    }

    // === 阶段6: 对关卡中的 Actor 排序 ===
    for (ULevel* Level : Levels)
    {
        Level->SortActorList();
        // 静态非网络相关 → 静态网络相关 → 其他
    }

    // === 阶段7: 广播初始化完成 ===
    OnActorsInitialized.Broadcast(OnActorInitParams);
    FWorldDelegates::OnWorldInitializedActors.Broadcast(OnActorInitParams);

    // === 阶段8: 初始化导航和 AI 系统 ===
    NavigationSystem->OnInitializeActors();
    AISystem->InitializeActorsForPlay(bResetTime);
}

从 Engine->Browse(*WorldContext, URL, Error); 过来

最后到 UEngine::LoadMap

LoadMap()
  │
  ├── 创建/加载 UWorld
  ├── InitWorld()
  │
  └── InitializeActorsForPlay()
        │
        ├── UpdateWorldComponents()
        ├── Level->InitializeNetworkActors()      // 每个 Level
        ├── SpawnServerActors()                    // 仅 DS/Listen
        ├── GameMode->InitGame()                   // 创建 GameSession
        ├── Level->RouteActorInitialize()          // 每个 Level
        │     ├── Actor->PreInitializeComponents() // 阶段1: 所有Actor
        │     ├── Actor->InitializeComponents()    // 阶段2: 所有Actor
        │     └── Actor->PostInitializeComponents()// 阶段2: 所有Actor
        ├── Level->SortActorList()
        ├── OnActorsInitialized 广播
        └── Nav/AI 系统初始化

Actor 初始化顺序

三个Init,然后到 BeginPlay

void ULevel::RouteActorInitialize(int32 NumActorsToProcess)
{
    switch (RouteActorInitializationState)
    {
        // === 阶段1: PreInitialize (可能 Spawn 新 Actor) ===
        case ERouteActorInitializationState::Preinitialize:
        {
            while (Index < Actors.Num())  // 注意: Actors.Num()可能增长
            {
                if (Actor && !Actor->IsActorInitialized())
                {
                    Actor->PreInitializeComponents();
                }
            }
            // Fall-through...
        }

        // === 阶段2: Initialize + PostInitialize ===
        case ERouteActorInitializationState::Initialize:
        {
            while (Index < Actors.Num())
            {
                if (Actor && !Actor->IsActorInitialized())
                {
                    Actor->InitializeComponents();
                    Actor->PostInitializeComponents();
                    // Fatal if PostInitializeComponents 没有正确调用 Super!
                }
            }
            // Fall-through...
        }

        // === 阶段3: BeginPlay (仅在 World 已 BeginPlay 时) ===
        case ERouteActorInitializationState::BeginPlay:
        {
            if (OwningWorld->HasBegunPlay())
            {
                while (Index < Actors.Num())
                {
                    if (Actor && !Actor->IsChildActor())
                    {
                        Actor->DispatchBeginPlay(bFromLevelStreaming);
                    }
                }
            }
        }
    }
}
阶段 方法 典型操作
PreInitialize PreInitializeComponents() 可以 Spawn 新 Actor(如 GameMode spawns GameState)
Initialize InitializeComponents() 注册所有组件到世界
PostInitialize PostInitializeComponents() 组件注册完毕,我们在这里初始化东西(如 PlayerState, CameraManager)
BeginPlay DispatchBeginPlay() 游戏逻辑启动(仅在 World BeginPlay 后,如 Streaming Level 延迟加载)

这里注意:GameMode也是一个Actor,所以也跑这几个初始化,

其中会创建几个重要的东西,例如GameState,GameNetworkManager,调用InitGameState,在后面一点

World BeginPlay

void UWorld::BeginPlay()
{
    // 1. Streaming Level 可见性 (DS/Listen)
    if (IsNetMode(NM_DedicatedServer) || IsNetMode(NM_ListenServer))
    {
        ServerStreamingLevelsVisibility = AServerStreamingLevelsVisibility::SpawnServerActor(this);
    }

    // 2. World 子系统 BeginPlay
    SubsystemCollection.ForEachSubsystem([](UWorldSubsystem* WorldSubsystem){
        WorldSubsystem->OnWorldBeginPlay(*this);
    });

    // 3. GameMode StartPlay (核心!)
    AGameModeBase* GameMode = GetAuthGameMode();
    if (GameMode)
    {
        GameMode->StartPlay();
        // → GameState->HandleBeginPlay()
        // → 标记 bReplicatedHasBegunPlay = true

        // AI 系统启动
        GetAISystem()->StartPlay();
    }

    // 4. 广播 World BeginPlay
    OnWorldBeginPlay.Broadcast();

    // 5. 物理场景 BeginPlay
    PhysicsScene->OnWorldBeginPlay();
}

生命周期

创建 ──→ InitWorld ──→ InitializeActorsForPlay ──→ BeginPlay
  │                                                      │
  │                                                      ▼
  │                                                   Tick 循环
  │                                                      │
  │                                                      ▼
  │                                                  EndPlay
  │                                                      │
  └──────────────── CleanupWorld ◄───── BeginTearingDown ─┘

总结:

到这里,我们以及有了

AGameModeBase

AGameSession

AGameNetworkManager

AGameState

AGameModeBase

仅存在于服务端。

核心类

class AGameModeBase : public AInfo
{
    // === 关键类引用(在项目设置/地图设置中配置)===
    TSubclassOf<AGameStateBase>      GameStateClass;         // 默认 AGameStateBase
    TSubclassOf<APlayerController>   PlayerControllerClass;  // 默认 APlayerController
    TSubclassOf<APlayerState>        PlayerStateClass;       // 在 GameState 中配置
    TSubclassOf<APawn>               DefaultPawnClass;       // 默认 ADefaultPawn
    TSubclassOf<AHUD>                HUDClass;               // 默认 AHUD
    TSubclassOf<ASpectatorPawn>      SpectatorClass;         // 默认 ASpectatorPawn

    // === 运行时引用 ===
    AGameStateBase* GameState;        // 当前 GameState 实例
    AGameSession* GameSession;        // 当前 GameSession 实例
};

GameMode 初始化时序

World::InitializeActorsForPlay()
  │
  ├── GameMode->InitGame(MapName, Options, Error)
  │     ├── SpawnGameSessionActor → AGameSession
  │     └── GameSession->RegisterServer()
  │
  └── Level->RouteActorInitialize()
        │
        ├── GameMode->PreInitializeComponents() ← 关键!
        │     ├── SpawnActor<AGameStateBase>(GameStateClass)
        │     ├── World->SetGameState(GameState)
        │     ├── SpawnActor<AGameNetworkManager>
        │     └── GameMode->InitGameState()
        │           ├── GameState->GameModeClass = GetClass()
        │           └── GameState->SpectatorClass = SpectatorClass
        │
        ├── GameMode->InitializeComponents()
        └── GameMode->PostInitializeComponents()

InitGame

void AGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
    // 1. 解析选项 (MaxPlayers, GamePassword 等)

    // 2. Spawn GameSession
    UWorld* World = GetWorld();
    FActorSpawnParameters SpawnInfo;
    SpawnInfo.Instigator = GetInstigator();
    GameSession = World->SpawnActor<AGameSession>(GetGameSessionClass(), SpawnInfo);
    GameSession->InitOptions(Options);

    // 3. 注册服务器 (仅 DS/Listen)
    if (GetNetMode() != NM_Standalone)
    {
        GameSession->RegisterServer();
    }
}

Lyra就是在这里启动体验加载的。

PreInitializeComponents

GameMode 也是 Actor,上面Actor初始化的这个步骤会跑到这里。

void AGameModeBase::PreInitializeComponents()
{
    Super::PreInitializeComponents();

    // 1. Spawn GameState (全网复制的游戏状态)
    GameState = World->SpawnActor<AGameStateBase>(GameStateClass);
    World->SetGameState(GameState);

    // 2. Spawn NetworkManager
    World->SpawnActor<AGameNetworkManager>(World->GameNetworkManagerClass);

    // 3. 配置 GameState
    InitGameState();
    // → GameState->GameModeClass = GetClass()
    // → GameState->SpectatorClass = SpectatorClass
}

StartPlay

对所有Actor执行BeginPlay

void AGameModeBase::StartPlay()
{
    // 在 World::BeginPlay() 中调用
    GameState->HandleBeginPlay();
    // → 设置 bReplicatedHasBegunPlay = true (复制到客户端)
    // → 对所有 Actor 执行 BeginPlay
}

AGameStateBase

核心属性

class AGameStateBase : public AInfo
{
    // === 复制的属性 ===
    UPROPERTY(Replicated)
    TSubclassOf<AGameModeBase> GameModeClass;    // GameMode 类 (客户端可知)

    UPROPERTY(Replicated)
    TSubclassOf<ASpectatorPawn> SpectatorClass;  // 观战 Pawn 类

    UPROPERTY(Replicated)
    bool bReplicatedHasBegunPlay;                 // World 是否已 BeginPlay

    UPROPERTY(Replicated)
    double ReplicatedWorldTimeSeconds;            // 同步的服务器时间

    // === 仅服务端 ===
    AGameModeBase* AuthorityGameMode;             // GameMode 引用 (仅服务端有效)

    // === 玩家列表 ===
    UPROPERTY(Replicated)
    TArray<TObjectPtr<APlayerState>> PlayerArray; // 所有玩家状态
};

关键方法

// 服务端调用,标记游戏开始
void HandleBeginPlay()
{
    bReplicatedHasBegunPlay = true;
    // → 所有 Actor DispatchBeginPlay
    // → 客户端收到复制后也执行 BeginPlay
}

// 客户端回调
void OnRep_ReplicatedHasBegunPlay()
{
    // 当 bReplicatedHasBegunPlay 从服务端复制到客户端时
    // 触发客户端的 BeginPlay 流程
}

// 玩家管理
void AddPlayerState(APlayerState* PlayerState);
void RemovePlayerState(APlayerState* PlayerState);

登录流程

当客户端连接到服务端时,发生以下流程(全部在服务端执行):

客户端连接请求
  │
  ▼
AGameModeBase::PreLogin(Options, Address, UniqueId, ErrorMessage)
  │  ← 验证客户端是否允许加入 (如: 满员、被封禁等)
  │  ← ErrorMessage 非空则拒绝
  │
  ▼
AGameModeBase::Login(NewPlayer, InRemoteRole, Portal, Options, UniqueId, ErrorMessage)
  │
  ├── GameSession->ApproveLogin(Options)    // 会话验证
  │
  ├── SpawnPlayerController(InRemoteRole, Options)
  │     └── SpawnPlayerControllerCommon(InRemoteRole, Location, Rotation, PlayerControllerClass)
  │           ├── SpawnActor<APlayerController>(bDeferConstruction = true)
  │           ├── 设置本地/远程角色
  │           └── FinishSpawningActor() → 触发 PostInitializeComponents
  │                 └── PC->PostInitializeComponents()
  │                       ├── InitPlayerState() → Spawn APlayerState
  │                       ├── SpawnPlayerCameraManager()
  │                       └── AddCheats()
  │
  └── InitNewPlayer(NewPC, UniqueId, Options, Portal)
        ├── GameSession->RegisterPlayer()
        ├── 查找 PlayerStart
        └── 设置玩家名等信息
  │
  ▼
AGameModeBase::PostLogin(NewPlayer)
  │
  ├── GenericPlayerInitialization(NewPlayer) // 通用初始化 (也用于 SeamlessTravel)
  ├── ClientCapBandwidth()                  // 带宽设置
  ├── GameSession->PostLogin()              // 会话通知
  ├── OnPostLogin() → K2_PostLogin()        // Blueprint 事件
  │
  └── HandleStartingNewPlayer(NewPlayer)
        │
        └── RestartPlayer(NewPlayer)         // 为玩家创建 Pawn
              ├── FindPlayerStart()          // 查找出生点
              ├── SpawnDefaultPawnFor()      // Spawn DefaultPawnClass
              │     └── SpawnDefaultPawnAtTransform()
              │           └── SpawnActor<APawn>(DefaultPawnClass)
              └── FinishRestartPlayer()
                    ├── NewPlayer->Possess(Pawn) // 控制 Pawn
                    │     └── Pawn->PossessedBy(Controller)
                    │           ├── SetOwner(Controller)
                    │           ├── SetController(Controller)
                    │           ├── SetPlayerState(Controller->PlayerState)
                    │           ├── SetReplicates(true)
                    │           └── SetAutonomousProxy(true) // 非本地
                    ├── SetPlayerDefaults(Pawn)
                    └── K2_OnRestartPlayer()
服务端                                     客户端
  │                                          │
  │◄──────── 连接请求 ────────────────────────│
  │                                          │
  ├── PreLogin()  (验证)                      │
  │     └── 拒绝? → 返回错误                   │
  │                                          │
  ├── Login()                                │
  │     ├── SpawnPlayerController()          │
  │     │     └── PC->PostInitializeComponents()
  │     │           └── InitPlayerState()    │
  │     └── InitNewPlayer()                  │
  │                                          │
  ├── PostLogin()                            │
  │     └── HandleStartingNewPlayer()        │
  │           └── RestartPlayer()            │
  │                 ├── SpawnDefaultPawnFor() │
  │                 └── Possess()            │
  │                                          │
  ├── ─── 复制 PC 到客户端 ─────────────────→ │
  ├── ─── 复制 PlayerState 到所有客户端 ────→  │
  ├── ─── 复制 Pawn 到客户端 ───────────────→  │
  │                                          │
  │                                    PC->BeginPlay()
  │                                    Pawn->BeginPlay()

小结一下:

GameInstance 开始加载梯度,->Brose 里面会 load map,初始化World。

World 初始化 GameMode。GameMode是个Actor,所以参与Actor的初始化。创建出了GameState。

玩家登录,触发GameMode::Login的时候,创建了PlayerController,有创建出PlayerState,然后PC到客户端,PS到所有客户端。

APlayerController

PlayerController 代表玩家的"意志",是玩家输入和游戏世界之间的桥梁。

  • 服务端: 由 GameMode 在 Login 时 Spawn
  • 客户端: 由网络复制创建(仅该客户端拥有的 PC 会被复制过来)

主要负责包括,Camera,HUD,Cheat

PostInitializeComponents

void APlayerController::PostInitializeComponents()
{
    Super::PostInitializeComponents();

    // 1. 服务端创建 PlayerState
    if (GetNetMode() != NM_Client)
    {
        InitPlayerState();
        // → SpawnActor<APlayerState>(PlayerStateClass)
        // → PlayerState 设置 OwnerPC、PlayerName 等
    }

    // 2. 创建 CameraManager (所有端)
    SpawnPlayerCameraManager();
    ResetCameraMode();

    // 3. 客户端创建 HUD
    if (GetNetMode() == NM_Client)
    {
        SpawnDefaultHUD();
    }

    // 4. 添加 CheatManager
    AddCheats();

    // 5. 初始为观战状态
    bPlayerIsWaiting = true;
    StateName = NAME_Spectating;
}

BeginPlay

void APlayerController::BeginPlay()
{
    Super::BeginPlay();

    // 处理鼠标锁定
    if (LocalPlayer && LocalPlayer->ViewportClient)
    {
        if (LocalPlayer->ViewportClient->ShouldAlwaysLockMouse())
        {
            LocalPlayer->GetSlateOperations().LockMouseToWidget(
                LocalPlayer->ViewportClient->GetGameViewportWidget().ToSharedRef());
        }
    }

    // 处理模拟触摸事件的光标显示
    if (FSlateApplication::Get().IsFakingTouchEvents())
    {
        SetShowMouseCursor(true);
    }
}

BeginPlayingState

void APlayerController::BeginPlayingState()
{
    // 默认空实现
    // 当 PC 从 Spectating(观众) 切换到 Playing 状态时调用
    // 子类可重写以执行游戏开始时的初始化逻辑
}
方法 说明 调用时机
PostInitializeComponents() 创建 PlayerState、CameraManager Spawn 后立即
BeginPlay() 鼠标锁定设置 World BeginPlay / 复制完成
OnPossess(APawn*) 控制 Pawn Possess() 调用
OnUnPossess() 释放 Pawn UnPossess() 调用
SetupInputComponent() 绑定输入 创建 InputComponent 时
SpawnPlayerCameraManager() 创建相机管理器 PostInitializeComponents
BeginPlayingState() 进入游戏状态 状态切换
ClientRestart(APawn*) 客户端重启通知 服务端调用
SetPlayer(UPlayer*) 关联 LocalPlayer/NetConnection 登录完成

APlayerState

PlayerState 是玩家在服务端的持久身份记录,复制到所有客户端。

所有 PlayerState 复制到所有客户端 (在 GameState->PlayerArray 中)

核心属性

class APlayerState : public AInfo
{
    // === 全部 Replicated ===
    UPROPERTY(Replicated)
    float Score;                    // 玩家分数

    UPROPERTY(Replicated)
    int32 PlayerId;                 // 唯一玩家ID

    UPROPERTY(Replicated) 
    uint8 Ping;                     // 网络延迟

    UPROPERTY(Replicated)
    uint8 bIsSpectator : 1;         // 是否是观战者

    UPROPERTY(Replicated)
    uint8 bIsABot : 1;              // 是否是Bot

    UPROPERTY(Replicated)
    uint8 bIsInactive : 1;          // 是否处于非活跃状态

    UPROPERTY(Replicated)
    FString PlayerNamePrivate;      // 玩家名

    // === 仅服务端 ===
    FString SavedNetworkAddress;    // 用于 seamless travel 重连

    // === 非复制 ===
    APawn* PawnPrivate;             // 关联的 Pawn (仅拥有者可见)
};

这个score好像从来没用过呀。给用户留的?

PlayerState 的生命周期特殊性:

  • 跨 Pawn 存在: 当 Pawn 被销毁(如死亡),PlayerState 仍然存在
  • Seamless Travel: PlayerState 可以在无缝旅行中被保留(通过 CopyPropertiesOverrideWith
  • 非活跃状态: 玩家断开连接后,PlayerState 不立即销毁,而是标记为 bIsInactive

APawn / ACharacter 初始化与 Possess

Pawn 的 Spawn 流程

看到lyra里面熟悉的接口了,服务器拿着 Controller来GameMode->RestartPlayer

GameMode->RestartPlayer(PC)
  │
  ├── FindPlayerStart(PC)              // 查找出生点
  │
  ├── SpawnDefaultPawnFor(PC, StartSpot)
  │     └── SpawnDefaultPawnAtTransform(PC, Transform)
  │           └── World->SpawnActor<APawn>(DefaultPawnClass)
  │                 ├── PreInitializeComponents()
  │                 ├── InitializeComponents()
  │                 └── PostInitializeComponents()
  │
  └── FinishRestartPlayer(PC, SpawnRotation)
        ├── PC->Possess(NewPawn)
        │     └── OnPossess(InPawn)
        │           ├── UnPossess() (如果已有 Pawn)
        │           ├── InPawn->PossessedBy(this) ← 核心
        │           ├── SetPawn(InPawn)
        │           ├── SetControlRotation(Pawn->GetActorRotation())
        │           └── Pawn->DispatchRestart()
        │
        ├── SetPlayerDefaults(Pawn)
        └── K2_OnRestartPlayer()

PossessedBy

void APawn::PossessedBy(AController* NewController)
{
    // 1. 设置 Owner 为 Controller (网络复制 Owner 判断)
    SetOwner(NewController);

    // 2. 设置 Controller 引用
    SetController(NewController);
    ForceNetUpdate();

    // 3. 更新网络连接归属 (Iris)
    UpdateOwningNetConnection();

    // 4. 同步 PlayerState
    if (GetController()->PlayerState != nullptr)
    {
        SetPlayerState(GetController()->PlayerState);
    }

    // 5. 设置网络角色 (仅有 PlayerController)
    if (APlayerController* PC = Cast<APlayerController>(GetController()))
    {
        if (GetNetMode() != NM_Standalone)
        {
            SetReplicates(true);       // 开启复制
            if (!PC->IsLocalController())
            {
                SetAutonomousProxy(true); // 非本地→自治代理
            }
        }
    }

    // 6. Blueprint 事件
    ReceivePossessed(GetController());
    NotifyControllerChanged();
}

APawn vs ACharacter

APawn (基础可控制体)
  ├── NavMovement 能力
  ├── Controller 挂载
  ├── PlayerState 引用
  └── 基础输入绑定

ACharacter : APawn (人形角色)
  ├── UCharacterMovementComponent (CMC)
  │     ├── 行走、跑步、跳跃、下蹲、飞行、游泳
  │     ├── 网络预测与修正
  │     └── Root Motion 支持
  ├── UCapsuleComponent (碰撞胶囊体)
  ├── USkeletalMeshComponent (骨骼网格)
  └── 动画驱动

Pawn 网络角色设置

不想加入listen server的情况

  • 服务端 = 永远 Authority
  • 我的 Pawn 在我的客户端 = AutonomousProxy(可预测,输入不等服务端确认)
  • 别人的 Pawn 在我的客户端 = SimulatedProxy(只能靠插值模拟)

存在可视化

┌────────────────── 专用服务器 (DS) ──────────────────┐
│  UGameInstance                                      │
│  UWorld                                             │
│  AGameMode ← 仅这里有                               │
│  AGameSession  ← 仅这里有                            │
│  AGameState                                         │
│  APlayerController_1 (玩家1)                        │
│  APlayerController_2 (玩家2)                        │
│  APlayerState_1                                     │
│  APlayerState_2                                     │
│  APawn_1                                            │
│  APawn_2                                            │
│  ❌ 无 AHUD, 无 ULocalPlayer                        │
└─────────────────────────────────────────────────────┘

┌────────────────── 客户端1 ──────────────────────────┐
│  UGameInstance (独立)                                │
│  UWorld (独立加载)                                    │
│  ❌ 无 GameMode                                     │
│  AGameState (从服务端复制)                             │
│  APlayerController_1 (仅自己的)                      │
│  APlayerState_1 (从服务端复制)                        │
│  APlayerState_2 (从服务端复制) ← 能看到所有人的         │
│  APawn_1 (AutonomousProxy)                          │
│  APawn_2 (SimulatedProxy)                           │
│  AHUD (本地)                                        │
│  ULocalPlayer (本地)                                 │
│  PlayerCameraManager (本地)                          │
└─────────────────────────────────────────────────────┘

继承关系

UObject
├── USubsystem
│     ├── UDynamicSubsystem
│     │     ├── UEngineSubsystem
│     │     └── UEditorSubsystem
│     └── UGameInstanceSubsystem
│           ├── UWorldSubsystem
│           └── ULocalPlayerSubsystem
│
├── UGameInstance
│     └── [YourGameInstance]
│
├── UPlayer
│     ├── ULocalPlayer
│     └── UNetConnection
│
AActor
├── AInfo
│     ├── AGameModeBase
│     │     └── AGameMode             (含 Match 状态机)
│     │           └── [YourGameMode]
│     ├── AGameStateBase
│     │     └── AGameState            (含 Match 状态同步)
│     │           └── [YourGameState]
│     ├── APlayerState
│     │     └── [YourPlayerState]
│     └── AGameSession
│
├── AController
│     ├── APlayerController
│     │     └── [YourPlayerController]
│     └── AAIController
│
├── APawn
│     ├── ADefaultPawn
│     ├── ASpectatorPawn
│     └── ACharacter
│           └── [YourCharacter]
│
├── AHUD
│     └── [YourHUD]
│
├── APlayerCameraManager
│     └── [YourCameraManager]
│
└── AWorldSettings
      └── [YourWorldSettings]

引擎时序图

初始化

═══════════════════════════════════════════════════════════════
  1. 进程启动 → GuardedMain()
═══════════════════════════════════════════════════════════════
  │
  ├── 2. FEngineLoop::PreInit()
  │     ├── PreInitPreStartupScreen()
  │     │     ├── 基础系统: 日志、文件系统、命令行
  │     │     ├── LoadCoreModules() → CoreUObject
  │     │     ├── LoadPreInitModules() → Engine, Renderer, RHI
  │     │     └── ProcessNewlyLoadedUObjects()
  │     │
  │     └── PreInitPostStartupScreen()
  │           ├── LoadStartupCoreModules() → Core, Networking, Slate
  │           ├── LoadStartupModules() → 项目/插件模块
  │           └── UObject/GC 系统完全就绪
  │
  ├── 3. FEngineLoop::Init()
  │     │
  │     ├── 创建 GEngine (UGameEngine)
  │     │
  │     ├── GEngine->Init()
  │     │     ├── UEngine::Init()  (基类)
  │     │     ├── NewObject<UGameInstance>(GameInstanceClass)
  │     │     ├── GameInstance->InitializeStandalone()
  │     │     │     ├── CreateNewWorldContext(EWorldType::Game)
  │     │     │     ├── UWorld::CreateWorld() (临时空世界)
  │     │     │     └── GameInstance->Init()
  │     │     │           ├── ReceiveInit() (BP)
  │     │     │           ├── 创建 OnlineSession
  │     │     │           └── SubsystemCollection.Initialize()
  │     │     │                 └── GameInstanceSubsystems 初始化
  │     │     │
  │     │     ├── 创建 GameViewportClient
  │     │     └── CreateInitialPlayer() → ULocalPlayer
  │     │           └── LocalPlayerSubsystems 初始化
  │     │
  │     ├── 加载 PostEngineInit 模块
  │     │
  │     └── GEngine->Start()
  │           └── GameInstance->StartGameInstance()
  │                 │
  │                 ├── 解析默认地图 URL
  │                 └── Engine->Browse(URL)
  │                       │
  │                       └── LoadMap()
  │
  ├── 4. LoadMap → World 初始化
  │     │
  │     ├── 加载/创建 UWorld
  │     ├── InitWorld()
  │     │     └── WorldSubsystems 初始化
  │     │
  │     ├── InitializeActorsForPlay()
  │     │     ├── UpdateWorldComponents()
  │     │     ├── Level->InitializeNetworkActors()
  │     │     ├── SpawnServerActors() [DS/Listen]
  │     │     │
  │     │     ├── GameMode->InitGame()
  │     │     │     └── Spawn AGameSession
  │     │     │
  │     │     ├── Level->RouteActorInitialize()
  │     │     │     │
  │     │     │     ├─ Phase 1: PreInitializeComponents (所有Actor)
  │     │     │     │    └── GameMode->PreInitializeComponents()
  │     │     │     │          ├── Spawn AGameState
  │     │     │     │          ├── Spawn AGameNetworkManager
  │     │     │     │          └── InitGameState()
  │     │     │     │
  │     │     │     ├─ Phase 2: InitializeComponents + PostInitializeComponents
  │     │     │     └─ Phase 3: BeginPlay (等 World BeginPlay 后)
  │     │     │
  │     │     ├── OnActorsInitialized 广播
  │     │     └── Nav/AI 系统初始化
  │     │
  │     └── BeginPlay()
  │           ├── WorldSubsystem->OnWorldBeginPlay()
  │           ├── GameMode->StartPlay()
  │           │     └── GameState->HandleBeginPlay()
  │           │           └── bReplicatedHasBegunPlay = true
  │           ├── AISystem->StartPlay()
  │           ├── 所有 Actor DispatchBeginPlay()
  │           └── OnWorldBeginPlay 广播
  │
  ├── 5. 本地玩家登录 (Standalone/ListenServer)
  │     ├── SpawnPlayActor() for LocalPlayer
  │     ├── GameMode->Login() → Spawn PC
  │     ├── PC->PostInitializeComponents()
  │     │     ├── InitPlayerState()
  │     │     └── SpawnPlayerCameraManager()
  │     ├── GameMode->PostLogin(PC)
  │     └── HandleStartingNewPlayer(PC)
  │           └── RestartPlayer(PC)
  │                 ├── SpawnDefaultPawnFor()
  │                 └── Possess()
  │
  └── 6. EngineTick() → 主循环开始
        └── 每帧: World->Tick() → 所有 Actor Tick

远程玩家加入 (客户端连接 DS)

═════════ 客户端 ════════════════════ 服务端 ═══════════════
  │                                      │
  ├── 发起连接 ───────────────────────→   │
  │                                      │
  │                                      ├── PreLogin()
  │                                      │     └── 验证通过
  │                                      │
  │                                      ├── Login()
  │                                      │     ├── SpawnPlayerController()
  │                                      │     │     └── PC->PostInitializeComponents()
  │                                      │     │           ├── InitPlayerState()
  │                                      │     │           └── SpawnPlayerCameraManager()
  │                                      │     └── InitNewPlayer()
  │                                      │
  │                                      ├── PostLogin(PC)
  │                                      │     ├── GenericPlayerInitialization()
  │                                      │     └── HandleStartingNewPlayer(PC)
  │                                      │           └── RestartPlayer(PC)
  │                                      │                 ├── SpawnDefaultPawnFor()
  │                                      │                 └── FinishRestartPlayer()
  │                                      │                       └── PC->Possess(Pawn)
  │                                      │                             └── Pawn->PossessedBy()
  │                                      │
  │◄──── 复制 PC 到客户端 ──────────────   │
  │◄──── 复制 PlayerState 到所有客户端 ──  │
  │◄──── 复制 Pawn (AutonomousProxy) ──   │
  │                                      │
  ├── PC->PostInitializeComponents()     │
  │     └── SpawnPlayerCameraManager()   │
  │                                      │
  ├── PC->BeginPlay()                    │
  ├── Pawn->BeginPlay()                  │
  └── 开始 Tick                           │
上一篇
下一篇