ue4 怎么传递变量到另一个蓝图_UE4 C++ 之 C++中函数参数,返回值在蓝图中的表现形式...

3a8063ba4f7453f4add49ef7e49108c6.png

UE4 C++与标准C++在某些方面有些区别。本文针对函数参数这块,做一个简要的分析,如文章有所疏漏,可以在下方留言提醒,不吝赐教。

在看此文之前,假设你已经对ue4 c++有了基本的了解,以及对c++有一定的编程经验。

如果你想直接看文章的结构及总结,可以直接翻到文章末尾的总结。

为方便起见,首先在c++中创建一个Actor类。

并建立一个public变量MyNumber

UPROPERTY

并在这个Actor类的上方,创建一个结构体,用于测试传递对象作为参数时的情况

USTRUCT

然后在蓝图中创建一个Actor类,并继承这个Actor类,这样就可以开始我们的测试了。

一、值传递函数参数在蓝图中的表现形式

(1)int32

在c++中,添加这个函数。

UFUNCTION

在蓝图中,可以看到为这样的形式,也就是正常的值传递参数

9980c750e42a5baa7f73e9927080d1bd.png

简单测试一下

87b17c2e5673c9fb464b2252cc7d8f8d.png

由于MyNumber默认值为0,传入Test1_1后为值传递,因此MyNumber的值不会由于传入了这个函数而发生变化,所以显然print的结果还是0.

186e067fe00d64c7acc77093042217f3.png

(2)const int32

在c++中,添加这个函数。

UFUNCTION

在蓝图中,可以看到为这样的形式,也就是正常的值传递参数

283b90978c63c56c843d76ee7ae887b2.png

由于const,那么MyNumber的值肯定不会发生变化了,并且在函数中也无法修改其值.

17f35a1b565027e50e0f429928331c4b.png

Log信息为

88db2add8a3c01f3621c4f33c1c30c77.png

(3)关于默认参数

无论是int32还是const int32,都可以在c++中设定默认参数,并在蓝图中可以手动填充实参。

UFUNCTION

在蓝图中打开,默认值就发生了变化

22cf943a95e4aae8d2cf2b65c466af9e.png

如果不在c++中设定默认参数,则其默认值均为0。

a6594b9ae6f3742e9c2f9d545b1d2538.png

(4)FMyTest和const FMyTest

若为参数并非基本类型,而是其他类型,如一个类,如上方定义的FMyTest

UFUNCTION

在蓝图中显示为

1e8fa8a5e8174d50ed45981401e5a762.png

由于蓝图无法给这个自定义类型自动生成默认值,所以在蓝图中不会像int32一样能直接填写默认值

测试一下

c30509e03a28333e7e110703817b640e.png

可以看到,调用了FMyTest这个结构体的默认构造函数。

FMyTest

37a7c5f19653523e2e59fe4568e2ad3b.png

(5)FMyTest的函数默认参数

如果我们想像int32=100这样写一个函数默认参数呢?自然我们就想这么去写下。

UFUNCTION

然后发现

2aab59850f7128ca4520d22e8a694233.png

也就是说,我们无法对自定义类用这种默认函数参数的形式。只有去掉了UFUNCTION之后才可以对自定义类用函数默认参数。

void 

如果能在c++中写自定义类型的函数默认参数,并用于蓝图,麻烦告知我一声,我谷歌了一圈没搜到。


二、引用传递函数参数在蓝图中的表现形式

(1)const int32&

其为const引用传递,即形参作为实参的别名传入函数中,相当于实参传入函数中,但不可更改实参的值。(虽然是c++基本常识,在这还是提一下)。无论在蓝图还是c++中,都符合这个规则。

a)不带有默认参数

在c++中,添加这个函数。

UFUNCTION

在蓝图中,可以看到为这样的形式

22a26c25520e427766545dd3cb67afc3.png

测试一下,不填充这个参数的值,就会发生错误警告

013c520ab5a6636554ca278f3ab979b0.png

4e37db9be5e8c95ea910b0fa3deeb646.png

传入一个参数后

c3a45454ea8f2350200a38fa97a43eb8.png

5544149da20384054ac85028b6e1b01b.png
b)带有默认参数
UFUNCTION

若我们写成这种方式,在c++中就可以不传递函数参数,直接通过

test1_4

的方式就能调用这个函数。但在蓝图中,当我们不连接任何参数时,一样会报错。

06f9520df5a635fb4f66a250d9de325c.png

也就是说,无论在c++中const int32&是否有默认参数,在蓝图中,都必须以引用的方式,传递一个参数到这个函数中。

(2)int32&

这个可以说是最迷惑人的函数传递方式,当你兴致勃勃地这么写函数参数时

UFUNCTION

你期望的结果是

a6389dbac460c9789e35191b3410b165.png

但实际结果是,这个参数直接作为函数的返回值了,而并非你想要的上面的那种引用传递。在下面一小节会介绍如何真正引用传递函数参数的方式。

055c2f4ba06df6b2d884c31f08eb62fe.png

无论如何,我们先来看输出结果

7442db92321f8edfe778a3250c38b14f.png

528ef1b0eecbea09042d8722c7406be7.png

可见,因为我们没有输入默认函数参数,所以ue4给他设定了一个初始值0,这个道理同上面int32作为参数的情况。

而且由于c++的语法(这时候ue4又讲究c++的语法了),你无法给一个非常量引用类型的变量赋予一个右值,所以你也无法赋予其默认参数(在VS中就会自动报错)

fc42edbbdc3ff9de7541d751f6c92141.png

这种方式的最大作用,在于可以让我们返回多个参数。同样在标准c++中,也经常这么使用引用能让我们达到返回多个函数参数的目的

UFUNCTION

c9d0a16dadd8c205e4aa6e2e3f72fd36.png

(3)神技之 UPARAM(ref) int32&

既然无法用int32&来达到引用传递函数参数的目的,那么如何才能做到这点。

此时UE4就特定为我们准备了一个宏 UPARAM(ref)

UFUNCTION

ff20c8d6e3293a8b6b7566bdab08b15c.png

测试一下

e6d54191bbb53b3b431428ef021029d4.png

e95381c115d4c64686e0ade117d41869.png

可以看到,此时,MyNumber的值由原来的0变成了1,也就是说,真正实现了c++的引用传递。

(4)int32&&

在c++11中,可以通过右值引用,达到更省时省力的效果,但很可惜,只能在c++内部使用右值引用,无法将右值引用类型的函数参数暴露给蓝图。

UFUNCTION

a0d709eb07a94f4f024619bba034e5ad.png
编译就发生失败,可见右值引用无法传递给蓝图

三、指针传递函数参数在蓝图中的表现形式

由于引用的便捷性,我相信应该不会有要写int32 *这种需求的参数类型的函数吧。。。(有的话,麻烦告知我一声,而且编译时也不会通过)

个人认为一般就是传递多态的参数的时候需要用到指针,由于本人对于ue4智能指针的使用还没用过几次,而且暴露给蓝图的指针类型,基本都是原生指针,所以在这里不对智能指针的情况进行分析。

由于UE4所有需要带有反射类型的类,都必须继承自UObject,所以此处以UObject为例。并且UObject作为参数传递给蓝图使用时,只能用指针的形式

并且注意,任何指针,都要注意其是否可能为空指针的情况。

(1)UObject*

同c++的指针,可以更改传入的内容。地址传递。

UFUNCTION

在蓝图中为

7038c59e82e128fa060ae7dc115cb15a.png

测试一下,由于Actor是继承于UObject的,所以可以将子类作为参数,传入父类的指针或引用中,实现动态多态。所以下面这个函数可以正常执行。

46c0824aed29a978d2eb308cced465cf.png

0b3053f44773a5bb11c425fbda7c5e6a.png

(2)const UObject*

这样传进来的UObject的值就不能发生更改。即*UObject的值不能发生改变。

UFUNCTION

同样,在蓝图中的表现形式为

6f23972c82a9352dc144cddf1e9b0aee.png

(3)const UObject*const

由于const UObject只是底层const,即不能修改UObject的值,可以修改它的地址。针对上面的函数,如果你加一句

MyObject

64acccf92ea0e8e8978b27149b8a918d.png

这样可行的,不过肯定会在运行时报错。

而在c++,有这样一种const写法。即const UObject*const,底层+顶层 const

这样,你就无法用MyObject++了。

6a859acde56df74e9f2ad54460911693.png

这样就能限定MyObject不被修改了。

UFUNCTION

在蓝图中为

d0c8c4c35ef02bc459e2b154bfce6ff7.png

在c++中,当你既想传入原本的这个参数,又不想改变其地址的时候,下式成立

const 

但因为UE4的UObject只能以指针的形式传给蓝图。所以,这样写不成立。

UFUNCTION

2761627f04043b0375128a64207af7ad.png

(4)UObject*&

在学习数据结构时,特别是写树的时候,经常用到指针引用,来进行节点的连接和取消连接。

在UE4中,同样可以使用指针引用,来让传进来的指针的值和地址都会由于函数中的某些步骤而导致变化时,原来的实参的值和地址也会变化。

但由于UE4的引用的特殊性,按下面的方式写的话

UFUNCTION

没错,就是作为返回值了

ac2308e2cd1f91d3af5e95fd34475dba.png

此时,同样也可以用之前的UPARAM(ref)来解决这个问题

UFUNCTION

58e7994b06271308abc5eaa91761a7f0.png

但此时传入的UObject必须为可写的对象,不可为只读对象

比如传入self时,会报错

03ba5a93e71a7f727098d1a8407fe5ac.png

34f8c1d247435bb514b39abd6817bf86.png

所以这种类型参数的使用,只能用于可写的UObject对象。

(5)const UObject*&

无法用于蓝图,只能c++中这么使用

UFUNCTION

6f6c997479642743eb65548fe57ff17c.png

四、函数参数类型为数组(TArray)时

(1)TArray<int32>

(2)const TArray<int32>

(3)TArray<int32>&

(4)const TArray<int32>&

UFUNCTION

9e9cdef59502d47d4c9416e4717b545e.png

并且,除了TArray<int32>&对应的参数以外,其他所有参数,都必须连接一个参数,才能在蓝图中使用。其规则完全跟单纯int32的时候相同

不连接的话

b786eff50d8c073b6fb2f8c111408a15.png

34d66c729dd44693ef1c7f3d72ca3308.png

连接之后

e8d7ab753b0fc6fd5b963ab6dfb233e2.png

f1b849a8333c3548147390c1bf39b856.png

(5)TArray<UObject*>

(6)const TArray<UObject*>

(7)TArray<UObject*>&

(8)const TArray<UObject*>&

当容器中的对象类型仅为UObject*时,其表现方式跟int32一样

UFUNCTION

03eedcb8737b2afaa2948bf912103542.png

(9)容器中对象类型为const UObject*,const UObject*const和UObject*&时

UFUNCTION

506536e40721f58138f3f4e944c42ff8.png
UFUNCTION

304b4565dcabb651b695d8424cb804bc.png
UFUNCTION

452c6895168ab1fe0d24dd27e4ce1b14.png

前面两个原因好像是不能将指针转换成常量指针这种,后面一个,已经看不出错在哪了。所以结论就是,UE4的TArray里面的对象,对于原生指针,只能用UObejct*这种方式,加const修饰的和加引用的指针,都无法作为TArray里面的对象。


五、函数的返回值在蓝图中的表现形式

(1)int32

UFUNCTION

与用int32&的形参不同的是,此时在蓝图中,会显示一个return value

82617eb580c58c579668d543dec1f4cd.png

30fc47661d57a4daa4f129531d311f42.png

5446524d0aa4acc80cef1801cda6af0e.png

(2)const int32

UFUNCTION

跟int32在蓝图中的表现形式一样。

f78e20ad01f68ce2bedbe671fcf4d9b7.png

(3)int32&

在c++中,返回值为引用的函数,可以作为左值使用,但是在蓝图中,是没有左右值的概念的。

并且int32&作为返回值时,其返回的变量,必须不能局部变量。因为返回局部变量的引用在c++中就是大忌

因此,当你这么写时,肯定会报错

UFUNCTION

因此,我这边就用下面这个代码进行测试

UFUNCTION

1dd46e3e64fb6fa913bd625793a1994a.png

5d75b2696f2f7cbe3411561b3dfedc13.png

830f2fc717d5825f7e9181071385e8ad.png

(4)const int32&

UFUNCTION

b7319284dee13e2d0f72b7ef8ee16596.png

大坑之返回值为const int32,int32&,const int32&。

在c++中,如果你函数的返回值为int32,const int32,int32&,const int32&。那么完全是不同的结果,但是在蓝图中,你会发现,无论你把返回值设为上面四种形式中的哪一种,它在蓝图中的结果,均为int32对应的结果

首先我们写一个测试函数

UFUNCTION

2ba5ae01a4e738238749a0dad6ddf876.png

然后见证奇迹

a.const int32

在c++中,如果你返回值是const,那么就代表返回值是一个右值,是无法作为一个左值使用的。即,你无法写出这样的代码。

e4e9a1f597050916df70ff517ab4a254.png

但是在ue4中,我们的test4_2是这样的

UFUNCTION

按c++中的概念,其返回值是一个右值,是无法用于刚定义的ChangeInt()函数的。

但是在蓝图中,可以看到它竟然能连上去,并且能成功运行

5d731ca69435d7e9d0d1e5d22c9356ac.png

当然,输出结果为0,MyNumber的值没有发生改变。

85ca7aa89bda0b6a20f52b7966bbe15f.png
b.int32&

再来看看我们的test4_3,其返回值是number的引用,即在c++中,要是对这个返回值进行修改,number必然也会发生修改。

UFUNCTION

但是在蓝图中

b408a655b842d1511b5cce7ee627b130.png

结果还是1,并没有加上100

97d861d0e6588a2189397ca8b12ea0e8.png
c.const int32&

看看最后的test4_4,这个返回值就根本不能被修改。

UFUNCTION

但是在蓝图中,可以看到其返回值成功传入了ChangeInt这个函数中,并且根据结果为1来看,这个返回值的类型也变成了int32.

8e27c060a31f59d18e9947186b32ad2b.png

97d861d0e6588a2189397ca8b12ea0e8.png
d.结论

所以对于蓝图而言,返回值为常量或引用时,还是以值的方式返回。

这个结论同样适用于之前的参数为int32&的时候。如

UFUNCTION

在c++中是引用传递,但是,实际上你在蓝图中,只能看到这个作为返回节点,并不能作为函数参数的输入节点。因此,还是以int32的方式返回。

33fb3b0ce6d301de9ef840d17b5bd67c.png

(5)int32&&

无法通过蓝图返回右值引用

UFUNCTION

6920e49070d8f188427f4c3be99d2377.png

(6)UObject*

(偷个懒,我也懒得加指针判空了,用的时候注意加就好!)

就以当前创建的Actor对象为例

UFUNCTION

86c0fa605099c9518b2ac27b6b4e3475.png

测试一下

077e2c638da9b3fc56beabdb4462064e.png

53a82836f365242910a7b6dc3bfdcb30.png

可见,是可以返回指针的。并可以返回这个对象本身。还是比较有用。

其作用效果,跟在参数列表中写UObject*类型的参数是一致的

UFUNCTION

但在蓝图中是会变成这样,造成视觉上的误解。但无论是test4_6的返回值还是MyActor传入test4_6_1后的值,都是this。

7c37e7c35fbe3c3e2016d76d67d5c881.png

(7)const UObject*

无法作为返回值被蓝图调用,但在c++中可以正常使用

UFUNCTION

af68a4aa98d95099cc6db30fc08d6ad3.png

(8)UObject*&

可以作为返回值被蓝图调用

UFUNCTION

5048befbd1cd82ab8bfc6a0dd3a1186a.png

这种方式的返回方式,跟在函数参数列表中的UObject*&方式在蓝图中的表现方式一致,并且意义也相同。

通过下面这种方式也能实现一样的效果,但注意要传指针引用才能在蓝图中表现出一样的效果。

UFUNCTION

e66a8a87f9c5044a09a3e2b80c8bc305.png

如果想要将MyActor变为参数,加个UPARAM(ref)即ok。(见test2_5)


注意:返回指针和返回指针引用,在c++完全是两回事,但是一旦用于蓝图,可能返回的均为指针(在蓝图中表现为引用的形式)。

因找不到很好的测试案例,望有心人可以提供一下测试的思路。但鉴于上面对返回类型为int32&的分析结果(c++中返回类型为int32&,但在蓝图中被UE4改成了int32)。

所以,此处假设返回值为指针引用时,其仍然跟返回指针的效果一致。


(9)TArray<int32>

此处用到了Initializer_list的方式进行初始化容器。

UFUNCTION

在蓝图中

a5b44d1d5af94100aac9fe181cf5989a.png

ef4381584cfda886948e4f6733ce9a4f.png

60f2a12b3ce7c027eb0697ac9a224532.png

(10)const TArray<int32>

首先我们先在c++中写一个改变容器内的数值的函数,让容器内每一个数的数值加10

UFUNCTION

a0d1acf8e4480c92f802087b97c51318.png

然后写出这个函数

UFUNCTION

在蓝图中,表现为

38b955c4611c2e86a255d450f024f984.png

想必已经猜到结果了,没错,跟之前const int32做返回值一致,其返回值也被从const TArray<int32>改成了TArray<int32>。所以当然可以连上ChangeArray

27728e8af85d0daf660db1281c70cdc9.png

(11)TArray<int32>&

UFUNCTION

dfc95c761a3ccb231d54ab33f6b6fc29.png

并在蓝图中,创建一个数组

6d98ee04765273e4a680d703cbb8ab2d.png

测试一下

430626a2316c9e59cd87fb25ffd5cb25.png

当然,结果一定为0 1 2,而不是10 11 12,因为返回值被从TArray<int32>&改成了TArray<int32>。

323588890353eefde2891e2a1aea49c0.png

(12)const TArray<int32>&

UFUNCTION

9118d54214afd1977b36b42a736fc2a9.png

测试一下

8d57289540ea1c1754881d5c905175ba.png

同样,返回值由const TArray<int32>&被改成了TArray<int32>

323588890353eefde2891e2a1aea49c0.png

六、关于函数重载

在c++中,支持函数的重载,但暴露给蓝图的函数不支持函数重载。

UFUNCTION

3b6fa3a772283a606b77c85af6e4d924.png

显示两个函数发生冲突。

如果想支持重载,只能在c++内部用,而不暴露给蓝图。将UFUNCTION反射去掉就可以支持c++内部的函数重载

UFUNCTION

fb0e39ac19276283b077037095940499.png

七、蓝图中能实现函数参数传递形式

1.能设定默认值的基本数据类型

像bool,FString,int32,int64等基本数据类型,是能在蓝图内设定其默认值的。

f488535b787dbff6ed491614bfff1d2e.png

2.无法设定默认值的数据类型

大多数类型,都在蓝图内无法设定默认值,只能在c++中默认构造函数中设定默认值,或者在只用于c++的函数中用默认参数,比如FDateTime,FTransform等等。

2bf9edfc42a77d4d0f2bc948fb8a498a.png

3.值传递形式

有个很好的方式区分值传递和引用传递,当为值传递时,参数符号为这样

b77bb2f189b986356236e94498b925ce.png

4.引用传递形式

当改成引用传递后,变成了菱形

15d3228766e30887af16ae78946a7838.png

5.在蓝图中没有const传递的形式,只能在c++中设定这样的函数

6.返回值均为指针(但是表现为引用类型)或者正常的类型,不带有引用和const属性

e0f5cde07c2a3ef8f1f3c48548660d12.png

八、总结

(一)函数参数

1.值传递(第一部分)
  1. int32
  2. const int32

040cb453f96868c73d481e4bebe7aebe.png

此部分还有对默认函数参数的分析。

2.引用传递(第二部分)
  1. const int32&;
  2. int32&;
  3. 神技之 UPARAM(ref) int32& ;
  4. int32&&(不能用于蓝图)

b82bd5e040400de48dedd1b00d5d3c1b.png
3.指针传递(第三部分)
  1. UObject*;
  2. const UObject*;
  3. const UObject*const;
  4. UObject*&;
  5. UPARAM(Ref)UObject*&;
  6. const UObject*&(不能用于蓝图)

a131a2cf1f78a406f002864909bd5270.png

e5c4ca5ebef404ae6c7bf43a5072fd3a.png
4.容器方式传递(第四部分)

(1)TArray<int32>

(2)const TArray<int32>

(3)TArray<int32>&

(4)const TArray<int32>&

3f45f81d8e5dc3b0335a711510b8a26a.png

(5)TArray<UObject*>

(6)const TArray<UObject*>

(7)TArray<UObject*>&

(8)const TArray<UObject*>&

c7edb7852225a0c341a3076279500a3f.png

(9)容器中对象类型为const UObject*,const UObject*const和UObject*&时

无法放在TArray容器中。

(二)返回值(第五部分)

  1. int32;
  2. const int32;
  3. int32&;
  4. const int32&;(1-4的返回类型均被蓝图改为int32,详见上述分析--大坑之返回值为const int32,int32&,const int32&。)
  5. int32&&;(无法用于蓝图)
  6. UObject*;
  7. const UObject*;(无法用于蓝图)
  8. UObject*&;(6,8的返回类型应该都是UObject*,详见上述分析,但有待核实)
  9. TArray<int32>;
  10. const TArray<int32>;
  11. TArray<int32>&;
  12. const TArray<int32>&(9-12的返回类型均为TArray<int32>,详见上述分析)

(一)和(二)结论

在c++的返回值和函数参数中。如果在蓝图中,对应的参数为蓝图中的返回值,其中的const和&属性都被消除,但是若对应的参数为蓝图中的函数参数,其const和&属性都被保留。

(三)其他(六-七部分)

1.关于函数重载

蓝图无法实现,只能c++实现

2.关于蓝图中能实现的函数参数传递形式,详见第七部分

本只打算自己总结下,没想到扩展了这么多,也看到了一些坑,如有遗漏,欢迎补充,要是你觉得有用,可以赏个赞。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UE4,可以通过蓝图调用C++全局函数。首先,需要在C++代码声明并实现全局函数。具体的步骤如下: 1. 打开UE4的项目代码文件夹,并找到对应的.h头文件和.cpp源文件。 2. 在.h头文件声明全局函数。例如,可以在声明文件的末尾添加如下代码: ``` #pragma once #include "CoreMinimal.h" UCLASS() class MYPROJECT_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, Category = "MyFunctions") static void MyGlobalFunction(); }; ``` 3. 在.cpp源文件实现全局函数。例如,可以在源文件的末尾添加如下代码: ``` #include "MyBlueprintFunctionLibrary.h" void UMyBlueprintFunctionLibrary::MyGlobalFunction() { // 在此处实现全局函数的逻辑 // 例如,可以在这里添加打印语句 UE_LOG(LogTemp, Warning, TEXT("调用了全局函数!")); } ``` 4. 保存并编译C++代码,确保函数成功添加到UE4项目。 接下来,在蓝图调用该全局函数的步骤如下: 1. 打开UE4蓝图编辑器。 2. 在蓝图选择要调用全局函数的节点。 3. 在蓝图编辑器右侧的详情面板,找到“蓝图调用”节点。 4. 从“蓝图调用”节点的输出引脚拖出连接线,并将其链接到“执行入口”节点。 5. 在连接线上右键点击并选择“Convert to Previous C++ Function Call”(转换为C++函数调用)。 6. 在“函数调用”节点的类下拉菜单选择“UMyBlueprintFunctionLibrary”。 7. 在函数下拉菜单选择要调用的全局函数,例如“MyGlobalFunction”。 8. 保存蓝图并点击运行即可调用全局函数。 通过以上步骤,就可以在UE4使用蓝图调用C++全局函数了。调用全局函数可以方便地实现一些复杂的逻辑和功能,提高UE4项目的灵活性和扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值