事件分发器、蓝图接口等蓝图通信详解
直接通信
考虑一个场景,当关卡boss被击杀后,前往下一关的门就会被打开,我们可以使用直接通信来实现。就通信而言,由三个部分组成:
- 通信的发送方
- 通信的内容
- 通信的接收方
比如上面的场景中,我们在boss销毁(Destroyed)的时候与门通信,让其打开。那么重点就在于找到通信的接收方,有如下几种方式:
- 指派(Assign) —— 可以在场景中的对象实例中直接设置对象的引用
- 碰撞(Collision) —— 碰撞事件/重叠事件会得到与之碰撞的对象的引用
- 创建(Crate/Spawn/Add…) —— 一些创建对象实例的方法会返回创建好对象的引用
- 获取(Get/Get all) —— 比如我们可以查询场景中的指定类的对象实例,返回对象引用的数组
上面的方式一般都要配合 Cast To
类型转换使用,来得到我们所需类型的对象引用。
比如这里我们在场景中直接指派通信的接收方对象的引用。
获取到通信接收方的对象引用后,我们就可以进行通信了。
在Boss的Destroyed
的事件调用Door
的CS_Open
自定义事件(补充这里调用Door的自定义事件是触发,所以和当前执行流程是并行)。
事件分发器
事件分发器类似于设计模式中的观察者模式,通信的接收方可以随时绑定和接触事件分发器,多个通信接收方可以监听同一个通信的发送方。
比如下面例子,boss被击杀后,关卡中的门打开,且玩家的经验值增加。
首先在 Boss
蓝图类中创建 OnKilled
事件分发器,并在Destroyed
时发送消息。
在Door
蓝图中将事件分发器与事件绑定,这里可以看出消息的接收方得知道消息的发送方是谁,而消息的发送方不用知道消息的接收方是谁。
同样的在Character
蓝图中也将事件分发器与事件绑定
这样在Boss
类对应实例被销毁时,Door
类的对象实例和Character
类的对象实例会监听到,进而执行相应的事件(并行的)。
注这里只是举个例子,上面的代码实际会遇到问题,因为在Door
类和Character
中GetActorOfClass
时,Boss
对象实例已经被销毁了,所以Get不到。
蓝图接口
蓝图接口主要用于抽象,比如场景中物体都可以被交互,但他们交互产生的结果是不一样的,在不同的蓝图类中都定义这样函数比较麻烦,那么我们就可以定义一个 OnTouch
蓝图接口,只需要在可以交互的物体蓝图类中,对OnTouch
进行不同的实现就可以。
这里我们新建一个蓝图接口,然后在其中添加一个函数 OnTouch
那么在一个蓝图类中可以添加这个蓝图接口来实现
下面我们来实现接口函数
我们在其他蓝图类中可以触发接口函数,这里有两种类型
Interface Call
是同步,即执行完OnTouch
之后才会继续后续执行流程Message
是异步,即只是发送了信息,让对应对象执行,并不会阻塞调用者的后续执行流程
这里我们循环调用要交互的对象,这里的数组为 Actor
对象引用数组,我们可以将任意Actor类/子类对象引用加入数组中,如果实现了 OnTouch
接口函数则执行相应流程,如果未实现则无事发生。这种实现方式实现了解耦,我们可以随意添加/删除实现该接口的对象,而不会影响下面调用该接口函数的代码,且不同类型的对象也不需要通过Cast To
来判断是否是我们可以交互的对象。
可以想想如果使用直接通信的代码要怎么写?,到这里我们也可以看出事件分发器是被动通信,而蓝图接口是主动通信。
总结
使用事件分发器通信的特征总结如下:
- 一对多
- 逆向监听
- 发信方不用知道接收方的信息,接收方需要知道发送方的信息
- 给关卡蓝图发信方便
- 没有返回值
- 在运行时可解除
使用蓝图接口通信的特征总结如下:
- 一对一(当然可以通过循环的方式给多个对象发信)
- 顺向发信
- 发信方需要知道接收方的信息,接收方不用知道发送方的信息,只要执行就好
- 给关卡蓝图发信不方便
- 可以有返回值
- 在运行中不可解除