本文為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
” 的無縫轉移功能。