本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P10 创建会话(Creating A Session)》 的学习笔记,该系列教学视频为 Udemy 课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
本节课将学习委托(Delegates)的基本概念、工作原理以及它对于管理多人游戏的关键(Crucial)作用,然后我们将在上节课《P9 访问 Steam(Acessing Steam)》 代码的基础上,尝试创建一个游戏会话,并通过在屏幕上打印文本来验证游戏会话是否创建成功。
委托可以被认为是一个持有对函数的引用(Reference)的对象。虚幻引擎的委托可以与函数 绑定(Having functions bound to them),可以 广播到 所有与它绑定的函数接收并执行以进行 响应 的信号。我们通常会编写 回调函数(Make functions referred to as callback functions)然后将它们绑定到委托上。于是,当游戏中的某个确定事件(Certain event)发生时,委托被触发或者广播,任何绑定在该委托的 回调函数 都将进行响应。
虚幻引擎的在线会话需要利用委托,这是因为创建和加入游戏会话都需要在互联网上发送信息。如果我们调用 会话接口(Session interface)函数 “CreateSession()
” 就会发送信息到服务平台(本课程为 Steam
),此时游戏会话就能被创建,然后服务平台将(反馈)信息发送到我们的设备上,让我们知道会话创建已经完成。
会话接口(Session interface)定义了一组委托类型,拥有一个可以在适当的时间内通过事件触发进行遍历的 委托列表(Delegate list),我们将以其中一个委托类型创建一个新对象,为它绑定一个回调函数,并添加到会话接口的委托列表中。
本节课的思路就是首先利用函数 “FOnCreateSessionCompleteDelegate
” 在第三人称游戏项目 MenuSystem
的 character
类中创建委托(变量),同时也创建一个绑定到该委托的回调函数 “OnCreateSessionComplete()
”,接着访问会话接口并添加委托到委托列表中;然后调用会话接口函数 “CreateSession()
” 连接到 Steam
以创建游戏会话,游戏会话创建完成后 Steam
向会话接口发送一个信号,会话接口将遍历委托列表,从而触发我们添加到此列表的委托并导致回调函数 “OnCreateSessionComplete()
” 被调用并接收游戏会话创建完成的信息;通过打印信息到屏幕上,就可以验证会话是否真的创建成功。
关于委托的更多知识可以参阅官方文档《委托》。
添加代码到 “MenuSystemcharacter.h
” 的类 “AMenuSystemCharacter
” 中,定义委托 CreateSessionCompleteDelegate
、创建游戏会话函数 CreateGameSession()
以及委托的回调函数 OnCreateSessionComplete()
:
...
#include "Interfaces/OnlineSessionInterface.h"
...
UCLASS(config=Game)
class AMenuSystemCharacter : public ACharacter
{
...
/* P10 创建会话(Creating A Session)*/
public:
// 会话接口智能指针
// IOnlineSessionPtr OnlineSessionInterface; // 添加头文件 "Interfaces/OnlineSessionInterface.h" 后使用,更具可读性
TSharedPtr<class IOnlineSession, ESPMode::ThreadSafe> OnlineSessionInterface; // 使用 TSharedPtr 智能指针包装器进行声明
protected:
UFUNCTION(BlueprintCallable)
void CreateGameSession(); // 创建游戏会话
void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful); // 委托 CreateSessionCompleteDelegate 的回调函数
private:
// 类 FOnCreateSessionCompleteDelegate 在 UE 5.0 和 5.1 版本的头文件 "Interfaces/OnlineSessionInterface.h" 中声明
// 而 5.2 和 5.3 版本的头文件 "Interfaces/OnlineSessionDelegates.h" 中声明
FOnCreateSessionCompleteDelegate CreateSessionCompleteDelegate; // 会话创建完成委托
/* P10 创建会话(Creating A Session)*/
};
在 “MenuSystemcharacter.cpp
” 构造函数 “AMenuSystemCharacter::AMenuSystemCharacter()
” 中为委托 “CreateSessionCompleteDelegate
” 绑定回调函数 “OnCreateSessionComplete()
” 并完成创建游戏会话函数 CreateGameSession()
的定义。
/* MenuSystemcharacter.h */
...
/* P10 创建会话(Creating A Session)*/
#include "OnlineSessionSettings.h"
/* P10 创建会话(Creating A Session)*/
...
/* MenuSystemcharacter.cpp */
...
/* P10 创建会话(Creating A Session)*/
AMenuSystemCharacter::AMenuSystemCharacter(): // 为委托绑定回调函数
CreateSessionCompleteDelegate(FOnCreateSessionCompleteDelegate::CreateUObject(this, &ThisClass::OnCreateSessionComplete))
/* P10 创建会话(Creating A Session)*/
{
...
}
/* P10 创建会话(Creating A Session)*/
void AMenuSystemCharacter::CreateGameSession() // 当按下数字键 1 时调用
{
// 检查会话接口是否有效
if (!OnlineSessionInterface.IsValid()) {
return;
}
// 检查是否先前存在会话
auto ExistingSession = OnlineSessionInterface->GetNamedSession(NAME_GameSession);
if (ExistingSession != nullptr) { // 如果先前存在会话
OnlineSessionInterface->DestroySession(NAME_GameSession); // 销毁会话
}
OnlineSessionInterface->AddOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegate); // 添加委托到会话接口的委托列表
TSharedPtr<FOnlineSessionSettings> SessionSettings = MakeShareable(new FOnlineSessionSettings()); // 创建会话设置,利用函数 MakeShareable 初始化
// FOnlineSessionSettings 在头文件 "OnlineSessionSettings.h" 中
// 会话设置成员变量参阅及含义:https://docs.unrealengine.com/5.3/en-US/API/Plugins/OnlineSubsystem/FOnlineSessionSettings/
SessionSettings->bIsLANMatch = false; // 会话设置:不创建 LAN 连接
SessionSettings->NumPublicConnections = 4; // 会话设置:设置最大公共连接数为 4
SessionSettings->bAllowJoinInProgress = true; // 会话设置:在会话运行时允许其他玩家加入
SessionSettings->bAllowJoinViaPresence = true; // 会话设置:Steam 使用 Presence 搜索会话所在地区,确保连接正常工作
SessionSettings->bShouldAdvertise = true; // 会话设置:允许 Steam 发布会话
SessionSettings->bUsesPresence = true; // 会话设置:允许显示用户 Presence 信息
SessionSettings->bUseLobbiesIfAvailable = true; // (视频中未提及)会话设置:优先选择 Lobby API(Steam 支持 Lobby API)
const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); // 获取本地玩家指针
OnlineSessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), // 第一个参数类型为 strut FUniqueNetIdRepl,公共继承了 struct FUniqueNetIdWrapper
// 这个包装器重载了引用运算符 *,它表示 * 返回一个引用 *UniquenetId
NAME_GameSession, // 第二个参数类型为 FName SessionName,游戏会话名称
*SessionSettings); // 第三个参数类型为 const FOnlineSessionSettings &NewSessionSettings
}
/* P10 创建会话(Creating A Session)*/
...
会话设置代码
SessionSettings->bUseLobbiesIfAvailable = true
在视频中是没有提及的,如果在后面的测试出现无法创建会话,则需要添加(作者在下一集教学视频 《P11 设置加入会话(Setup for Joining Sessions)》 中会提到这点)。
继续在 “MenuSystemcharacter.cpp
” 构造函数 “AMenuSystemCharacter::AMenuSystemCharacter()
” 中完成回调函数 OnCreateSessionComplete()
的定义。
...
/* P10 创建会话(Creating A Session)*/
void AMenuSystemCharacter::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful) { // 如果游戏会话创建成功
if (GEngine) {
GEngine->AddOnScreenDebugMessage( // 添加调试信息到屏幕上
-1, // 使用 -1 不会覆盖前面的调试信息
15.f, // 调试信息的显示时间
FColor::Red, // 字体颜色
FString::Printf(TEXT("Create session: %s!"), *SessionName.ToString()) // 打印游戏会话的名称
);
}
}
else { // 如果游戏会话创建失败
if (GEngine) {
GEngine->AddOnScreenDebugMessage( // 添加调试信息到屏幕上
-1, // 使用 -1 不会覆盖前面的调试信息
15.f, // 调试信息的显示时间
FColor::Red, // 字体颜色
FString::Printf(TEXT("Failed to create session!")) // 打印失败消息
);
}
}
}
/* P10 创建会话(Creating A Session)*/
...
进行实时编译,编译成功后打开 “BP_ThirdPersonCharacter
” 蓝图编辑器,绘制如下蓝图,编译、保存。
将项目打包之后再运行游戏(保证 Steam
已经运行),按下数字键 “1”,屏幕左上角红色字体显示会话的名称 “Game Session
” ,说明创建会话成功。
本节课学习委托(Delegates)的基本概念、工作原理以及它对于管理多人游戏的关键(crucial)作用,在创建会话的 C++ 编程中,首先利用函数 “FOnCreateSessionCompleteDelegate
” 创建委托,接着创建一个绑定到该委托的回调函数 “OnCreateSessionComplete()
”,访问会话接口并添加该委托到委托列表中;然后调用会话接口函数 “CreateSession()
” 连接到 Steam
以创建游戏会话,游戏会话创建完成后 Steam
向会话接口发送一个信号,会话接口遍历委托列表,触发我们添加到此列表的委托并导致回调函数 “OnCreateSessionComplete()
” 被调用,接收游戏会话创建完成的信息,我们将其打印在屏幕上。
在 10.1 委托 中,有关委托的进一步学习可以参阅官方文档《委托》。
在 10.2 创建委托以及回调函数 的 步骤 2 中,会话设置代码 SessionSettings->bUseLobbiesIfAvailable = true
在视频中是没有提及的,如果在后面的测试出现无法创建会话,则需要添加。