本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 ------ 《P22 从菜单中加入会话(Join Sessions from The Menu)》 的学习笔记,该系列教学视频为 Udemy 课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
文章目录
- [P22 从菜单中加入会话](#P22 从菜单中加入会话)
- [22.1 搜索会话](#22.1 搜索会话)
- [22.2 加入会话](#22.2 加入会话)
- [22.3 进行测试](#22.3 进行测试)
- [22.4 Summary](#22.4 Summary)
P22 从菜单中加入会话
本节课将借助前几节课自定义的子系统委托及与其绑定的回调函数,实现菜单中的加入按钮具体功能:点击后搜索会话并加入。
22.1 搜索会话
-
在 "
MultiplayerSessionsSubsystem.h
" 中定义一个搜索在线会话设置 "FOnlineSessionSettings
" 类型的变量,保存上次搜索的会话的设置。cpp... UCLASS() class MULTIPLAYERSESSIONS_API UMultiplayerSessionsSubsystem : public UGameInstanceSubsystem { GENERATED_BODY() ... private: // 会话接口智能指针 IOnlineSessionPtr SessionInterface; // 添加头文件 "Interfaces/OnlineSessionInterface.h" 后使用,更具可读性 // TSharedPtr<class IOnlineSession, ESPMode::ThreadSafe> SessionInterface; // 使用 TSharedPtr 智能指针包装器进行声明 TSharedPtr<FOnlineSessionSettings> LastSessionSettings; // 上次创建的会话的设置 /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ TSharedPtr<FOnlineSessionSearch> LastSessionSearch; // 上次搜索的会话的设置 /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ ... };
-
在 "
MultiplayerSessionsSubsystem.cpp
" 中实现搜索会话接口函数 "FindSessions()
",然后实现搜索会话成功的回调函数 "OnFindSessionsComplete()
"。cpp... void UMultiplayerSessionsSubsystem::FindSessions(int32 MaxSearchResults) { /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ // 检查会话接口是否有效 if (!SessionInterface.IsValid()) { return; } // 保存委托句柄,以便此后移出委托列表 FindSessionsCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate); // 添加委托到会话接口的委托列表 // https://docs.unrealengine.com/5.0/en-US/API/Plugins/OnlineSubsystem/FOnlineSessionSearch/ LastSessionSearch = MakeShareable(new FOnlineSessionSearch()); // 利用函数 MakeShareable 初始化会话查找智能指针。 // 会话搜索设置 LastSessionSearch->MaxSearchResults = 10000; // 会话搜索设置:最大搜索结果数,尽量设置成较高的数字 LastSessionSearch->bIsLanQuery = IOnlineSubsystem::Get()->GetSubsystemName() == "NULL" ? true : false; // 会话设置:如果找到的子系统名称为 "NULL",则使用 LAN 连接,否则不使用 LastSessionSearch->QuerySettings.Set(SEARCH_PRESENCE, true, EOnlineComparisonOp::Equals); // 会话搜索设置:查询设置,确保任何查找到的会话都使用了 Presence const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); //获取本地玩家指针 // SessionInterface->FindSessions():https://docs.unrealengine.com/5.0/en-US/API/Plugins/OnlineSubsystem/Interfaces/IOnlineSession/FindSessions/2/ if (!SessionInterface->FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef())) { // 如果搜索会话失败,将委托移出委托列表 SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle); // 广播搜索会话结果空数组和失败消息到自定义的子系统委托 SubsystemOnFindSessionsCompleteDelegate.Broadcast(TArray<FOnlineSessionSearchResult>(), false); } /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ } ... void UMultiplayerSessionsSubsystem::OnFindSessionsComplete(bool bWasSuccessful) { /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ // 如果搜索会话成功,将委托移出委托列表 if (SessionInterface) { SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle); } // 如果搜索结果为空,广播搜索会话结果的空数组和失败消息到自定义的子系统委托 if (LastSessionSearch->SearchResults.Num() <= 0) { SubsystemOnFindSessionsCompleteDelegate.Broadcast(TArray<FOnlineSessionSearchResult>(), false); return; } // 广播搜索会话结果的非空数组和成功消息到自定义的子系统委托 SubsystemOnFindSessionsCompleteDelegate.Broadcast(LastSessionSearch->SearchResults, bWasSuccessful); /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ }
-
在 "
Menu.cpp
" 的回调函数 "JoinButtonClicked()
" 中使用 "MultiplayerSessionsSubsystem->FindSessions()
" 函数,设置入参最大搜索数为10000。然后实现 "OnFindSessions()
" 函数,遍历搜索结果并加入第一个匹配类型相同的会话。cpp... void UMenu::JoinButtonClicked() // 回调函数:响应鼠标单击 HostButton 事件 { //if (GEngine) { // GEngine->AddOnScreenDebugMessage( // 添加调试信息到屏幕上 // -1, // 使用 -1 不会覆盖前面的调试信息 // 15.f, // 调试信息的显示时间 // FColor::Yellow, // 字体颜色:黄色 // FString::Printf(TEXT("Join Button Clicked!")) // 打印点击事件消息 // ); //} /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ if (MultiplayerSessionsSubsystem) { MultiplayerSessionsSubsystem->FindSessions(10000); } /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ } ... void UMenu::OnFindSessions(const TArray<FOnlineSessionSearchResult>& SearchResults, bool bWasSuccessful) { /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ if (MultiplayerSessionsSubsystem == nullptr) { return; } // 遍历搜索结果并加入第一个匹配类型相同的会话(以后可以进行改进) for (auto Result : SearchResults) { FString SettingsValue; // 保存会话匹配类型 Result.Session.SessionSettings.Get(FName("MatchType"), SettingsValue); // 获取会话匹配类型 if (SettingsValue == MatchType) { // 如果匹配类型相同 MultiplayerSessionsSubsystem->JoinSession(Result); // 调用子系统的加入会话函数 return; } } /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ } ...
22.2 加入会话
-
在 "
MultiplayerSessionsSubsystem.cpp
" 中实现加入会话接口函数 "JoinSession()
",然后实现加入会话成功的回调函数 "OnJoinSessionComplete()
"。cpp... void UMultiplayerSessionsSubsystem::JoinSession(const FOnlineSessionSearchResult& SessionResult) { /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ // 检查会话接口是否有效 if (!SessionInterface.IsValid()) { // 如果会话接口无效 SubsystemOnJoinSessionCompleteDelegate.Broadcast(EOnJoinSessionCompleteResult::UnknownError); // 广播加入失败消息到自定义的子系统委托 return; } // 保存委托句柄,以便此后移出委托列表 JoinSessionCompleteDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate); // 加入会话 const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); //获取本地玩家指针 if (!SessionInterface->JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, SessionResult)) { // 如果加入会话失败,将委托移出委托列表 SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle); // 广播加入会话失败消息到自定义的子系统委托 SubsystemOnJoinSessionCompleteDelegate.Broadcast(EOnJoinSessionCompleteResult::UnknownError); } /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ } ... void UMultiplayerSessionsSubsystem::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result) { /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ // 如果加入会话成功,将委托移出委托列表 if (SessionInterface) { SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle); } // 广播加入会话的结果到自定义的子系统委托 SubsystemOnJoinSessionCompleteDelegate.Broadcast(Result); /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ }
-
在 "
Menu.h
" 添加头文件 "OnlineSubsystem.h
",这样就能在 "Menu.cpp
" 中实现 "OnJoinSession()
" 函数时,通过在线子系统访问访问在线会话接口,然后通过会话接口获取会话创建源地址,实现客户端传送至关卡 "Lobby
",完成加入会话的功能。最后,进行编译。cpp// Menu.h ... /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ #include "OnlineSubsystem.h" /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ ... // Menu.cpp void UMenu::OnJoinSession(EOnJoinSessionCompleteResult::Type Result) { /* P22 从菜单中加入会话(Join Sessions from The Menu)*/ // 加入会话,并传送至关卡 "Lobby" IOnlineSubsystem* OnlineSubsystem = IOnlineSubsystem::Get(); // 获取当前的在线子系统指针 if (OnlineSubsystem) { // 如果当前在线子系统有效 IOnlineSessionPtr SessionInterface = OnlineSubsystem->GetSessionInterface(); // 获取会话接口智能指针 if (SessionInterface.IsValid()) { // 如果获取会话接口成功 FString Address; // 保存会话创建源地址 SessionInterface->GetResolvedConnectString(NAME_GameSession, Address); // 获取会话创建源地址 APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController(); // 获取玩家控制器 if (PlayerController) { PlayerController->ClientTravel(Address, ETravelType::TRAVEL_Absolute); // 客户端传送至关卡 "Lobby" } } } }
22.3 进行测试
-
将项目打包之后发送到另一台设备上。在设备 1 上运行游戏(保证 Steam 已经运行),点击 "
Host
" 按钮,当前关卡跳转至 "Lobby
",且左上角显示会话创建成功消息。
-
在设备 2 上运行游戏(保证 Steam 已经运行且登录的账户与设备1 上登录的账号不同),点击 "
Join
" 按钮,当前关卡跳转至 "Lobby
",并且可以看到有两个玩家存在,说明设备 2 成功找到并加入到了设备 1 创建的会话中。
22.4 Summary
本节课在 "MultiplayerSessionsSubsystem.cpp
" 中实现搜索和加入会话接口函数 "FindSessions()
" 和 "JoinSession()
",接着实现搜索和加入会话成功的回调函数 "OnFindSessionsComplete()
" 和 "OnJoinSessionComplete()
";此后在 "Menu.cpp
" 实现 "OnFindSessions()
" 和 "OnJoinSession()
" 函数,遍历搜索结果并加入第一个匹配类型相同的会话,通过会话接口获取会话创建源地址,实现客户端传送至关卡 "Lobby
",由此我们完成了多人会话插件的核心功能------搜索和加入会话的功能。最后,我们在两台设备上对这两个功能进行测试。