[UE C++] Interface 的使用

[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标记为蓝图可调用的函数,必须还存在BlueprintImplementableEventBlueprintNativeEvent标记。同时声明的函数不能是virtual函数(蓝图对于虚函数有特殊处理方法)
  • 若你希望该接口类只能在C++中被实现,蓝图无法实现。可将接口类标记为UINTERFACE(meta = (CannotImplementInterfaceInBlueprint)),但不能加上BlueprintImplementableEventBlueprintNativeEvent标记了,否则编译不通过。此时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);

参考链接

猜你喜欢

转载自blog.csdn.net/qq_52179126/article/details/129979662