关卡与 World 流转
自动关联目录:关卡与 World 流转
"切场景"在 UE 里不是一个动作,而是两类机制:换 World(Travel)与在当前 World 里加载/卸载子关卡(Level Streaming)。选错会出现"玩家数据丢了"或"卡顿"。
一句话定位:要换规则换玩家 → Travel;要无感地拼接大世界 → Streaming。而 Travel 又分"无缝"和"硬切",区别是玩家对象是否保留。
三种手段
| 手段 | 用途 | 玩家对象 |
|---|---|---|
OpenLevel / ServerTravel(非无缝) | 换关卡、回主菜单 | 全部重建 |
| 无缝 Travel(Seamless) | 换关卡但保留玩家 | PlayerController / PlayerState 保留 |
| Level Streaming | 大世界按需加载 | 不变 |
非无缝 Travel
UGameplayStatics::OpenLevel(GetWorld(), FName("/Game/Maps/Forest")); // 单机/客户端
GetWorld()->ServerTravel(TEXT("/Game/Maps/Forest?listen")); // 服务端带所有客户端走流程:断开 → 卸载 World → 加载新 World → 重新登录(走一遍 PreLogin / PostLogin)。
所有玩家状态都会丢,除非你自己写到 GameInstance 或存档里。
无缝 Travel
// GameMode 开启
AMyGameMode::AMyGameMode()
{
bUseSeamlessTravel = true;
}
// 触发
GetWorld()->ServerTravel(TEXT("/Game/Maps/Forest"), /*bAbsolute=*/false, /*bShouldSkipGameNotify=*/false);| 保留 | 不保留 |
|---|---|
PlayerController | Pawn(会重新生成) |
PlayerState | 关卡里的 AGameMode(新关卡用新的) |
UGameInstance | 关卡 Actor |
| 已加载的 Subsystem(GameInstance 级) | UWorldSubsystem |
关键回调:
// GameMode:玩家在新关卡就位
virtual void HandleSeamlessTravelPlayer(AController*& C) override;
// PlayerController:跨关卡保留的数据
virtual void GetSeamlessTravelActorList(bool bToEntry, TArray<AActor*>& ActorList) override;默认除了 Controller 和 PlayerState,其它 Actor 都不会跟着走。想带上某个 Actor(比如玩家的背包实体),要在 GetSeamlessTravelActorList 里显式加入。
Transition Map
无缝 Travel 中间会加载一个过渡关卡(TransitionMap,默认是个空场景)来顶住加载时间。
[/Script/EngineSettings.GameMapsSettings]
TransitionMap=/Game/Maps/Transition忘了配 TransitionMap,无缝 Travel 会退化成硬切,表现为"说好的保留玩家数据怎么没了"。
Level Streaming
// 蓝图/C++ 加载子关卡
UGameplayStatics::LoadStreamLevel(GetWorld(), FName("Forest_East"), true, false, FLatentActionInfo());
UGameplayStatics::UnloadStreamLevel(GetWorld(), FName("Forest_East"), FLatentActionInfo());| 机制 | 说明 |
|---|---|
LevelStreamingVolume | 玩家进入体积时自动加载 |
蓝图 Load Stream Level | 手动控制 |
| World Partition(UE5) | 自动按格子流式,超大场景首选 |
AWorldSettings 的 Streaming 距离 | 按距离自动 |
Streaming 是异步的,加载完成前不能假设关卡里的 Actor 存在——要等回调或 OnLevelLoaded。
后台加载与卡顿
| 手段 | 说明 |
|---|---|
| 预加载 | 提前 LoadStreamLevel 但先不显示 |
| 分帧预算 | s.LevelStreamingActorsUpdateTimeLimit |
| 过渡关卡 | 用 Travel 的 TransitionMap 顶住 |
| Loading 界面 | 最朴素也最有效 |
bShouldBlockOnLoad | 强制同步加载,会卡,只在必须时用 |
UGameplayStatics::LoadStreamLevel(World, Name, /*bMakeVisibleAfterLoad=*/true,
/*bShouldBlockOnLoad=*/false, LatentInfo);客户端-服务端一致性
| 事项 | 说明 |
|---|---|
ServerTravel 由服务端发起 | 所有连接的客户端跟着走 |
ClientTravel | 单个客户端换关卡(回主菜单、换服务器) |
| 断线重连 | 需要自己实现,UE 不自动保存 |
| PIE 下的多端 | 编辑器里每个端是独立的 World,调试时注意 |
// 客户端回到主菜单
PC->ClientTravel(TEXT("/Game/Maps/MainMenu"), ETravelType::TRAVEL_Absolute);排查
| 现象 | 原因 |
|---|---|
| 切场景后玩家数据丢了 | 用了非无缝,或数据放在 Pawn/World |
| 无缝 Travel 还是丢了 | 没配 TransitionMap |
| 切场景卡顿 | Streaming 同步加载 / 没预加载 |
| 子关卡里的 Actor 找不到 | 还没加载完就用(见 资源加载) |
| 客户端没跟着换关卡 | 用了 OpenLevel 而非 ServerTravel |
常见坑
| 坑 | 说明 |
|---|---|
用 OpenLevel 期望保留玩家 | 非无缝会全部重建 |
开 bUseSeamlessTravel 但没配 TransitionMap | 退化成硬切 |
跨关卡数据放 UWorldSubsystem | 切场景即销毁 |
在 ServerTravel 后立刻访问新关卡 Actor | 还没加载完 |
| Streaming 关卡没等回调就用 | 异步未就绪 |
| 把过渡关卡打成很大的包 | 过渡反而变慢 |
| 切场景不清理 GameInstance 里的临时状态 | 内存与逻辑逐渐腐败 |
在客户端调 ServerTravel | 无效,必须由服务端发起 |
许可协议:CC BY
作者:Davids
本文链接:https://hustjjd.github.io/f692fa6d.html
更新于:2026年10月10日