蓝图通信三

来源:互联网 发布:生态学现状知乎 编辑:程序博客网 时间:2024/05/29 06:30

对类功能进一步封装完成蓝图通信

CSDN的博客添加图片太麻烦了,而且链接网页图片会出现错误,如果需要看详细文章,请访问我的微信公众号《猎梦虚幻研究社》

在前一篇中,我们已经将蓝图通信的功能成功的从关卡蓝图中封装到了独立的蓝图类中。

现在我在这里提出一个新的需求:

当我们指定物体之后,还是需要先记录“门”的初始位置,然后在初始位置的基础上进行移动。有没有办法不记录初始位置达到效果呢?省去一个变量,并且减少BeginPlay中的逻辑。

所以我决定将“门”也创建成一个Actor类,开门的动作直接改为这个Actor类自身局部坐标的改变,这样就将功能分解了,避免了功能的堆积(虽然门通常不会用这种方法来实现,但是因为它的逻辑直观简单,所以我用这个例子来讲解蓝图通信)。

说到这里,我们转回内容浏览器再创建一个Actor类,命名为BP_Door来当做我们的门。

这个蓝图中同样添加一个StaticMesh组件,然后设置为我们准备好的门的模型。因为触发的事件我们放在了BP_OpenDoor蓝图中,所以我们只需要一个模型即可。我们不改动模型组件的名称,仍然叫StaticMesh(记得给这个组件赋一个实际模型)。
这里写图片描述
这个地方说明一下,可能有些人注意到我的根组件已经改名叫Root了,不再使用默认的DefaultSceneRoot作为根组件。实际上我只是创建了一个SceneComponent改名为Root,对默认根组件进行了覆盖
这里写图片描述
这属于个人开发习惯,不是必须操作。如果以后在我的文章中看到根组件为Root,执行相同操作即可,今后不再赘述。

现在离开我们创建的BP_Door,回到上一篇创建的BP_OpenDoor蓝图,在我们获取到AimActor之后,将他转换成我们创建的BP_Door蓝图:
这里写图片描述
这个地方用到Cast是因为,我们通过Actor变量在场景中指定对象获取到了一个Actor对象,可实际上它是一个BP_Door。然而引擎是按照Actor来识别并获取到这个对象的,所以我们需要手动用Cast函数将它还原成我们的BP_Door。

这个时候我们得到的就不只是一个Actor了,而是我们创建的BP_Door,也可以从其中获取到我们的StaticMesh组件(这里注意,搜索到StaticMesh组件是因为之前我们将BP_Door中的模型命名为StaticMesh,如果更改为别的名字比如MyDoor,则在Cast函数后面就要搜索MyDoor来获取到这个组件)。
这里写图片描述
如果你Get到的组件和我一样是灰色的
这里写图片描述
那么将BP_Door编译一遍即可。

这时就能直接获取到StaticMesh了,这里的StaticMesh就是我们在BP_Door中创建的门的模型,这时候我们只需要设置它的局部坐标就能实现与上面相同的功能了。这里要用到要一个函数:SetRelativeLocation。
这里写图片描述
这个函数和SetActorLocaiton的区别在于,之前使用的SetActorLocation是直接设置Actor的世界坐标,而SetRelativeLocation是用于设置组件的局部坐标。

这时BP_OpenDoor蓝图的逻辑如下图:
这里写图片描述

最后记得在场景中重新指定一下AimActor变量为我们新建的BP_Door蓝图(在场景中用变量旁边的小吸管选中场景中的BP_Door蓝图,操作方法同上一篇)。

这样就完成功能了,这时实现的功能和之前相同,但是从逻辑层面则向优化靠近了一步。

我们创建了一个蓝图,这个作为专职的开门触发器,里面有一个模型和一个Box触发器,并且通过变量来指定到我们“门”的蓝图。当触发开门时,通过触发器蓝图中的逻辑,使“门”蓝图里的模型改变局部坐标从而间接改变在世界中的位置。

这样改动的意义是,提高了现有函数和属性的利用率,去掉了一个Vector变量,将对坐标的操作从世界转缩小到了局部。

到这里,新提出来的需求基本上就算完成了。但是可以看出,这样做仍然很麻烦,从开发效率上讲,与其多创建一个蓝图,不如多添加一个变量,之前说的优化貌似是简化了,可实际上反而显得更加复杂。而且模型的名字不可以随意更改,否则逻辑就会出现错误。这样是很不可取的。

所以现在,我们再进行进一步的封装,真正使“由关卡蓝图转换成两个独立蓝图”这个动作变得有意义。

我们先整理一下思路:

上面我们做的是,BP_OpenDoor直接改变BP_Door里的组件的相对坐标来实现功能,实际操作逻辑还是在BP_OpenDoor中。BP_OpenDoor蓝图作为触发器,还实现了移动“门”位置的功能。

为了让功能的耦合性更低,功能界限更加明确,现在我们决定进一步的专职专用,让BP_OpenDoor仅仅用来触发事件,而实现的逻辑则整个放到BP_Door中。

也就是说触发器只负责触发,谁需要产生效果,则由谁来实现这个功能。

说完了思路,让我们回到BP_Door蓝图中。

在事件图表(EventGraph)中,右键输入custom来添加一个自定义事件,我将它命名为OpenDoor:
这里写图片描述
然后将组件面板中的StaticMesh拖放到场景中。在介绍蓝图组件面板时提到过,直接拖放组件调用的默认是Get方法。现在我们设置这个StaticMesh组件的局部坐标,来实现“门”的移动。

创建Timeline,时间轴的设置与之前相同。

设置局部坐标仍然是使用SetRelativeLocation函数,完整逻辑如下:
这里写图片描述

这个动作是在BP_Door中添加了一个自定义事件,当这个事件被调用时,会自动执行后续的逻辑。

所以现在要做的是,当BP_OpenDoor这个触发器被触发时,调用BP_Door中的OpenDoor事件。
这里写图片描述
这个时候将BP_OpenDoor中的逻辑更改为如下图所示:
这里写图片描述

当同时设置好了BP_OpenDoor和BP_Door以后,就能完整实现开门功能了。

当我们做到这一步时,对类的封装就完成了。

按功能划分不同的类,需要谁去产生效果,就由谁去执行逻辑。触发功能的类就仅仅只负责触发事件,通知相应的类去执行功能。

这样是标准的做法。假设我们的“门”除了打开之外,还会在触发错误的时候对玩家造成伤害,这时候就可以创建一个MakeDamage事件来对玩家造成伤害。当玩家触发了会造成伤害的事件时,只需要调用这个MakeDamage事件就可以,将功能的实现和调用分开来使功能便于管理,使逻辑变得清晰。

对于开门和开灯的蓝图交互就到这里,在最后说一下,对于开门和开灯这样的逻辑没有必要分成两个部分,直接将触发和实现效果合成一个蓝图类即可,这样就达到了一个最独立的状态,不干扰任何其他的类,完全独立工作。在后面我将简单介绍创建一个独立的门或者灯来完成功能。

这里写图片描述

原创粉丝点击