虚幻学习笔记17—C++委托(单播)

news2024/12/28 20:25:22

一、前言

        相比“代理”这个名词我更喜欢叫“委托”,虚幻的委托分为三类,分别为单播、多播和动态多播。单播顾名思义就是一次只能绑定一个函数的委托,多播能一次性绑定多个,动态多播即可以在蓝图中进行动态的绑定且可以绑定多个。

        使用的虚幻版本为5.2.1,VS版本为2022。

二、实现

        在虚幻中创建一个Actor类,命名为“DelegateActor”类。

 1、定义类型

单播可以定义带参数、返回值和参数+返回值三类委托,如下代码所示,分别定义了这三类单播委托,所有的说明见注释部分

DECLARE_DELEGATE(NoParamDelegate);//没有参数没有返回值的委托,委托名称为“NoParamDelegate”
DECLARE_DELEGATE_OneParam(OneParamDelegate, FString);//一个参数没有返回值的委托,名称为“OneParamDelegate”
DECLARE_DELEGATE_TwoParams(TwoParamDelegate, FString, int32);//两个参数没有返回值的委托,名称为“TwoParamDelegate”
DECLARE_DELEGATE_RetVal(FString, OnlyRetDelegate);//仅仅是返回值的委托,名称为“OnlyRetDelegate”
DECLARE_DELEGATE_RetVal_OneParam(FString, RetOneParamDelegate, FString);//定义了带参数和返回值的委托,第一个参数为返回值,第二个为委托名称,第三个为返回值
2、声明委托类型变量

上述定义的可以将其看作是一个类型定义,要真正使用还需要在类中声明该类型的变量,如下所示

	//单播代理变量的声明
	NoParamDelegate NoParamDelegate;
	OneParamDelegate OneParamDelegate;
	TwoParamDelegate TwoParamDelegate;
	OnlyRetDelegate OnlyRetDelegate;
	RetOneParamDelegate RetOneParamDelegate;

注:C++中居然允许变量名称可以和委托名称一样,这样带来的问题是后续想再定义一个同类型的委托变量就会报错。如图2.1.1所示

图2.1.1
因此最好还是将变量名称不要定义成和类型名称一样
	NoParamDelegate NoParamDelegate1;
	NoParamDelegate NoParamDelegate2;
	OneParamDelegate OneParamDelegate1;
	TwoParamDelegate TwoParamDelegate1;
	OnlyRetDelegate OnlyRetDelegate1;
3、定义委托需要绑定的函数

        定义的函数要和对应的委托保持一致的返回值和参数。

	//单播代理绑定函数定义
	void NoParamDelegateFunc1();
	void NoParamDelegateFunc2();
	void OneParamDelegateFunc(FString strVal);
	void TwoParamDelegateFunc(FString strVal, int32 intVal);
	FString OnlyRetDelegateFunc();
	FString RetOneParamDelegateFunc(FString strVal);

其实现代码如下,注:FString作为返回值时,不能直接返回声明的FString类型变量tempStr,必须返回FString(tempStr)这样的结构。


void ADelegateActor::NoParamDelegateFunc1()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("NoParamDelegateFunc1")));
}

void ADelegateActor::NoParamDelegateFunc2()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("NoParamDelegateFunc2")));
}

void ADelegateActor::OneParamDelegateFunc(FString strVal)
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("OneParamDelegateFunc:%s"), *strVal));
}

void ADelegateActor::TwoParamDelegateFunc(FString strVal, int32 intVal)
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("NoParamDelegateFunc:%s,%d"), *strVal, intVal));
}
FString ADelegateActor::OnlyRetDelegateFunc()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("OnlyRetDelegate")));
	return FString();
}

FString ADelegateActor::RetOneParamDelegateFunc(FString strVal)
{
	FString tempStr = strVal.Append("RetOneParamDelegateFunc");
	return FString(tempStr);
}
4、 最后在构造函数或其他初始函数中进行绑定

            单播委托的绑定如下所示

	NoParamDelegate1.BindUObject(this, &ADelegateActor::NoParamDelegateFunc1);
	NoParamDelegate2.BindUObject(this, &ADelegateActor::NoParamDelegateFunc2);
	OneParamDelegate1.BindUObject(this, &ADelegateActor::OneParamDelegateFunc);
	TwoParamDelegate1.BindUObject(this, &ADelegateActor::TwoParamDelegateFunc);
	OnlyRetDelegate1.BindUObject(this, &ADelegateActor::OnlyRetDelegateFunc);
	RetOneParamDelegate1.BindUObject(this, &ADelegateActor::RetOneParamDelegateFunc);
5、执行

        通过如下代码进行执行,编译代码通过后,在编辑器中创建该类的蓝图并将其拖放到场景中运行即可看到每个函数的打印消息。

	NoParamDelegate1.ExecuteIfBound();
	NoParamDelegate2.ExecuteIfBound();
	OneParamDelegate1.ExecuteIfBound("TestStr");
	TwoParamDelegate1.ExecuteIfBound("TwoParam", 22222);
	OnlyRetDelegate1.Execute();//这个和上面的不一样
	FString tempStr = RetOneParamDelegate1.Execute("this is:");
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, tempStr);

注:带返回值的和不带返回值的执行函数是不一样的,这点官方文档也未作详细说明,敲代码的时候这个执行函数也不会提示,也是比较坑。

6、解绑与绑定新的函数

        解绑有点坑,官方文档写的是"UnBind",而实际上是“Unbind()"函数,而在使用了番茄助手的情况下又没有提示,敲完”UnBind“后一直报错

	NoParamDelegate.Unbind();

绑定新的函数,直接像之前一样添加绑定,绑定后会覆盖掉之前的,在执行的时候只会执行新的绑定函数。

	NoParamDelegate.BindUObject(this, &ADelegateActor::NoParamDelegateFunc2);

如图6.1.1所示为官方给定的其他绑定函数,不过官方没有案例说明,只有如图简单的介绍性说明,这点和Unity是没法比,所以针对每一个绑定只能自己去尝试使用一下。

图6.1.1

1)Bind

        绑定现有的委托对象,提示没有这个函数,暂时还不知道怎么使用。

2)BindStatic

        需要定义一个静态函数然后进行绑定,执行方法不变。

//定义
static void StaticNoParamDelegate();

//实现
void StaticNoParamDelegate()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("BindStatic")));
}
//绑定全局静态函数
NoParamDelegate1.BindStatic(StaticNoParamDelegate);

//绑定类中的静态函数
NoParamDelegate1.BindStatic(类名::静态函数名称);

注意:可以绑定类和全局的静态函数,这里和之前BindUObject中不同的是参数前不用”&“运算符。

3)BindRaw

       绑定不是继承虚幻UObject类的函数,这个和BindUObject正好相反。首先需要在虚幻中创建一个不继承任何东西的类

然后在其类中顶一个不带参数和返回值的函数 

//函数定义
	void Raw_NoParamDelegateFunc();

//函数实现
void MyRawClass::Raw_NoParamDelegateFunc()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, FString::Printf(TEXT("Raw_NoParamDelegateFunc")));
}

注:在不经常虚幻基础类的其他类中也可以使用虚幻引擎的打印屏幕函数。

绑定这个函数

	MyRawClass* tempRawClass = new MyRawClass();
	NoParamDelegate1.BindRaw(tempRawClass, &MyRawClass::Raw_NoParamDelegateFunc);

注意绑定的参数第一个是对象指针,如下所示是另一种创建对象方式(这都是C++的基础,(;´༎ຶД༎ຶ`) 

	MyRawClass tempRawClass;
	NoParamDelegate1.BindRaw(&tempRawClass, &MyRawClass::Raw_NoParamDelegateFunc);

4)BindLambda

绑定一个Lambda表达式,Lambda表达式是一个匿名函数片段,即可以在函数内单独定一个一段函数,代码如下,使用关键字auto,其中”auto LambdaDelegateFunc = []()->void“,()里面可以添加参数,返回值可以将void类型改成其他返回值类型。

	auto LambdaDelegateFunc = []()->void
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("LambdaDelegateFunc")));
		};

	NoParamDelegate1.BindLambda(LambdaDelegateFunc);

Lambda表达式也可以直接写在要绑定的方法参数里,这个比上面的更简便,完全没有函数名

	NoParamDelegate1.BindLambda(
		[]()->void
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("LambdaDelegateFunc")));
		}
	);

5)BindSP

        绑定SharedPtr(智能指针)指向对象的函数,即纯C++类(非继承虚幻基类的类)的函数。因为纯C++类一般会使用TSharedPtr用于管理内存。

	//绑定智指针
	TSharedRef<MyRawClass> ObjSP1 = MakeShareable(new MyRawClass());
	NoParamDelegate1.BindSP(ObjSP1, &MyRawClass::Raw_NoParamDelegateFunc);

注意,这段绑定函数不要和之前的函数一样放在构造函数中,否则会出现绑定不成功的情况。

6)BindUObject

       绑定继承了虚幻引擎基类的类成员函数,前面案例中已经介绍使用,不再赘述。 

三、总结

3.1、单播可以定义三类委托,分别为带参数、返回值和参数+返回值。

3.2、声明委托的时候变量名称最好不要喝类型名称一样。

3.3、要绑定的函数和定义的委托在参数和返回值上要一致。

3.4、带返回值的和不带返回值的执行函数是不一样的。

3.5、解绑不是"UnBind",而实际上是“Unbind()"函数。

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

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

相关文章

YOLOv8改进 | Conv篇 | 轻量级下采样方法ContextGuided(涨点幅度)

一、本文介绍 本文给大家带来的是改进机制是一种替换Conv的模块Context Guided Block (CG block) &#xff0c;其是在CGNet论文中提出的一种模块&#xff0c;其基本原理是模拟人类视觉系统依赖上下文信息来理解场景。CG block 用于捕获局部特征、周围上下文和全局上下文&#…

Leetcode—2413.最小偶倍数【简单】

2023每日刷题&#xff08;六十&#xff09; Leetcode—2413.最小偶倍数 class Solution { public:int smallestEvenMultiple(int n) {return (n % 2 1) * n;} };运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连哦&#xff0c;点赞关注收藏…

新手HTML和CSS的常见知识点

​​​​ 目录 1.HTML标题标签&#xff08;到&#xff09;用于定义网页中的标题&#xff0c;并按照重要性递减排列。例如&#xff1a; 2.HTML段落标签&#xff08;&#xff09;用于定义网页中的段落。例如&#xff1a; 3.HTML链接标签&#xff08;&#xff09;用于创建链接…

小区生活污水处理需要哪些设备和工艺

在小区生活中&#xff0c;污水处理是一个非常重要的环节&#xff0c;它关乎到环境的保护和居民的生活质量。因此&#xff0c;了解小区生活污水处理所需要的设备和工艺是至关重要的。 首先&#xff0c;在小区生活污水处理中&#xff0c;需要用到的设备包括污水收集系统、初级沉淀…

详解RTC:以华人文化打造链上生态

文化是人类在发展的历史长河中淘洗出来的智慧结晶&#xff0c;随着人类社会的进步和变迁&#xff0c;经历了从口口相传到互联网等不同历史时代的传承和创新。在数字技术飞速发展的当今&#xff0c;区块链技术为文化的创新与传承提供了全新的空间和方式&#xff0c;使其得以在新…

【CANN训练营】CANN算子开发进阶笔记

Ascend C Tilling计算 Tilling基本概念介绍 大多数情况下&#xff0c;Local Memory的存储&#xff0c;无法完全容纳算子的输入与输出的所有数据&#xff0c;需要每次搬运一部分输入数柜进行计算然后搬出&#xff0c;再敲运下一部分输入数据进行计算&#xff0c;直到得到完愁的…

【深度学习】TensorFlow深度模型构建:训练一元线性回归模型

文章目录 1. 生成拟合数据集2. 构建线性回归模型数据流图3. 在Session中运行已构建的数据流图4. 输出拟合的线性回归模型5. TensorBoard神经网络数据流图可视化6. 完整代码 本文讲解&#xff1a; 以一元线性回归模型为例&#xff0c; 介绍如何使用TensorFlow 搭建模型 并通过会…

智能优化算法应用:基于松鼠算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于松鼠算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于松鼠算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.松鼠算法4.实验参数设定5.算法结果6.参考文献7.MA…

使用opencv的Laplacian算子实现图像边缘检测

1 边缘检测介绍 图像边缘检测技术是图像处理和计算机视觉等领域最基本的问题&#xff0c;也是经典的技术难题之一。如何快速、精确地提取图像边缘信息&#xff0c;一直是国内外的研究热点&#xff0c;同时边缘的检测也是图像处理中的一个难题。早期的经典算法包括边缘算子方法…

黑马头条--day01.环境搭建

一.前言 该项目学习自黑马程序员&#xff0c;由我整理如下&#xff0c;版权归黑马程序员所有 二.环境搭建 1.数据库 第一天&#xff0c;先创建如下库和表: sql文件如下: CREATE DATABASE IF NOT EXISTS leadnews_user DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_…

内存泄漏排查与预防

前言 内存泄漏问题&#xff0c;在C程序中经常会被开发测试人员忽略&#xff0c;最终会在客户运行现场因为内存泄漏最后导致程序内存耗尽&#xff0c;最后崩溃&#xff0c;从而影响客户的生产环境&#xff0c;导致异常发生。因为内存泄漏是一个共性的问题&#xff0c;所有的C程序…

html的学习笔记

开发工具&#xff1a;vscode 文字标签 h1:一级标题&#xff0c;h2&#xff1a;二级标题h6 p&#xff1a;段落标签 hr&#xff1a;分隔线 br&#xff1a;换行 strong/b&#xff1a;文字加粗 ins/u:下划线 em/i&#xff1a;倾斜 del/s&#xff1a;删除线 媒体标签 图片…

Vue前端与后端放在一起的搭建方式

1.首先把后端项目搭建好 去到项目的存放位置 2.然后cmd黑窗口输入命令创建vue项目 3.创建成功后回到后端项目进行合并 3.1在File处选择Project Structure 3.2选择模块 3.3找到自己的vue项目 3.4疯狂next最后create 3.5选择Apply并确定OK&#xff0c;恭喜您创建成功了 二、启动…

Star 4.1k!Gitee GVP开源项目!新一代桌面应用开发框架 ElectronEgg!

前言 随着现代技术的快速升级迭代及发展&#xff0c;桌面应用开发已经变得越来越普及。然而对于非专业桌面应用开发工程师在面对这项任务时&#xff0c;可能会感到无从下手&#xff0c;甚至觉得这是一项困难的挑战。 本篇文章将分享一种新型桌面应用开发框架 ElectronEgg&…

自动驾驶学习笔记(十八)——Lidar感知

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo 社区开发者圆桌会》免费报名—>传送门 文章目录 前言 Lidar感知 运动补偿 点云分割 总结…

day34算法训练|贪心算法

1005.K次取反后最大化的数组和 两次贪心算法思路 1. 数组中有负数时&#xff0c;把绝对值最大的负数取反 2. 数组全为非负数时&#xff0c;一直取反最小的那个数 步骤&#xff1a; 第一步&#xff1a;将数组按照绝对值大小从大到小排序&#xff0c;注意要按照绝对值的大小…

Linux--Docker容器(1)

这里写目录标题 简介名词解释作用 指令在本地创建容器的过程&#xff1a;&#xff08;这里以tomcat为例&#xff09;访问容器端口映射目录挂载验证端口映射验证目录挂载 删除镜像多小组访问容器mysql容器 简介 名词解释 Docker镜像&#xff1a;可以将镜像理解为面向对象的类&a…

Axure的动态图使用以及说明

认识Axure动态图 Axure动态图是Axure中的一种功能&#xff0c;它允许用户在原型中添加动画效果和交互动作&#xff0c;使原型更加生动和具有真实的用户体验。用户可以通过添加动态图来展示页面过渡、按钮点击、下拉菜单等交互操作的效果。 这是&#xff1a;就是我们今天要叫的…

深入理解模板引擎:解锁 Web 开发的新境界(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【Spark精讲】Spark作业执行原理

目录 基本流程 主要组件 Driver端 Executor端 Job提交执行流程 Task提交 Task执行 基本流程 用户编写的Spark应用程序最开始都要初始化SparkContext。 用户编写的应用程序中&#xff0c;每执行一个action操作&#xff0c;就会触发一个job的执行&#xff0c;一个应用程…