找回密码
 立即注册
查看: 280|回复: 0

UE/C++编程方略(16)响应物体事件

[复制链接]
发表于 2022-9-13 09:06 | 显示全部楼层 |阅读模式
相关代码仓库:https://gitee.com/xarray/unreal-cpp-examples
在本节中,我们将尝试为场景中的Actor对象设置事件响应函数,也就是说,当例如鼠标掠过、点击、或者碰撞到其它角色等事件发生时,我们可以及时地从C++代码中监测到变化,并进行自己期望的处理操作。
我们首先回忆起第4节的内容,即建立一个默认的Cube网格对象,然后将它放置到场景中。这一次我们首先在Unreal编辑器中新建一个名为EventActor的类,并且将_mesh作为它的第一个组件对象。
静态网格体是可以产生鼠标掠过以及点击事件的,我们再添加一个空白的UBoxComponent,将它放在这个静态的立方体对象旁边,我们希望当场景相机或者其它角色接触到这个空白Box对象的时候,会触发碰撞事件。这种名为Overlap的事件触发机制,对于游戏中设置陷阱或者推进剧情发展来说是十分常见的;而非游戏应用当中,也完全可以用这种方法来监测用户操作的过程,并且及时进行响应。相比第12节中我们所使用的Raycast射线求交的方式,这样的事件触发机制无疑显得更为灵活,当然,前提是我们已经把需要触发的对象都收集在一起,并且使用本节中的方法去管理它们的事件回调函数。
我们的EventActor类的头文件声明如下:


在构造函数中,我们使用和第4节完全相同的做法来创建静态的立方体对象。而Box对象并不能同时作为Actor的Root component存在。事实上,考虑到这个虚拟的Box和实际的网格体之间需要有一个相对位置关系,我们最好是把_box作为_mesh的子组件,并且设置这两者之间的相对位置,来实现“一个可触发的虚拟Box陷阱”的效果。相关代码如下图所示:




之后我们将为这两个组件分别设置一些可触发的事件。我们在这里最基本的需求就是,当鼠标光标接触到_mesh立方体的时候,让这个立方体缓缓地转动起来,直到光标离开为止;但是在这个过程中,一旦点击了鼠标,则立即让立方体归于原位。
为此,我们需要在头文件中增加多个回调函数的声明。这里的UFUNCTION()定义负责将函数定义为可进行委托和反射的对象,这样它可以后续在蓝图等环境中被找到和自动加载。有关UFUNCTION的具体说明和详细参数定义,请参看:
https://docs.unrealengine.com/5.0/en-US/ufunctions-in-unreal-engine/
头文件的代码增加如下的字段,注意最上方的_hoverToRotate是一个用户自定义的bool量,负责判断是否让立方体自动旋转(就像它在第4节中做的那样)。


而在构造函数中,我们可以分别将各个回调函数设置给_mesh和_box两个组件。需要注意的是,这里的AddDynamic实际上是一个宏定义,而非直接使用了USceneComponent类的相关函数:


这种写法虽然方便,不过实际上也可以考虑使用标准的FDelegate委托方式来定义和使用UFUNCTION函数,如下面的代码所示:
FScriptDelegate delegator;
delegator.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("AEventActor::OnClicked")));
_mesh->OnClicked.Add(delegator);
下面我们将逐一了解每个事件回调函数的作用。首先是OnOverlapBegin()和OnOverlapEng(),它们被触发表示有其它Actor对象进入了_box所在的区域内,或者离开了所在区域。如果我们有相应的场景逻辑需要执行,例如在游戏者进入该空间时,产生一个新的敌人和他战斗,或者产生新的NPC与之对话,那么就可以在这里的回调函数中编写对应的功能。不过这里我们并没有更多需要做的事情,因此仅仅是在Log中给出提示即可。这两个回调函数中都可以获取到与当前Component产生碰撞的另一个场景对象的Actor实例参数,以及该Actor中实际产生碰撞的Component对象参数。


然后是判断鼠标光标进入物体区域,以及离开物体区域的OnHoverBegin()和OnHoverEnd()函数,根据前文中所述的需求,我们在这里将_hoverToRotate变量设置为true或者false,来指引立方体产生每帧的旋转动画,或者立即停止。


最后是鼠标点击物体的事件,这里我们不需要使用之前的Raycast函数就可以知道鼠标是否点击了该对象。此时我们需要把场景对象的旋转值复位,并且让它不要继续旋转,直到鼠标光标离开并再次进入到_mesh的屏幕显示范围中为止。


最后别忘了在BeginPlay()和Tick()函数中添加相关的代码。因为要考虑鼠标光标的显示和鼠标点击,因此我们启动关卡时不能让光标自动被隐藏,这个功能的实现是第12节中介绍过的;而Tick()函数中只需要判断_hoverToRotate并且让当前Actor对象缓慢地以自身Z坐标为轴旋转,这是早在第4章的时候就已经介绍过的内容了。


这里唯一需要注意的是,因为我们有两个组件对象被同时加入到EventActor中,因此为了方便我们在程序运行时看到Actor对象的包围体范围,进而知道自己进入到哪个区域后会触发Overlap事件,我们在启动时加上了一个DrawDebugBox()函数,用来绘制一个虚拟的线框立方体,帮助进行调试。如果不需要这个调试辅助对象的话,可以直接将它屏蔽掉,或者在必要的地方执行FlushPersistentDebugLines()清除已经显示的虚拟调试线框。没错,这也是第12节中曾经涉及到的问题。




从图中可以看到,当我们操作第一人称相机冲到_mesh对象的上方,即_box对象所在的区域时,将触发回调函数,并显示DefaultPawn对象(即我们的摄像机)产生了Overlap事件;而鼠标光标进入_mesh立方体的区域时,立方体将不断旋转。
除此之外,Actor和Component对象还支持其它一些回调事件,可以在创建对象,关卡开始,销毁对象,关卡结束等多个事件产生时被触发,读者可以根据自己的需要实现和响应自己所需的场景对象事件。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 12:01 , Processed in 0.066794 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表