本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P34 关卡与大厅之间的过渡(Transition Level And Lobby)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author) Stephen Ulibarri 发布在 Udemy 上的课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
文章目录
- P34 关卡与大厅之间的过渡(Transition Level And Lobby)
- 34.1 无缝与非无缝转移
- 34.2 创建大厅游戏模式 C++ 类
- 34.3 创建过渡关卡
- 34.4 进行测试
- 34.5 Summary
P34 关卡与大厅之间的过渡(Transition Level And Lobby)
本节课将学习虚幻引擎在关卡间进行无缝转移(Seamless Travel)的工作原理以及为何在多人模式游戏更推荐使用无缝转移而不是非无缝转移(Non-seamless Travel);接着,我们将创建一个关卡过渡模式,以便我们在进行关卡转换时自动设置为使用无缝转移;此后我们将创建一个大厅游戏模式,在此模式下,一旦大厅中的玩家人数足够,将开启游戏。
34.1 无缝与非无缝转移
-
虚幻引擎中主要有两种关卡转移(Travel,翻译成切换、漫游也可)方式:无缝转移和非无缝转移方式。两者的主要区别在于,无缝转移是一种非阻塞(Non-blocking)操作,而非无缝转移则是一种阻塞(Blocking)操作。
每当客户端进行非无缝转移时,它都会断开与当前服务器端的连接,然后再重新连接到这个服务器端,而此过程是需要时间的,再次进行重新连接可能会出现问题。根据非无缝转移的特点,它主要出现以下在三种场景中:客户端首次加载地图、客户端首次连接到服务器端以及上一个多人游戏终止下一个多人游戏开始。
-
在不同关卡间进行转移时,更推荐使用无缝转移(It‘s preferred to use seamless travel)。它可以带来更流畅的游戏体验(Smoother exprience),还可以避免当前客户端进行重新连接带来的一系列问题,例如由于太多其他新玩家突然加入先前的服务器,导致当前客户端的玩家被先前的服务器禁止加入,从而使得当前客户端找不到先前的服务器。
想要启用无缝转移,首先需要设置一个过渡地图(Transition map),之所以存在过渡地图,是因为必须始终有一个被加载的世界(用于存放地图),所以在加载新地图之前,我们不能释放原有的地图,由于新旧地图可能会非常大,需要大量的资源(Require a tremendous amount of resources),有了过渡地图,新旧地图就不会同时(Simultanously)存放在存储器内。在设置好过渡地图后,您需要将 游戏模式中的变量 “bUseSeamlessTravel
" 设置为 "true
”,这样就可以实现无缝转移了。
-
在虚幻引擎的多人游戏中,与(关卡)转移的函数包括:
- “
UWorld::ServerTravel
”:此函数仅供服务器端调用。当服务器端调用此函数时,服务器端将会转移至新的关卡,同时所有连接到该服务器的客户端都会随着服务器端转移至这个新的关卡,这个过程是服务器端通过控制所有客户端的玩家控制器并对它们分别调用 “APlayerController:ClientTravel
” 来实现的。 - “
APlayerController:ClientTravel
”:此函数既可以被服务器端调用,也可以被客户端调用。当客户端调用此函数时,客户端将转移至新的服务器,此过程需要提供服务器的新地址;当服务器端调用此函数时,服务器内的所有玩家都将转移至该服务器指定(Specify)的新地图中。
- “
-
我们接下来的任务将是创建一个大厅游戏模式 “
LobbyGameMode
”,在此模式下,一旦大厅中有足够多的玩家时,将调用函数 “UWorld::ServerTravel
”,使得所有连接到服务器端的客户端随着服务器进入游戏地图,开始游戏。
34.2 创建大厅游戏模式 C++ 类
-
在虚幻引擎内容浏览器中新建一个 “
GameMode
” C++ 类,这里不选择 “GameModeBase
” C++类是因为“GameMode
” C++ 类有更多的附加功能(Extra additional functionality),命名为 “LobbyGameMode
”,路径为“.../Blaster/GameMode
”。
-
在 Visual Studio 中打开头文件 “
LobbyGameMode.h
,声明 "PostLogin()
函数的重写。/*** LobbyGameMode.h ***/// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "GameFramework/GameMode.h" #include "LobbyGameMode.generated.h"/*** */ UCLASS() class BLASTER_API ALobbyGameMode : public AGameMode {GENERATED_BODY()/* P34 关卡与大厅之间的过渡(Transition Level And Lobby)*/ public:virtual void PostLogin(APlayerController* Newplayer) override; // 原生(Native)游戏模式类函数 PostLogin() 重写,管理玩家登录// https://dev.epicgames.com/documentation/zh-cn/unreal-engine/game-mode-and-game-state-in-unreal-engine/* P34 关卡与大厅之间的过渡(Transition Level And Lobby)*/ };
-
在虚幻引擎的内容浏览器中,将 “
/LearningKit_Games/Maps
” 目录下的地图资产 “LearningKit_Games_Showcase
” 复制到 “内容” 目录下的 “Maps
” 文件夹中,并重命名为 “BlasterMap
”。接着,在 Visual Studio 中打开 “LobbyGameMode.cpp
”,完成 “PostLogin()
” 函数的重写定义。
-
回到虚幻引擎,打开关卡 “
Lobby
” ,下一步为关卡 “Lobby
” 设置游戏模式而不是用原生C++类(Raw C++ class)游戏模式。在内容浏览器 “内容” 目录下的 “Blueprints
” 文件夹中新建蓝图类 “BP_LobbyGameMode
”,其父类为上文编译好的 “LobbyGameMode
”
/*** LobbyGameMode.cpp ***/// Fill out your copyright notice in the Description page of Project Settings./* P34 关卡与大厅之间的过渡(Transition Level And Lobby)*/ #include "LobbyGameMode.h" // 原来自动生成的代码是 #include "GameMode/LobbyGameMode.h",这里需要把 "GameMode/" 去掉,否则找不到文件 "LobbyGameMode.h" #include "GameFramework/GameStateBase.h" void ALobbyGameMode::PostLogin(APlayerController* Newplayer) {Super::PostLogin(Newplayer); // 调用父类 GameModeBase 的 PostLogin() 函数,用于成功登录后调用。int32 NumberOfPlayers = GameState.Get()->PlayerArray.Num(); // 获取上线玩家人数,调用 GameState 指针需要包含头文件 "GameFramework/GameStateBase.h"// GameState 本质是 UE5 新引入的智能指针包装器(Smart pointer wrapper)—— T 对象指针 TObjectPtr,它包含了一组玩家状态的矩阵(An array of player state)// 虚幻引擎为此提供了追踪访问(access tracking)的功能,它可以检测哪些对象正在使用// https://dev.epicgames.com/documentation/zh-cn/unreal-engine/game-mode-and-game-state-in-unreal-engine?application_version=5.0/** // GameState is used to replicate game state relevant properties to all clients. UPROPERTY(Transient)TObjectPtr<AGameStateBase> GameState;**/// 当玩家人数达到一定数量,转移至下一个关卡// 本节课使用硬编码(Hard coding),即指定从关卡 Lobby 转移到 BlasterMap,之后的课程将使用确切的变量if (NumberOfPlayers == 2) {UWorld* World = GetWorld();if (World) {bUseSeamlessTravel = true; // 设置关卡 Lobby 转移到 BlasterMap 的方式为无缝转移World->ServerTravel(FString("/Game/Maps/BlasterMap?listen")); // 作为监听服务器打开关卡 BlasterMap}} } /* P34 关卡与大厅之间的过渡(Transition Level And Lobby)*/
-
在内容浏览器中双击 “
BP_LobbyGameMode
” 进入蓝图编辑器,在右侧 “细节” 面板的 “类”(Classes)选项卡下将 “默认 Pawn 类”(Default Pawn Class) 设置为 “BP_BlasterCharacter
”,然后编译并进行保存。
-
返回关卡 “
Lobby
” 编辑器,在右侧 “世界场景设置”(World Settings)面板中,将 “游戏模式”(GameMode)选项卡下的“游戏模式重载”(GameMode Override)改为 “BP_LobbyGameMode
”。
-
在设置好关卡 “
Lobby
” 的游戏模式后,此时我们不再需要场景中的 Actor “BP_BlasterCharacter
”,将其删除即可,因为任何抵达该关卡的玩家都会自动在此生成一个角色。点击工具栏的 “播放”(▶)按钮启动运行,可以看到角色自动生成并 “跳入” 关卡中。
-
也可以在勾选 “
BP_LobbyGameMode
” 蓝图编辑器中,在右侧细节面板的 “游戏模式”(Game Mode)选项卡下勾选 “使用无缝漫游”(Use Seamlees Travel) ,这个似乎影响不大,因为我们已经在代码中设置无缝转移了。
34.3 创建过渡关卡
-
在内容浏览器 “内容” 目录下的 “
Maps
” 文件夹中创建一个 “Empty Level
” 过渡关卡,这里选择新建关卡类型为 “Empty Level
” 是因为过渡关卡越 “小” 越好(The smaller and more simple, the better),保存关卡,命名为 “TransitionMap
”。
-
在编辑器上方中打开 “编辑”(Edit) 菜单栏下的 “项目设置”(Project Settings),设置 “转移地图”(Transition Map)为 “
TransitionMap
”。
-
确保 “
GameStartupMap
”、“Lobby
”、“BlasterMap
” 以及 “TransitionMap
” 四个关卡都已经添加到 “项目设置” 的 “打包版本中要包括的地图列表” (List of maps to include in a packaged build)选项中。
34.4 进行测试
-
在打包项目进行测试,可以通过添加已在项目中的资产来丰富关卡,因为它们看起来有些无聊(Sort of boring),具体做法如下:
- 从 “
LearningKit_Games
” 目录中寻找并添加资产到 “GameStartupMap
”,发挥创造力,为准备开始游戏的玩家制作一个可供预览的游戏主界面(use a bit of creativity and show the player a little bit of a preview of what they’re going to see in the game); - 寻找并添加资产到 “
Lobby
” 中,让加入游戏后等待游戏开始的玩家有自由跑动的小区域(A small area that people can run around in); - 最重要的是游戏地图 “
BlasterMap
”,这是多人匹配游戏中玩家进行枪战的地方(A multiplayer matchmaking type of game where players are shooting at each other),需要考虑放置一些障碍掩体(Obstacles)、道具和武器的拾取点(Pickups)以及其他游戏机制(Gameplay mechanics),尝试加入地形高度的变化(Experiment with changes in elevation)以及隐藏点(Hiding spots)。
- 从 “
-
由于笔者比较懒且没有创造力(笑),所以直接使用从 Github 下载的 原项目的地图。
-
可以看到教学视频作者在关卡 “
GameStartupMap
” 和 “Lobby
” 中放置并缩放了(Scale and postion)几个浮岛(Floating island),并添加了一些效果(Add some effect),在关卡 “GameStartupMap
” 还放置了一个骨骼网格体 Actor 类。这里需要重新设置关卡 “Lobby
” 的游戏模式为“BP_LobbyGameMode
”。
-
为关卡 “
BlasterMap
” 新建一个游戏模式蓝图类 “BP_BlasterGameMode
”,并在蓝图编辑器右侧 “细节” 面板的 “类”(Classes)选项卡下将 “默认 Pawn 类”(Default Pawn Class) 设置为 “BP_BlasterCharacter
”,然后编译并进行保存。
-
打开关卡 “
BlasterMap
” 编辑界面,从左侧 “放置 Actor” (Place Actor)面板中拖拽 “玩家出生点”(Player Start) 至场景中,设置该关卡的游戏模式为“BP_BlasterGameMode
”。运行关卡进行测试,玩家角色可以出生在场景中。
-
打包项目发送到另一台机器上,进行多人联机测试。关于如何打包项目以及配置、访问 Steam 以进行测试,可以参见笔者之前的学习笔记:
- 《UE5_C++多人TPS完整教程》学习笔记3 ——《P4 测试多人游戏(Testing Mutiplayer)》
- 《UE5_C++多人TPS完整教程》学习笔记7 ——《P8 为项目配置 Steam(Configuring A Project for Steam)》
- 《UE5_C++多人TPS完整教程》学习笔记8 ——《P9 访问 Steam(Acessing Steam)》
在两台设备上分别登录 Steam 并运行游戏,进入游戏开始界面 “
GameStartupmap
”。
设备 1 点击 “Host
” 按钮创建服务器,进入游戏大厅 “Lobby
” 等待设备 2 (小窗)加入。
设备 2 点击 “Join
” 按钮加入服务器,成功加入设备 1 的服务器,此时两台设备均通过过渡关“TransitionMap
” 无缝转移至游戏关卡 “BlasterMap
”。
两台设备均可以操控人物进行移动,并且能互相看见对方,测试成功。
注意:
如果角色没法上台阶,可以在角色蓝图 “BP_BlasterCharacter
” 的 “角色移动 (CharMoveComp)” 组件里将 “可行走地面角度”(Walkable Floor Angle)的值改大(笔者用的默认数值,没出现这个问题)。
@凯特加莫
34.5 Summary
本节课实现了关卡之间的无缝转移,我们首先分析了虚幻引擎中无缝转移与非无缝转移的工作原理与适用场景,明确了无缝转移在多人游戏中的优势;接着在 Visual Studio 中创建了 “LobbyGameMode
” C++ 类并重写 “PostLogin
” 函数,实现了玩家数量达标时自动触发服务器端无缝转移至游戏地图的逻辑;然后通过新建过渡关卡 “TransitionMap
” 并在项目设置中配置转移地图,确保新旧关卡切换时的资源管理优化;最后通过创建蓝图游戏模式、配置关卡参数以及多机测试,成功测试了玩家从游戏大厅 “Lobby
” 到游戏关卡 “BlasterMap
” 的无缝转移功能。