本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P20 我们子系统的回调函数(Callbacks to Our Subsystem)》 的学习笔记,该系列教学视频为 Udemy 课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
文章目录
- P20 我们子系统的回调函数
- 20.1 子系统的自定义委托
- 20.2 创建自定义委托及与其绑定的回调函数
- 20.3 Summary
P20 我们子系统的回调函数
本节课将声明新的创建会话委托,并实现与创建会话委托绑定的回调函数。
20.1 子系统的自定义委托
- 我们为我们的子系统定义了会话接口委托,当子系统调用会话接口函数时,委托将会被添加到会话接口的委托列表中。当菜单类调用子系统的函数时,子系统的函数将会去调用会话接口函数,会话接口函数将遍历它的委托列表,找到对应的委托,并调用子系统中与其绑定的回调函数。为了传递信息返回给菜单类,其中一种方法时在我们的子系统类上创建一组自定义(Custom)的委托,一旦子系统回调函数响应了会话接口委托,就会触发子系统委托调用与其绑定的、在菜单类中定义的回调函数,从而实现信息的传递。
- 上述这种方法以一种统一且具有通用性(Versatile)的方式保持了模块间的依赖关系:菜单类依赖于子系统,子系统不需要知道任何关于菜单类的信息;子系统依赖于在线接口,在线接口不需要知道关于子系统的信息。想要更改菜单类的代码并不需要更改子系统的代码,我们要做的只是创建一组新的回调函数来相应在子系统中已经创建好的自定义委托。
20.2 创建自定义委托及与其绑定的回调函数
-
在 “
MultiplayerSessionsSubsystem.h
” 中使用创建带一个参数动态的多播委托的宏来声明将与菜单类上的回调函数绑定自定义的子系统委托类型,然后再使用这个类型定义一个委托变量。
单播委托只能绑定一个回调函数,新绑定的会覆盖旧的;而多播委托可以绑定到多个函数并一次性同时执行它们的委托,“DYNAMIC
” 意味着委托可以序列化并在蓝图中保存和加载它们,在蓝图中这也被称为事件调度器(Event dispatchers)。... /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ // 使用创建动态多播委托的宏来声明将与菜单类上的回调函数绑定的自定义委托 DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSubsystemOnCreateSessionCompleteDelegate, bool, bWasSuccessful); /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ UCLASS() class MULTIPLAYERSESSIONS_API UMultiplayerSessionsSubsystem : public UGameInstanceSubsystem { GENERATED_BODY() ... public: ... /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ // 将与菜单类上的回调函数绑定的自定义委托 FSubsystemOnCreateSessionCompleteDelegate SubsystemOnCreateSessionCompleteDelegate; /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ ... };
多播委托:可以绑定到多个函数并一次性同时执行它们的委托。
多播委托拥有大部分与单播委托相同的功能。它们只拥有对对象的弱引用(Weak references),可以与结构体一起使用,可以四处轻松复制等等。
就像常规委托一样,多播委托可以远程加载/保存和远程触发(Triggered remotely);但多播委托函数不能使用返回值。它们最适合用来 四处轻松传递一组委托(Pass a collection of delegates around)。
事件是一种特殊类型的多播委托,它在访问Broadcast()
、IsBound()
和Clear()
函数时会受到限制。
声明多播委托
多播委托在声明方式上与声明标准委托相同,只是前者使用特定于多播委托的宏变体。声明宏 说明 DECLARE_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )
创建一个多播委托。 DECLARE_DYNAMIC_MULTICAST_DELEGATE[_RetVal, ...]( DelegateName )
创建一个动态多播委托。
—— 虚幻引擎官网文档《多播委托》
-
在 “
Menu.h
” 中创建回调函数 “OnCreateSession()
” 并在 “Menu.cpp
” 中实现绑定。如果回调函数绑定失败说明虚幻引擎的反射机制没有正确找到函数,需要在声明函数时使用 UFUNCTION() 宏。// Menu.h ... UCLASS() class MULTIPLAYERSESSIONS_API UMenu : public UUserWidget { GENERATED_BODY() ... protected: ... /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ // UFUNCTION() // 虚幻引擎的反射机制可以仅根据函数名来正确绑定回调函数,如果绑定失败说明没有找到函数,需要使用 UFUNCTION() 宏。 void OnCreateSession(bool bWasSuccessful); /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ ... }; // Menu.cpp ... void UMenu::MenuSetup(int32 NumberOfPublicConnections, FString TypeOfMatch) { ... UGameInstance* GameInstance = GetGameInstance(); // 获取游戏实例 if (GameInstance) { MultiplayerSessionsSubsystem = GameInstance->GetSubsystem<UMultiplayerSessionsSubsystem>(); // 访问子系统 } /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ // 绑定菜单类回调函数到子系统委托上 if (MultiplayerSessionsSubsystem) { MultiplayerSessionsSubsystem->SubsystemOnCreateSessionCompleteDelegate.AddDynamic(this, &ThisClass::OnCreateSession); } /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ } ...
-
在 “
MultiplayerSessionsSubsystem.cpp
” 中分别在 “CreateSession()
” 和 “OnCreateSessionComplete()
” 中实现广播会话创建成功或失败的消息到自定义的子系统委托上。... void UMultiplayerSessionsSubsystem::CreateSession(int32 NumpublicConnections, FString MatchType) { ... if (!SessionInterface->CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), NAME_GameSession, *LastSessionSettings)) { // 如果会话创建失败,将委托移出委托列表 SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle); /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ // 广播会话创建失败消息到自定义的子系统委托 SubsystemOnCreateSessionCompleteDelegate.Broadcast(false); /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ } } ... /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ void UMultiplayerSessionsSubsystem::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful) { // 会话创建成功,将委托移出委托列表 if (SessionInterface) { SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle); } // 广播会话创建成功消息到自定义的子系统委托 SubsystemOnCreateSessionCompleteDelegate.Broadcast(bWasSuccessful); } /* P20 我们子系统的回调函数(Callbacks to Our Subsystem)*/ ...
-
在 “
Menu.cpp
” 的 “OnCreateSession()
” 函数中实现打印会话成功消息功能,将把 “HostButtonClicked()
” 中打印点击事件消息的代码注释掉(选中代码块后使用快捷键 “Ctrl + Shift + /
”),并将传送至关卡 “Lobby
” 的代码块复制到 “OnCreateSession()
” 函数中,这样就可以确保只有在会话创建成功后才传送。... void UMenu::OnCreateSession(bool bWasSuccessful) { if (bWasSuccessful) { if (GEngine) { GEngine->AddOnScreenDebugMessage( // 添加调试信息到屏幕上 -1, // 使用 -1 不会覆盖前面的调试信息 15.f, // 调试信息的显示时间 FColor::Yellow, // 字体颜色:黄色 FString::Printf(TEXT("Session created Successfully!")) // 打印会话创建成功消息 ); } // 会话创建成功后传送至关卡 Lobby UWorld* World = GetWorld(); if (World) { // Uworld->ServerTravel:https://docs.unrealengine.com/5.0/en-US/API/Runtime/Engine/Engine/UWorld/ServerTravel/ World->ServerTravel(FString("/Game/ThirdPerson/Maps/Lobby?listen")); // 作为监听服务器打开 Lobby 关卡 } } else { if (GEngine) { GEngine->AddOnScreenDebugMessage( // 添加调试信息到屏幕上 -1, // 使用 -1 不会覆盖前面的调试信息 15.f, // 调试信息的显示时间 FColor::Yellow, // 字体颜色:黄色 FString::Printf(TEXT("Failed to create session!")) // 打印会话创建成功消息 ); } } } void UMenu::HostButtonClicked() // 回调函数:响应鼠标单击 HostButton 事件 { //if (GEngine) { // GEngine->AddOnScreenDebugMessage( // 添加调试信息到屏幕上 // -1, // 使用 -1 不会覆盖前面的调试信息 // 15.f, // 调试信息的显示时间 // FColor::Yellow, // 字体颜色:黄色 // FString::Printf(TEXT("Host Button Clicked!")) // 打印点击事件消息 // ); //} if (MultiplayerSessionsSubsystem) { MultiplayerSessionsSubsystem->CreateSession(NumPublicConnections, MatchType); // 创建游戏会话 会话创建后传送至关卡 Lobby //UWorld* World = GetWorld(); //if (World) { // // Uworld->ServerTravel:https://docs.unrealengine.com/5.0/en-US/API/Runtime/Engine/Engine/UWorld/ServerTravel/ // World->ServerTravel(FString("/Game/ThirdPerson/Maps/Lobby?listen")); // 作为监听服务器打开 Lobby 关卡 //} } } ...
-
编译后进行测试,屏幕上左上角消息显示会话创建成功。
20.3 Summary
本节课介绍一种在线会话接口和子系统传递信息返回给菜单类的方法:在我们的子系统类上创建一组自定义(Custom)的委托,一旦子系统回调函数响应了会话接口委托,就会触发子系统委托调用与其绑定的、在菜单类中定义的回调函数,从而实现信息的传递。于是,我们在 “MultiplayerSessionsSubsystem.h
” 中使用创建动态多播委托的宏来声明将自定义子系统委托,在 “Menu.h
” 中创建菜单类回调函数 “OnCreateSession()
” 并在 “Menu.cpp
” 中实现与自定义子系统委托的绑定,然后在 “MultiplayerSessionsSubsystem.cpp
” 中分别在 “CreateSession()
” 和 “OnCreateSessionComplete()
” 中实现广播会话创建成功或失败的消息到自定义子系统委托上。
在 20.2 创建自定义委托及与其绑定的回调函数 的 步骤 2 如果回调函数绑定失败说明虚幻引擎的反射机制没有正确找到函数,需要在声明函数时使用 UFUNCTION() 宏。
在 步骤 4 中学到了一个 VS 的使用技巧,选中代码块后使用快捷键 “Ctrl + Shift + /
” 可对代码块进行快速注释,也可以使用这个快捷键取消代码行的注释。