[UE C++] Interface 的使用
1. 概念
接口类有助于确保一组(可能)不相关的类实现一组通用函数,这些类没有公共的父类但需要相同的功能,这种情况下推荐使用接口。
原生C++是不存在接口的,C++开发者就用纯抽象类代替接口。在UE中,为了使接口能被纳入反射系统进行管理,引入了UInterface。UInterface继承于UObject,但当UE开发者需要使用接口时,若直接继承于UInterface,会出现菱形继承的问题。虽然可以用虚继承缓解,但并不保险。所以当我们创建Interface时会出现两个类,其中第二个类才是开发者定义接口函数的地方,它不继承于任何类,避免了菱形继承。第一个类继承于UInterface,主要是为了让接口类被纳入UE反射系统
2. 创建接口
New C++ Class 滑到最下面选择Unreal Interface,不要点击show all Classes进行选择,搜不出来
3. 声明接口函数
3.1 在C++中定义
UFUNCTION()
virtual void UFUNCTIONTesFun(float value) {};//UFunction虚函数
virtual void VirtualTestFun(float value) {};//普通虚函数,须有定义
virtual void PureVirtualTestFun(float value) = 0;//纯虚函数
注意事项:
- 接口函数必须声明为virtual函数才有意义,若不加virtual关键字,编译可不通过(有UFUNCTION标记),若通过发生的也是函数覆盖,而不是函数重载。
- Not Pure virtual 声明的函数,在接口中必须有相应定义,上述例子的 “{}” 就是通过空定义来防止编译不通过
- 一旦接口存在Pure virtual函数,C++中继承此接口的类必须实现该函数,否则编译不通过。蓝图实现此接口的类无影响
3.2 在蓝图中定义
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
void BlueprintImpTestFun(float value);//不能有virtual
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void BlueprintNatTesFun(float value);//不能有virtual
virtual void BlueprintNatTesFun_Implementation(float value) {};//可以不在接口类中声明定义。只在实现自该接口的C++类中声明并定义
注意事项:
BlueprintCallable
标记为蓝图可调用的函数,必须还存在BlueprintImplementableEvent
或BlueprintNativeEvent
标记。同时声明的函数不能是virtual函数(蓝图对于虚函数有特殊处理方法)- 若你希望该接口类只能在C++中被实现,蓝图无法实现。可将接口类标记为
UINTERFACE(meta = (CannotImplementInterfaceInBlueprint))
,但不能加上BlueprintImplementableEvent
或BlueprintNativeEvent
标记了,否则编译不通过。此时BlueprintCallable
单独存在,表面此函数可被蓝图调用
4. 实现并使用接口类
这里放出我的接口类
UINTERFACE(MinimalAPI)
class UInterfaceTest : public UInterface
{
GENERATED_BODY()
};
class STUDY427TEST_API IInterfaceTest
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
void BlueprintImpTestFun(float value);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void BlueprintNatTesFun(float value);
virtual void BlueprintNatTesFun_Implementation(float value) {};
UFUNCTION()
virtual void UFUNCTIONTesFun(float value) {};
void NormalFun(float value);//此函数无作用,用于测试蓝图覆盖
virtual void VirtualTestFun(float value) {};
virtual void PureVirtualTestFun(float value) = 0;
};
4.1 实现接口类
前文所提到的IInterfaceTest
才是开发者声明接口函数的地方,所以需要继承它;而UInterfaceTest
是用于反射的辅助类
class STUDY427TEST_API AInterfaceTestActor : public AActor, public IInterfaceTest
4.2 判断是否实现了接口类
4.2.1 通过Class
使用的是UInterfaceTest
,这里用的是反射来判断
bool bIsImplemented = false;
bIsImplemented = this->GetClass()->ImplementsInterface(UInterfaceTest::StaticClass());
bIsImplemented = this->Implements<UInterfaceTest>();
Implements<UInterfaceTest>()
内部其实调用的GetClass()->ImplementsInterface(UInterfaceTest::StaticClass())
方法
4.2.2 通过Cast
使用的是IInterfaceTest
,C++类继承自IInterfaceTest
,所以Cast对象是它
IInterfaceTest* MyInterfaceTest = Cast<IInterfaceTest>(this);
if(MyInterfaceTest)
{
//内部逻辑
}
注意事项: 这种方法无法判断蓝图类是否实现该接口,因为蓝图类使用接口并不是通过继承,所以Cast无效。使用Class的判断方法(4.2.1)
4.3 调用接口函数
4.3.1 在C++中定义的函数
使用普通的C++函数调用方式即可
IInterfaceTest* MyInterfaceTest = Cast<IInterfaceTest>(TestActor);
if (MyInterfaceTest)
{
MyInterfaceTest->UFUNCTIONTesFun(10.f);
MyInterfaceTest->PureVirtualTestFun(20.0f);
MyInterfaceTest->VirtualTestFun(30.0f);
}
4.3.2 在蓝图中定义的函数
使用IInterfaceTest::Execute_XXXXX(Actor, Parameter···)
,若还是用上种方式强行调用,则可能会出现编辑器崩溃
//使用之前判断是否实现该接口更保险
IInterfaceTest::Execute_BlueprintImpTestFun(this,50.0f);
IInterfaceTest::Execute_BlueprintNatTesFun(this, 60.0f);