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

news2024/11/28 18:41:47

本文为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 + /” 可对代码块进行快速注释,也可以使用这个快捷键取消代码行的注释。


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

外汇天眼:小白开始实盘之前,必须知道的7件事

在进行外汇交易时&#xff0c;保持松弛的心态和学习外汇知识是一件很重要的事情&#xff0c;但对于缺乏交易经验的交易小白来说&#xff0c;想保持松弛的心态和学习外汇知识比较困难&#xff0c;考虑到这一点&#xff0c;天眼给大家总结了7件在交易前必须知道的事情。 1、实现财…

基于PSO优化的GRU多输入分类(Matlab)粒子群优化门控循环单元神经网络分类预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分程序&#xff1a; 四、完整代码数据分享下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台…

167基于matlab的根据《液体动静压轴承》编写的有回油槽径向静压轴承的程序

基于matlab的根据《液体动静压轴承》编写的有回油槽径向静压轴承的程序&#xff0c;可显示承载能力、压强、刚度及温升等图谱.程序已调通&#xff0c;可直接运行。 167 显示承载能力、压强、刚度及温升 (xiaohongshu.com)https://www.xiaohongshu.com/explore/65d212b200000000…

element 表单提交图片(表单上传图片)

文章目录 使用场景页面效果前端代码 使用场景 vue2 element 表单提交图片   1.点击【上传图片】按钮择本地图片&#xff08;只能选择一张图片&#xff09;后。   2.点击图片&#xff0c;支持放大查看。   3.点击【保存】按钮&#xff0c;提交表单。 页面效果 前端代码…

新版本HC物联网系统

代码开源在 HC物联网平台: HC物联网系统用于小区&#xff0c;园区等设备管理&#xff0c;包括门禁&#xff0c;道闸&#xff0c;监控&#xff0c;充电桩&#xff0c;智能水电表&#xff0c;梯控和门锁等设备 HC 物联网系统 HC物联网系统专注于小区和园区内的设备管理&#xf…

辽宁博学优晨教育:视频剪辑培训新篇章,开启你的创意之旅

在数字化时代&#xff0c;视频剪辑已成为一项炙手可热的技能。辽宁博学优晨教育深知市场需求&#xff0c;特别推出视频剪辑培训专业&#xff0c;为广大爱好者和专业人士提供了一个学习和交流的平台。在这里&#xff0c;我们不仅传授技术&#xff0c;更致力于激发你的创意潜能&a…

Unity ScreenPointToRay 获取到的坐标不准确

&#x1f47e;奇奇怪怪的 &#x1f959;问题描述&#x1f96a;解决方案&#x1f37f;验证代码 &#x1f959;问题描述 使用&#xff1a;Camera.main.ScreenPointToRay 将鼠标坐标转换成射线&#xff0c;然后通过&#xff1a;Physics.Raycast 获取到射线碰撞到的坐标&#xff0…

单反sd卡照片突然没有了?原因+解决方案

在使用单反相机拍摄时&#xff0c;SD卡作为存储媒介&#xff0c;承担着存储照片的重要角色。然而&#xff0c;有时候我们会遇到SD卡中的照片突然消失的情况&#xff0c;给拍摄工作带来很大的困扰。本文将深入剖析导致这一问题的原因&#xff0c;并提供相应的解决方案&#xff0…

SMART信息——你的固态硬盘实时体检报告

SMART&#xff0c;或者严谨地说&#xff1a;S.M.A.R.T (Self-Monitoring Analysis and Reporting Technology)&#xff0c;即“自我监测分析与报告技术”&#xff0c;在硬盘行业耳熟能详。无论是传统的机械硬盘&#xff0c;还是作为后起之秀的固态硬盘&#xff0c;它们都在使用…

C++面试宝典第28题:寻找丢失的数字

题目 给定一个包含n个整数的数组nums,其中nums[i]在区间[1, n]内。请找出所有在[1, n]范围内,但没有出现在nums中的数字,并以数组的形式返回结果。 示例1: 输入:nums = [4, 3, 2, 7, 8, 2, 3, 1] 输出:[5, 6] 示例2: 输入:nums = [1, 1] 输出:[2] 解析 初看这道题,…

【WEB环境】-LNTMP或LAMP结构搭建(记录)

一、手工安装搭建 1.1 LNTMP 【nginx:rpm安装】 安装根目录路径&#xff1a;/usr/local/nginx 启动&#xff1a;/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf 停止kill -QUIT 2072 ./nginx -s stop /usr/local/nginx/sbin/nginx -s stop 重启…

FPGA之移位寄存器

SLICEM中的LUT可以配置为32位移位寄存器,而无需使用slice中可用的触发器。以这种方式使用,每个LUT 可以将串 行数据延迟 1 到 32 个时钟周期。移入D &#xff08;DI1 LUT 引脚&#xff09;和移出 Q31&#xff08;MC31 LUT 引脚&#xff09;线路将LUT级联&#xff0c;以形成更大…

物联网水域信息化:水质监测智慧云平台

行业背景 由于传统水务企业水质监测和管理技术不足&#xff0c;以及水源污染等问题&#xff0c;确保供水水质达标困难重重。 且传统水务行业信息化手段单一&#xff0c;缺乏对大数据等新技术的应用&#xff0c;一定程度上影响了水务工作的精细化和智能化程度。 系统特点 为…

Positive SSL 证书介绍

Positive SSL 是一种受欢迎的 SSL 证书&#xff0c;提供了卓越的安全性、性价比和品牌信任。以下是对 Positive SSL 在这些方面的简要介绍&#xff1a; 1. 安全性&#xff1a; Positive SSL 证书采用强大的加密技术&#xff0c;确保网站和用户之间的数据传输是安全的。它使用…

新版Java面试专题视频教程——准备篇、Redis篇

新版Java面试专题视频教程——准备篇、Redis篇 准备篇 准备篇-01-企业简历筛选规则准备篇-02-简历注意事项准备篇-03-应届生该如何找到合适的练手项目 Redis篇 01-redis开篇02-缓存穿透03-缓存击穿04-缓存雪崩05-双写一致性…

Python算法100例-1.8 冒泡排序

完整源代码项目地址&#xff0c;关注博主私信’源代码’后可获取 1.问题描述2.问题分析3.算法设计4.完整的程序5.问题拓展 1&#xff0e;问题描述 对N个整数&#xff08;数据由键盘输入&#xff09;进行升序排列。 2&#xff0e;问题分析 对于N个类型相同的数&#xff0c;…

波特率和(FSK)调制速率的关系

最近在学习基于STM32的FSK解调&#xff0c;刚开始一直对波特率和FSK调制速率两者的概念有些混淆&#xff0c;于是出一篇帖子进行总结。 在百度百科中查到&#xff1a; 调制速率定义 在电子通信领域&#xff0c;调制速率&#xff0c;指的是信号被调制以后在单位时间内的变化&am…

软考学习--计算机组成原理与体系结构

计算机组成原理与体系结构 数据的表示 进制转换 R 进制转换为 10 进制–按权展开法 10进制转换为2进制 原码 反码 补码 移码 原码 &#xff1a;数字的二进制表示反码 &#xff1a; 正数的反码等于原码&#xff0c;负数的反码等于原码取反补码&#xff1a; 正数的补码等…

大模型之二十二 OpenAI sora

2024年2月15日在中国新年还没过完的时候&#xff0c;OpenAI 发布的Sora&#xff0c;这是AI视频生成领域的‘Midjourney时刻’&#xff0c;Sora将Diffusion模型和Transformer模型相结合&#xff0c;在视觉领域实现了大语言模型类似的突破&#xff0c;这将类似于ChatGPT一样&…

【Python笔记-设计模式】工厂模式

一、说明 (一) 解决问题 提供了一种方式&#xff0c;在不指定具体类将要创建的情况下&#xff0c;将类的实例化操作延迟到子类中完成。可以实现客户端代码与具体类实现之间的解耦&#xff0c;使得系统更加灵活、可扩展和可维护。 (二) 使用场景 希望复用现有对象来节省系统…