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

一、前言

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

        使用的虚幻版本为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()"函数。

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

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

相关文章

MySQL进阶2 - 索引

MySQL进阶1 - 索引 1. 索引概述2. 索引结构2.1 二叉树2.2 B-Tree(多路平衡查找树)2.3 BTree2.4 Hash 3. 索引分类4. 索引语法5. SQL性能分析5.1 SQL执行频率5.2 慢查询日志5.3 profile5.4 explain执行计划5.3.1 EXPLAIN执行计划各字段含义&#xff1a; 6. 索引使…

服务器一直掉线怎么回事?

随着网络的高速发展&#xff0c;不管是网站还是游戏&#xff0c;如果遇到服务器卡顿的情况&#xff0c;会造成用户访问网站或进游戏&#xff0c;网站页面长时间无法打开&#xff0c;游戏页面运行卡顿&#xff0c;这样就很容易会造成用户的流失&#xff0c;从而导致业务亏损极大…

Redis设计与实现之跳跃表

目录 一、跳跃表 1、跳跃表的实现 2、跳跃表的应用 3、跳跃表的时间复杂度是什么&#xff1f; 二、跳跃表有哪些应用场景&#xff1f; 三、跳跃表和其他数据结构&#xff08;如数组、链表等&#xff09;相比有什么优点和缺点&#xff1f; 四、Redis的跳跃表支持并发操作吗…

消除非受检警告

在Java中&#xff0c;有一些情况下编译器会生成非受检警告&#xff08;Unchecked Warnings&#xff09;。这些警告通常与泛型、类型转换或原始类型相关。消除这些警告可以提高代码的可读性和安全性。以下是一些常见的非受检警告以及如何消除它们的例子&#xff1a; 1. 泛型类型…

0基础学习VR全景平台篇第128篇:720VR全景拍摄设备推荐

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 本篇教程&#xff0c;小编为大家推荐720VR全景拍摄所需要的设备器材。上节我们提到&#xff0c;理论上任意相机和镜头都能够拍摄全景&#xff0c;但为了标准化制作流程&#xff0…

oracle 锁表解决办法

相关表介绍 V$LOCKED_OBJECT&#xff08;记录锁信息的表&#xff09;v$session&#xff08;记录会话信息的表&#xff09;v$sql&#xff08;记录 sql 执行的表&#xff09;dba_objects&#xff08;用来管理对象&#xff0c;表、库等等&#xff09; 查询锁表的 SID select b.…

t-io 程序执行后,jvm不退出的原因

基于t-io 1.7.3 版本分析源码 1、设定当前时间&#xff0c;每10毫秒执行一次 (非守护线程) 2、对应线程池的核心线程在AioServer启动时全部激活&#xff0c;并且添加空任务到阻塞队列&#xff0c;让核心线程(非守护线程)一直存活

等等Domino 14.0FP1

大家好&#xff0c;才是真的好。 节奏确实太快了&#xff0c;有时候我深感我也追不上。 以前Notes Domino是三年磨一剑&#xff0c;也就说每三年才发一个大版本&#xff0c;从2019年开始&#xff0c;进行了高频提速&#xff0c;居然一年一个大版本&#xff01; 周末&#xf…

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

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

Linux--Docker容器(1)

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

黑豹程序员-axios+springmvc传递数组

问题 奇怪的现象&#xff0c;axios在往后台传递数组时&#xff0c;springmvc竟然接收不到 解决 尝试多次无果&#xff0c;突然看一篇文章写vue中的数组不是真正的数组需要强转转化JSON.stringify 将信将疑下测试了一把&#xff0c;还真的传递成功了。 不光要JSON.stringify…

[PyTorch][chapter 6][李宏毅深度学习][Logistic Regression]

前言&#xff1a; logistic回归又称logistic回归分析&#xff0c;是一种广义的线性回归分析模型&#xff0c;常用于数据挖掘&#xff0c;疾病自动诊断&#xff0c;经济预测等领域。 逻辑回归根据给定的自变量数据集来估计事件的发生概率&#xff0c;由于结果是一个概率&#xf…

linux内核使用ppm图片开机

什么是ppm图片 PPM&#xff08;Portable Pixmap&#xff09;是一种用于存储图像的文件格式。PPM图像文件以二进制或ASCII文本形式存储&#xff0c;并且是一种简单的、可移植的图像格式。PPM格式最初由Jef Poskanzer于1986年创建&#xff0c;并经过了多次扩展和修改。 PPM图像…

Axure的动态面板的使用

目录 1.什么是动态面板&#xff1f; 2.使用动态面板 ​编辑 轮播图 erp的登录系统 erp侧边栏 1.什么是动态面板&#xff1f; 动态面板是Axure的高级交互元件&#xff0c;由不同的状态面板组成&#xff0c;是我们制作交互过程中运用频率最高的元件&#xff0c;很多交互效果需…

【Docker】实战:nginx、redis

▒ 目录 ▒ &#x1f6eb; 导读开发环境 1️⃣ Nginx 拉取 Nginx 镜像nginx.conf启动 Nginx访问 Nginx 2️⃣ redis拉取 Redis 镜像启动 Redis 容器测试 Redis &#x1f4d6; 参考资料 &#x1f6eb; 导读 开发环境 版本号描述文章日期2023-12-15操作系统Win10 - 22H222621.2…

网站服务器/域名/备案到底有什么关联?

​  在一个网站的组成中&#xff0c;网站服务器、域名、备案这几个要素是要被常提到的。在谈及三者关联之前&#xff0c;我们先了解下三者的各自概念。 域名&#xff1a;它是网站的唯一标识符&#xff0c;通俗理解来说就是用户在浏览器地址栏中输入的网址。一般来说&#xff…

MySQL作为服务端的配置过程与实际案例

MySQL是一款流行的关系型数据库管理系统&#xff0c;广泛应用于各种业务场景中。作为服务端&#xff0c;MySQL的配置过程对于数据库的性能、安全性和稳定性至关重要。本文将详细介绍MySQL作为服务端的配置过程&#xff0c;并通过一个实际案例进行举例说明。 一、MySQL服务端配…

新手HTML和CSS的常见知识点

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

首次使用 git 配置 github,gitee 密钥

gitee 和 github 密钥配置 1. 检查配置信息 使用命令 git config --global --list 检查邮箱是否一致 不一致可以使用如下命令进行设置 git config --global user.name "name" git config --global user.email "emailqq.com" 2. 生成 SSH 密钥 # 为 G…

2023年19款最佳3D打印软件

https://wenku.baidu.com/view/c0551497cf7931b765ce0508763231126edb77e3.html 2023年19款最佳3D打印软件 有免费也有付费&#xff01;十款入门至专业级的3D打印软件推荐 【云图创智】23种最好用的3D打印软件&#xff0c;常用的3D打印软件都在这里了