《UE5_C++多人TPS完整教程》学习笔记19 ——《P20 我们子系统的回调函数(Callbacks to Our Subsystem)》


本文为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 子系统的自定义委托

  1. 我们为我们的子系统定义了会话接口委托,当子系统调用会话接口函数时,委托将会被添加到会话接口的委托列表中。当菜单类调用子系统的函数时,子系统的函数将会去调用会话接口函数,会话接口函数将遍历它的委托列表,找到对应的委托,并调用子系统中与其绑定的回调函数。为了传递信息返回给菜单类,其中一种方法时在我们的子系统类上创建一组自定义(Custom)的委托,一旦子系统回调函数响应了会话接口委托,就会触发子系统委托调用与其绑定的、在菜单类中定义的回调函数,从而实现信息的传递。
  2. 上述这种方法以一种统一且具有通用性(Versatile)的方式保持了模块间的依赖关系:菜单类依赖于子系统,子系统不需要知道任何关于菜单类的信息;子系统依赖于在线接口,在线接口不需要知道关于子系统的信息。想要更改菜单类的代码并不需要更改子系统的代码,我们要做的只是创建一组新的回调函数来相应在子系统中已经创建好的自定义委托。
    在这里插入图片描述

20.2 创建自定义委托及与其绑定的回调函数

  1. 在 “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 )创建一个动态多播委托。

    —— 虚幻引擎官网文档《多播委托》

  2. 在 “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)*/
    }
    
    ...
    
  3. 在 “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)*/
    
    ...
    
  4. 在 “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 关卡
    		//}
    	}
    }
    		
    ...
    
  5. 编译后进行测试,屏幕上左上角消息显示会话创建成功。
    在这里插入图片描述


20.3 Summary

本节课介绍一种在线会话接口和子系统传递信息返回给菜单类的方法:在我们的子系统类上创建一组自定义(Custom)的委托,一旦子系统回调函数响应了会话接口委托,就会触发子系统委托调用与其绑定的、在菜单类中定义的回调函数,从而实现信息的传递。于是,我们在 “MultiplayerSessionsSubsystem.h” 中使用创建动态多播委托的宏来声明将自定义子系统委托,在 “Menu.h” 中创建菜单类回调函数 “OnCreateSession()” 并在 “Menu.cpp” 中实现与自定义子系统委托的绑定,然后在 “MultiplayerSessionsSubsystem.cpp” 中分别在 “CreateSession()” 和 “OnCreateSessionComplete()” 中实现广播会话创建成功或失败的消息到自定义子系统委托上。
在这里插入图片描述

20.2 创建自定义委托及与其绑定的回调函数步骤 2 如果回调函数绑定失败说明虚幻引擎的反射机制没有正确找到函数,需要在声明函数时使用 UFUNCTION() 宏。
步骤 4 中学到了一个 VS 的使用技巧,选中代码块后使用快捷键 “Ctrl + Shift + /” 可对代码块进行快速注释,也可以使用这个快捷键取消代码行的注释。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/394642.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【XR806开发板试用】+移植rosserial到XR806

1 XR806简介 板子来源于极术社区的试用&#xff0c;XR806的在线网址 其主要参数&#xff1a; 主控XR806AF2LDDRSIP 288KB SRAM存储SIP 160KB Code ROM. SIP 16Mbit Flash.天线板载WiFi/BT双天线&#xff0c;可共存按键reboot按键 1&#xff0c;功能按键 1灯红色电源指示灯 1…

如何在wxPython应用程序中使用Panda3D

我们知道wxPython提供了丰富的工具和部件来构建用户界面&#xff0c;如果当我们整合wxPython和Panda3D可以创建出功能丰富且交互性强的应用程序&#xff0c;可以创建出强大而丰富的用户界面和3D场景。这样做的主要挑战在于将两个库整合到一个应用程序中&#xff0c;同时确保它们…

【.NET Core】深入理解async 和 await 理解

【.NET Core】深入理解async 和 await 理解 文章目录 【.NET Core】深入理解async 和 await 理解一、概述二、async异步执行机制理解三、async与await应用3.1 async与await简单应用3.2 带有返回值async与await应用 四、async和await中常见问题总结4.1 当方法用async标识时&…

(每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第10章 项目进度管理(六)

博主2023年11月通过了信息系统项目管理的考试&#xff0c;考试过程中发现考试的内容全部是教材中的内容&#xff0c;非常符合我学习的思路&#xff0c;因此博主想通过该平台把自己学习过程中的经验和教材博主认为重要的知识点分享给大家&#xff0c;希望更多的人能够通过考试&a…

跨境云手机如何简化tiktok运营流程

如今&#xff0c;tiktok已经成为世界范围内都非常流行的社交媒体平台。然而在大多数情况下&#xff0c;由于网络原因&#xff0c;tiktok无法在国内使用&#xff0c;但依然有越来越多的人注册tiktok号码、建立tiktok矩阵。原因是tiktok仍然有大量的流量可供商业使用&#xff0c;…

day04-股票K线功能实现

股票K线功能实现 今日目标 1.理解股票T和T-1概念&#xff0c;实现成交量对比功能; 2.理解个股涨跌幅度统计功能; 2.1 分析业务&#xff0c;SQL落地; 2.2 完善不存在数据的区间默认回显功能; 3.理解个股分时线业务&#xff0c;并实现功能; 4.理解个股日K线业务&#xff0c;并实…

使用傅里叶实现100倍的压缩效果(附Python源码)

傅里叶变换&#xff08;Fourier Transform&#xff09;是一种将一个函数&#xff08;在时间或空间域&#xff09;转换为另一个函数&#xff08;在频率域&#xff09;的数学变换方法。它在信号处理、图像处理、通信等领域有广泛应用。 实现过程 将傅里叶系数核心的1%保留&…

从零开始手写mmo游戏从框架到爆炸(十五)— 命令行客户端改造

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 到现在&#xff0c;我们切实需要一个客户端来完整的进行英雄选择&#xff0c;选择地图&#xff0c;打怪等等功能。所以我们需要把之前极为简陋的客户端改造一下。 首先…

0成本部署github前端项目流程

0成本部署github纯前端项目流程 对业内来说应该是一个比较常规的操作&#xff0c;对于新手来说进行过一次应该就很难忘记了&#xff0c;但很多人仍然是不会的&#xff0c;认为部署项目很难&#xff0c;很专业&#xff0c;其实现在由于这些厂商的努力&#xff0c;大众&#xff…

js设计模式:装饰者模式

作用: 可以给原有对象的身上添加新的属性方法 可以让对象或者组件进行扩展 示例: class Person{constructor(name,selfSkill){this.name namethis.selfSkill selfSkill}run 会走路}//所有人类都有的共同特性和技能let wjt new Person(王惊涛,写代码)let mashi new Pers…

Python实现KDJ指标计算:股票技术分析的利器系列(3)

Python实现KDJ指标计算&#xff1a;股票技术分析的利器系列&#xff08;3&#xff09; 介绍算法解释 代码rolling函数介绍计算LLV&#xff08;最低价最小值&#xff09;和HHV&#xff08;最高价最大值&#xff09;计算RSV计算SMA&#xff08;简单移动平均&#xff09; 完整代码…

micro-app以UMD js链接方式引入使用

npm 下载好micro-zoe/micro-app后&#xff0c;找到index.umd.js&#xff1a; 新建一个测试html&#xff0c;引入并使用&#xff1a; 参考&#xff1a; 微组件实践 - 掘金

八、计算机视觉-边界填充

文章目录 前言一、原理二、具体的实现 前言 在Python中使用OpenCV进行边界填充&#xff08;也称为zero padding&#xff09;是一种常见的图像处理操作&#xff0c;通常用于在图像周围添加额外的像素以便进行卷积或其他操作。下面是使用OpenCV进行边界填充的基本原理和方法 一…

答题抽奖活动怎么做_一场智慧与幸运的碰撞

答题抽奖&#xff0c;知识变现&#xff0c;一场智慧与幸运的碰撞&#xff01; 在这个信息爆炸的时代&#xff0c;如何吸引人们的注意力&#xff0c;成为每个营销者都需要面对的挑战。而答题抽奖活动&#xff0c;以其独特的魅力&#xff0c;正成为越来越多品牌吸引用户、提升用…

智能无人仓|加快步伐 河北沃克HEGERLS将突破与创新“常态化”

物流的发展涉及工业、商业各个领域&#xff0c;涵盖原材料&#xff0c;生产成品从起点到终点的全过程&#xff0c;在室内物流操作上涵盖了收、发、存、拣等作业。近年来&#xff0c;由于人工成本的提高&#xff0c;基础劳动力取得的难度不断地加大&#xff0c;自动化和智能化逐…

pytest 框架自动化测试

随笔记录 目录 1. 安装 2. 安装pytest 相关插件 2.1 准备阶段 2.2 安装 2.3 验证安装成功 3. pytest测试用例的运行方式 3.1 主函数模式 3.1.1 主函数执行指定文件 3.1.2 主函数执行指定模块 3.1.3 主函数执行某个文件中的某个类、方法、函数 3.1.4 主函数执行生…

『运维备忘录』之 SSH 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等知识&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

C++Qt:noteBookPro_01

一、创建项目 选择Qt Widgets 常用的是QWidgets和MainWindow。两者的区别&#xff1a; QWidgets用于简单的窗口&#xff0c;没有内置的菜单栏、工具栏和状态栏。适用于简单专用的应用程序&#xff0c;不需要复杂的界面组件。 MainWindow是包含完整的菜单栏、工具栏和状态栏的主…

入门级10寸加固行业平板—EM-I10J

亿道信息以其坚固耐用的智能终端设备而闻名&#xff0c;近日发布了一款理想入门级 10 英寸加固平板电脑—I10J。 EM-I10J​​ 这是一款 10 英寸的平板电脑&#xff0c;主要运行 Windows 10操作系统&#xff0c;带有硬化塑料外壳&#xff0c;具有 IP65 防水防尘功能和 MIL-STD 8…

unity学习(19)——客户端与服务器合力完成注册功能(1)入门准备

逆向服务器用了三天的时间&#xff0c;但此时觉得一切都值&#xff0c;又可以继续学习了。 服务器中登录请求和注册请求由command变量进行区分&#xff0c;上一层的type变量都是login。 public void process(Session session, SocketModel model) {switch (model.Command){ca…