5.2 玩家生命周期:从匹配加入、重连到退出
一次完整的加入流程
客户端 服务器
│ ClientTravel 到服务器地址
│ ─────────地图加载 & 连接────────→
│ 预登录(PreLogin): ban 检查、版本检查、人数检查
│ ←─────────── 通过/拒绝 ──────────
│ 登录(Login): PlayerController spawn
│ ←─────── 承载玩家(OnPostLogin)─
│ GameMode 选择 Pawn、AssignPlayerState
│ ←── 复制的世界快照(相关 Actors)─
│ 客户端 BeginPlay,UI 初始化完毕
关键排坑点:
- 版本检查放 PreLogin:客户端版本号不对直接拒绝,别等加载完再踢。
- 数据加载顺序:PlayerState 上的持久化数据(等级、装备)由服务器在 OnPostLogin 后异步拉取(DB/WEB),加载完成前先给玩家一个观战/等待状态,别让「空数据玩家」短暂参与游戏。
- 重连(Reconnect):玩家网络断了重连回来。要区分「断线重连」(保留原 PlayerState、积分、位置)与「新加入」。实现:为每个玩家生成 SessionId/PlayerId,重连时按 ID 找回旧 PlayerState。
旅行(Travel):从大厅到战局
TRAVEL_Absolute(去一个具体地址) vs TRAVEL_Relative(当前服务器内换地图) vs TRAVEL_Partial(部分加载):
// 客户端:去某个服务器
PC->ClientTravel(TEXT("127.0.0.1:7777"), TRAVEL_Absolute);
// 服务器:全员换地图(配合 seamless travel)
GetWorld()->ServerTravel(TEXT("/Game/Maps/Arena?a=1"));
无缝旅行(Seamless Travel) 是大厅→战局的关键:开启后服务器换地图时客户端不回主菜单、不断连接,直接流式加载新地图。要点:
bUseSeamlessTravel = true(GameMode)- 把需要保留的 Actor 标
bAlwaysRelevant并在 GetSeamlessTravelActorList 里保留(如 PlayerController、PlayerState、规则数据) - 过渡地图(TransitionMap)可给玩家一个加载界面
详见「多人游戏中的旅行」(https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/travelling-in-multiplayer-in-u-54bf)。
退出流程
玩家退出 服务器
│ ClientTravel 离开 / 点退出 / 断线
│ ← Logout(PlayerController 销毁,PlayerState 标记销毁)
│ ← 保存数据、广播通知其他玩家、结算
│ ← 会话层 EndSession(如果最后一人则关服)
要点:
- 数据保存在服务器权威侧:玩家退出时服务器主动存盘,别依赖客户端上报。
- 掉线区分:超时(连接断了)与主动退出,数据保存策略一致,但可以在重连时给「断线补偿」。
- PlayerState 的存活窗口:玩家断线后 PlayerState 保留一段时间再销毁(支持快速重连),由 GameMode 的
InactivePlayerState机制管理。
加入失败的体感
加入失败(服务器满、版本旧、被 ban)要给明确反馈:
| 失败原因 | 给玩家的提示 |
|---|---|
| 服务器已满 | 「房间已满,正在返回匹配」 |
| 版本不匹配 | 「请更新到最新版本」 |
| 被 ban | 「您被限制进入」(给申诉入口) |
| 连接超时 | 「服务器无响应,正在重试(2/3)」 |
没有任何提示直接踢回主菜单是差体验的常见来源——而且玩家会怀疑「是不是我卡了」而不是「服务器的问题」。
心得:玩家生命周期这块「不出彩但出事就是大事」。我们的规范是:每个连接状态迁移(连接中/已连接/加载中/游戏中/掉线/重连)都在 GameMode 里有显式钩子+日志,线上问题 80% 靠这些日志定位。
