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 可以在无缝旅行中被保留(通过
CopyProperties和OverrideWith) - 非活跃状态: 玩家断开连接后,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 │