《UE5_C++多人TPS完整教程》学习笔记21 ——《P22 从菜单中加入会话(Join Sessions from The Menu)》


本文为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 搜索会话

  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)*/
    
    	...
    	
    };
  2. 在 "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)*/
    }
  3. 在 "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 加入会话

  1. 在 "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)*/
    }
  2. 在 "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. 将项目打包之后发送到另一台设备上。在设备 1 上运行游戏(保证 Steam 已经运行),点击 "Host" 按钮,当前关卡跳转至 "Lobby",且左上角显示会话创建成功消息。

  2. 在设备 2 上运行游戏(保证 Steam 已经运行且登录的账户与设备1 上登录的账号不同),点击 "Join" 按钮,当前关卡跳转至 "Lobby",并且可以看到有两个玩家存在,说明设备 2 成功找到并加入到了设备 1 创建的会话中。



22.4 Summary

本节课在 "MultiplayerSessionsSubsystem.cpp" 中实现搜索和加入会话接口函数 "FindSessions()" 和 "JoinSession()",接着实现搜索和加入会话成功的回调函数 "OnFindSessionsComplete()" 和 "OnJoinSessionComplete()";此后在 "Menu.cpp" 实现 "OnFindSessions()" 和 "OnJoinSession()" 函数,遍历搜索结果并加入第一个匹配类型相同的会话,通过会话接口获取会话创建源地址,实现客户端传送至关卡 "Lobby",由此我们完成了多人会话插件的核心功能------搜索和加入会话的功能。最后,我们在两台设备上对这两个功能进行测试。


相关推荐
木向1 小时前
leetcode42:接雨水
开发语言·c++·算法·leetcode
sukalot1 小时前
windows C++-创建基于代理的应用程序(下)
c++
labuladuo5201 小时前
AtCoder Beginner Contest 372 F题(dp)
c++·算法·动态规划
DieSnowK1 小时前
[C++][第三方库][httplib]详细讲解
服务器·开发语言·c++·http·第三方库·新手向·httplib
StrokeAce3 小时前
linux桌面软件(wps)内嵌到主窗口后的关闭问题
linux·c++·qt·wps·窗口内嵌
家有狸花6 小时前
VSCODE驯服日记(三):配置C++环境
c++·ide·vscode
dengqingrui1237 小时前
【树形DP】AT_dp_p Independent Set 题解
c++·学习·算法·深度优先·图论·dp
幻狐boke7 小时前
【游戏模组】星际争霸1代模组燃烧之地,泰伦帝国对决UED。特效华丽兵种巨多特别好玩
游戏
C++忠实粉丝7 小时前
前缀和(8)_矩阵区域和
数据结构·c++·线性代数·算法·矩阵