UE4里面的线程问题的思考/UE4里面的delay是如何实现的

 

因为要做性能优化,所以,本来项目里面很多用蓝图实现的方法需要用cpp来实现,但是之前的一些需要延时的Event却不好在Cpp实现;

1.据我所知,在虚幻引擎中Functions和Events是两种完全不同的概念。

    Events是区分于主线程的单独的线程,不能有任何的返回值,而且是可以执行有延时的方法,比如说delay;

    而Function不像Events那样,他仍然是执行在主线程上的函数;

这是我所知道的CPP和蓝图之间的关于线程的问题。然而,困惑我的是如何在cpp里面实现这些可以延时的方法,如delay还有camerafade等方法;

我有一下两个问题:

1.UFUNCTION宏有两个相关的定义,一个是BlueprintNativeEvent一个是BlueprintIementationEvent。第一个让我们可以在CPP里面实现,然后在蓝图里面调用,第二个可以让我们在蓝图里面实现,然后在cpp里也可以调用。我看到这两个定义在CPP里面是可以给他添加返回值的。但是这个名字有开始误导了,他们都有Event在两个定义里,但是,他们都是用来实现Functions。这两个定义可以用来实现Events而不是方法吗?有没有其他的宏定义来实现这个功能 ?用宏定义BlueprintsNativeEvent可以实现延时的函数吗?他们能实现单独的线程吗?如果我不定义返回值,他们是不是自动的变成了Event类型了呢?

2.我注意到虚幻里面有一个Event类型的代理机制,你可以通过这个机制来实现让一个代理变成event。

如何在c++里面严格的实现Event类的事件?

 

我可以通过一个例子来再次阐明我的问题:

我希望实现player的demage。当player收到攻击的时候,他的camera需要转化到红色,delay一段时间,这段时间是根据受到的伤害的大小来决定的,然后,在让camerafade到正常的状态;这个很容易就在蓝图里面实现了。这在蓝图里面只需要三四个节点就可以实现了。

但是在c++里面,这个该如何实现呢? 这里有从论坛上找到的几个答案大都建议我用SetTomer来实现来代替Delay。但是这却增加了我不少的工作。如果是这样来做的话,我需要将把这个函数分成三个函数,分别将这三个函数和Timer关联起来,所以,每当要实现一个延时的效果的时候都要调用一个新的函数,即时我使用的是同一个Timer。这让我的类里面有很多的function去实现每一个延时的效果,这让里面的东西很难去管理。

如果我可以声明一个单独的线程或者Event来确保是一个独立的线程的话,我觉得这在cpp端是可以实现的;

上面说的是可以实现的吗?

如果是这样的话,如何在cpp实现?通过一个没有返回值的用BlueprintNativeEvent来定义的方法吗?或者是一个事件代理绑定到一个CPP方法吗?

 

因为,没有人回答我的问题,索性我就自己做了一些测试来判断BlueprintNativeEvents(或者普通的蓝图自定义事件)是不是单独的线程?

我的发现非常奇怪。

下面是我测试的类的头文件

下面是它的实现:

最初的想法是看看是否在调用NativeFooWithoutReturn()方法的时候,是不是阻止了主线程BegPlay(当然也包括整个游戏进程)的进度,我通过计算其中的阻隔时间的不同。

在NativeFooWithoutReturn_Implementation()里面我写了一点东西来增加它的运行时间,

  1)我添加了一个非常大的循环,可以执行到循环到比如:9999999。他阻止了整个游戏进程一个非常短暂的时间,所以,他看起来好像是运行在主程序的,而不是区分于Beginplay的单独区分出来的线程;

  2) 我也尝试用FProcess::Sleep()来代替循环函数,出现了相同的现象。

在第二次实验中,我尝试在继承自这个类里面实现的蓝图类里面实现的NativeFooWithoutReturn 。

令人惊讶的是,这个仍然组织了游戏的整个进程,所以,NativeFooWithoutReturn 不是一个单独的进程,而是运行在主线程上的;

我想在这里做一个主题笔记。BeginPlay()方法在C++端延时了接近2S,然而同样的代码都让游戏整体进度受到了阻碍。然而在CPP端这个阻碍还不到0.01s。这意味着蓝图的c++实现要比蓝图编辑实现快200倍(转化成CPP的另一个重要原因)。

接下来让你看看会让你吃惊的现象:

 

我已经添加了一个掩饰的节点在蓝图实现的事件中,我去,NativeFooWithoutReturn() 成了一个单独的线程。C++里面的BeginPlay()报告有0S延时,并且游戏整体进程都没有受到阻碍。并且Log的顺序也发生了变化,因为,C++里面的打印信息马上就出来了。

所以,这项测试的结果是:BlueprintEvents不是单独的线程。他们在主线程上执行除非你放一些延时的节点比如delay、CameraFade等节点等。

作为旁注,我还尝试声明一个名为FooEvent的委托(尝试多播,单播,事件等)并在BeginPlay()中绑定一个FooFunction。然后我调用了fooEvent.Broadcast()来代替NativeFooWithoutReturn()。NativeFooWithoutReturn_Implementation()具有完全相同的实现。

结果完全一样,delegate方法同样对主线程造成了影响。这意味着,即使你用代理来执行所有的方法,那么也是在引擎的主线程上来执行。

所以,好像我只有自己用Timer来实现一个单独的线程了。

所以,我自己回答了我自己的问题。

但是,我仍然对此问题的评论和简介非常感激。

 

这篇博文是转发来自问答中心的一个外国友人的关于如何在CPP中实现延时方法的讨论,自己翻译的同时也学到许多,分享给大家!如下(纯手打,手工翻译,如有错误还望指正):

原文连接:https://answers.unrealengine.com/questions/541571/how-to-implement-events-strictly-on-c-side.html?childToView=858490#answer-858490

 

我学到的:我的项目使用蓝图做的,但是,我要做的是,这个方法执行完毕以后再执行我的方法,我是因为项目中要用到delay节点,而不同的效果又是通过多个delay来实现的,因为delay和Timeline非常消耗性能,所以,我只是尝试查找关于delay的任何问题,偶然遇到了博主写的这篇文章,没想到给我带来了更多发现,让我对虚幻引擎的比较深层的设计有了更多的了解。他对我的影响主要体现在下面几点:

1.这种情况在写比较大的完整的项目中肯定会遇到,因为,你会经常遇到这种多线程问题,如果你是蓝图开发,那么你肯定会做优化,因为,特别是因为一些平台的性能限制和开发一些移动平台的应用的时候,因为平台的性能不会太高,所以一定要做线程方面的优化。像经常用到的蓝图节点,timeline、delay等。而如果在C++里面做这种多线程的事情,如果用Timer来实现,则会无比的麻烦,因此,这里面肯定还有更好的解决办法。

2.比如我们在做蓝图的时候,如果两个效果是有先后顺序的,那么在不用delay的情况下该如何实现呢?有没有一个节点实现完成了,然后再执行接下来的节点?据我所知,在蓝图中有一个节点是可以做到的(如下)节点中有一个变量是 should block on load 这个变量为真的意思是,只有在关卡加载成功后才能执行接下来的节点,是不是很酷啊?

3.与2问题正好相反的是,与上面文章所说到的,他是想同时执行的,而在蓝图里面都是同时执行的,你做两个事件,他们都是单独的线程,都是可以同时执行,不会阻碍游戏的整体进程,通过转述的文章你会发现这个问题,蓝图中遇到的问题恰好在CPP里面不是问题,而CPP里面的问题却是蓝图里面的问题,是不是很尴尬?

 

至于以上问题,可以通过Timer来写一个单独的线程:下面是官方文档!

https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/Timers

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值