目录
- 0 引言
- 1 直接蓝图通信
- 1.1 在关卡蓝图中直接拖拽Actor
- 1.2 Get Actor of Class/Get All Actors of Class
- 2 事件分发器
- 2.1 创建事件分发器
- 2.2 绑定事件分发器
- 2.3 调用事件分发器
- 3 蓝图接口
- 3.1 使用步骤
- 3.2 为什么要使用蓝图接口
- 4 蓝图转换
0 引言
- 问题:为什么需要掌握蓝图通信?
- 答案:在开发一个项目时,总是会遇到一个蓝图对象需要获取另一个蓝图对象的属性值或者函数功>能等。例如,在制作一个开关门时,人物蓝图就需要调用门蓝图的一些功能。
- 蓝图通信的四种方式:直接蓝图通信、事件分发器、蓝图接口、蓝图转换(castTo**)
1 直接蓝图通信
- 直接蓝图通信其实还有几种方式,下面一一介绍。
- 缺点:是只能实现一种蓝图和另一种蓝图通信,不能实现一对多的通信方式。
1.1 在关卡蓝图中直接拖拽Actor
- 这种方式只有在关卡蓝图中才能使用,因为只有关卡蓝图才能直接将本关卡中的Actor直接选中拖入蓝图中,如下图所示:
- 然后可以将两个需要通信的Actor都拖拽到关卡蓝图中,然后进行属性或函数的访问。
1.2 Get Actor of Class/Get All Actors of Class
- 利用Get Actor of Class(获取类的Actor)或Get All Actors of Class(获取类的所有Actors)这两个节点获得场景中的Actor。
- 这两个节点的区别是一个获取一个Actor(第一个实例化的Actor),另一个是获取所有实例化的Actor。
- 案例:两个蓝图,一个是BP_Pawn,一个是BP_Door,BP_Pawn需要调用Door蓝图中的OpenDoor函数,来实现开门的功能。
2 事件分发器
- 事件分发器(Event Dispatchers)可以实现一对多的蓝图通信方式,功能更加强大
- 其实事件分发器这个功能和MFC的消息/事件机制类似,和QT中的信号槽机制类似。所以学习过类似的编程思想的话,学习这个会更好上手。
- 使用场景:在一个游戏关卡中,一个BOSS的死亡,往往会触发很多功能,例如,爆装备、打开隐藏机关、人物升级等等。这就是一个事件发生后触发很多其他事件。这个业务就能使用事件分发器来实现。
- 使用步骤:在BOSS蓝图中创建一个事件分发器(EP_BOSSDead),然后机关蓝图、人物蓝图等其他需要响应BOSS死亡事件的蓝图中,绑定EP_BOSSDead事件分发器。
2.1 创建事件分发器
- 在BP_BOSS蓝图中创建事件分发器EP_BOSSDead。点击图中的加号即可。
2.2 绑定事件分发器
- 首先通过直接蓝图通信的方式获取BP_BOSS蓝图对象,然后绑定事件分发器。
- 绑定事件分发器的可以是事件,也可以是函数。(大家可以自己去试试)
2.3 调用事件分发器
- 调用事件分发器,Call ****,即可。事件分发器被调用后,之前和该事件分发器绑定的事件/函数,都会被调用。
3 蓝图接口
- 问题:什么情况需要使用蓝图接口?
- 答案:当同样的操作,需要不同的响应时,就可以使用蓝图接口(其实概念和C++中的多态一样)。例如,玩家按下键盘E键时,物体会被拾取,NPC会与玩家对话,等等。又或者子弹击中不同目标时响应不同,油桶会爆炸,NPC会死亡等等。
蓝图接口特性:
- 蓝图接口(简称 接口)是一对一通信方式,这点需要注意。
- 蓝图接口是函数的集合体,只有 函数 名称,没有 函数 实现。(其概念和C++中的接口基本一样,C++的接口。C++抽象类是至少有一个纯虚函数即可,还可以有属性值)
- 和C++中抽象类的区别:子类继承一个蓝图接口后,不重写接口函数,也能实例化(创建对象)。但是C++子类不重写父类的纯虚函数是无法实例化的。
- 蓝图接口通信的前提:①需要有被通信放的引用;②被通信方实现了蓝图接口函数;
- 从上一点通信前提不难看出,其实蓝图接口通信本质上还是直接蓝图通信,只不过是获取被通信方的引用后调用了蓝图接口函数而已。(所以不知道,为什么虚幻官方要单独将蓝图接口列为一种通信方式,可能比较特殊吧)
3.1 使用步骤
-
创建一个蓝图接口,是全局的。名叫(BPI_MyInterface),同时新建一个接口函数OnTakeWeaponFire(只有名称,参数信息,没有函数具体实现)
-
需要使用蓝图接口的类,在类设置(Class Setting)中继承该接口
-
编译蓝图后,再重写接口函数。(不编译蓝图的话,找不到接口函数)
-
获取被通信方的引用,然后通过引用调用接口函数(接口函数会出来两种,不要选错了,看下面图片)
3.2 为什么要使用蓝图接口
- 思考:既然 直接通信 和 蓝图接口 都是要先获取对象的引用再去调用函数,那直接调用类成员函数,和调用类的接口函数有什么区别呢?
- 解答:方便调用,降低程序耦合。假如使用直接调用类成员函数的方式来通信,在收信方(上面叫做被通信方)做的工作没有区别,都是实现响应函数而已。但是发信方所做的工作就繁琐了,需要知道对象的具体类型。
- 这个问题的同等于为什么需要多态?
- 如何实现多态:基类指针指向子类对象,然后调用纯虚函数,实现多态!!!
class animal
{
public:
virtual void name() = 0;
};
class cat : public animal
{
public:
virtual void name()
{
std::cout << "cat\n";
}
private:
int a;
double b;
};
class dog : public animal
{
public:
virtual void name()
{
std::cout << "dog\n";
}
};
void name(animal* MyNimal)
{
// 基类指针指向子类对象,然后调用纯虚函数,实现多态!!!
MyNimal->name();
}
int main()
{
// animal是抽象类,是不能实例化对象的。但是可以抽象类定义指针!!!
// 原因:编译器在编译时需要了解类的所有信息以便准确的为要实例化的对象分配内存;
// 由此可见,定义一个类对象是要生成一个类的实例的,而C++规定抽象类是不能实例化的。
// 但是抽象类指针的内存大小是可以确定的,因为指针都是4个字节。
animal* cat1 = new cat();
animal* dog1 = new dog();
name(cat1);
name(dog1);
}
4 蓝图转换
- Cast To *(类型转换为),其实就是将父类对象类型转换为子类对象类型。因为有些时候获取的并不是具体的子类对象,而是父类对象。
- 例如Event Hit事件返回的Hit结构体中的Hit Actor变量就是一个父类类型,因为这样设计可以大大降低程序的耦合性,因为谁也不知道以后会有多少种类是集成Actor的,不可能把每一种子类都重写一个函数,这样程序设计效率非常低。