UE Gameplay入门50(C++/蓝图接口使用方法)


Unreal有一套自己的反射机制,编码都是围绕UObject在进行,其中包括接口的使用方法也是一样;我刚开始接触接口是在蓝图中的使用,
蓝图中使用接口相对来说比较简单,只要定义了蓝图接口,在任意地方都可以被调用,通过传递调用的对象来执行接口方法,
在C++中有所不同,但依然遵循蓝图通信协议;我们定义的接口当然是要在蓝图及C++都可以被调用到,
这样编码才会方便后续的拓展,有两种接口,分别是C++层和C++及蓝图都需要被调用到的接口


#1. 蓝图使用接口
蓝图的接口都是应用在蓝图层,不能被作用到C++,蓝图的功能相对来说是比较独立于C++的,
C++的功能可以被映射到蓝图,反之不可以

#1-1.定义蓝图接口
在这里插入图片描述
通过Blueprints中的Blueprint Interface来创建一个蓝图接口
在这里插入图片描述
在蓝图接口中添加自己需要的接口方法
在这里插入图片描述
添加接口需要输入参数和返回值类型,Inputs输入参数,Outputs返回数据

#1-2. 使用蓝图接口
在这里插入图片描述
在需要实现接口的蓝图类中打开Class Settings
在这里插入图片描述
在类设置中,添加我们定义的蓝图接口,这里也可以定义C++定义的接口,不过不能被调用到
因为C++和蓝图并不是同一作用域的,C++不知道蓝图有哪些方法,相对来说C++更底层,蓝图是应用层
在这里插入图片描述
在图表列表中会展示我们接口定义的方法,因为这是纯蓝图的,可以被完全兼容使用
在这里插入图片描述
蓝图接口的使用,需要注意需要传递Target,也就是接口执行的对象,该对象必须已经实现了这个接口,
然后执行这个接口里面的方法,蓝图中任意地方都可以调用接口


#2. C++使用接口
C++中使用接口相对蓝图来说复杂多了,并且容易晕,其实是在遵循蓝图通信协议

#2-1. 定义C++中的接口

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UExampleInterface : public UInterface
{
	GENERATED_BODY()
};

class MONTAGETEST_API IExampleInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	UFUNCTION(BlueprintNativeEvent)
    void BlueprintCall(); // Blueprint & C++ 
	
	UFUNCTION()
	virtual void CommonCall() = 0;// C++ Only
};

在我们新建一个接口的时候,会生成2个部分的代码类,其实UE中的接口是使用类在模拟的,并不是我们传统编程语言
里面的接口,也许是为了统一管理吧,效率考虑
第一部分UExampleInterface,上面有提示This class does not need to be modified.告诉我们不能修改这个类,这个类
是反射的基础构建,C++是强类型语言,约束接口的类型,可以通过这个类型来查找的
第二部分IExampleInterface,以I开头表示这是一个接口(Interface),并且有提示告诉我们添加接口方法到这个类,
这个类是被继承来实现这些接口方法
UFUNCTION()
virtual void CommonCall() = 0;// C++ Only
virtual加上方法后面=0表示这是一个纯虚方法,纯虚方法是不需要方法实现的,在子类中必须重写实现
虚函数表就是指这种方法的专业术语
这里面定义的是一个只能在C++层使用的接口,并且必须这样写

UFUNCTION(BlueprintNativeEvent)
void BlueprintCall(); // Blueprint & C++
这是一个普通的蓝图通信方法定义,使用的BlueprintNativeEvent来标记,这个方法可以在C++及蓝图都可以被调用
相关资料见:#. UE中蓝图和C++相互调用
但这个方法有点特殊,他在接口中定义的时候是不需要添加后缀_Implementation并且可以通过编译器的编译

#2-2. 实现C++定义的接口

UCLASS()
class MONTAGETEST_API AExampleInterfaceImplActor : public AActor, public IExampleInterface
{
	GENERATED_BODY()
public:
	virtual void CommonCall() override;

	UFUNCTION(BlueprintNativeEvent)
    void BlueprintCall();
	virtual void BlueprintCall_Implementation() override;
};
#include "ExampleInterfaceImplActor.h"

void AExampleInterfaceImplActor::CommonCall()
{
	GLog->Logf(TEXT("I am common method call from interface"));
}

void AExampleInterfaceImplActor::BlueprintCall_Implementation()
{
	GLog->Logf(TEXT("I am BlueprintCall_Implementation from interface"));
}

virtual void CommonCall() override;和通用C++语言语法一致,重写实现父类中(模拟接口)中的纯虚方法

UFUNCTION(BlueprintNativeEvent)
void BlueprintCall();
virtual void BlueprintCall_Implementation() override;
如果需要把接口应用到C++和蓝图中,需要这样写,见接口中定义的方法包括标记复制过来
然后重写接口中的BlueprintCall_Implementation()UE反射代码生成的方法,也是和蓝图通信的方法签名一样的

// UFUNCTION(BlueprintNativeEvent)
// void BlueprintCall();
virtual void BlueprintCall_Implementation() override;

刚才测试注释上面的代码也可以正常,之前不可以估计UE抽风了,最好全部写上,标准写法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在蓝图中继承我们定义的C++类,可以看到我们把接口也继承过来了,这个时候UE会自动识别蓝图接口

在这里插入图片描述
需要调用父类的Parent:Blueprint Call蓝图节点,在接口方法上右击可以找到实现,默认情况下,接口会被蓝图覆盖,
如果C++层也需要执行,需要执行一次父类调用

#2-3. C++调用接口

void AExampleInterfaceCallActor::BeginPlay()
{
	Super::BeginPlay();
	TArray<AActor*> AllActors;
	UGameplayStatics::GetAllActorsWithInterface(this, UExampleInterface::StaticClass(), AllActors);

	if (AllActors[0]->GetClass()->ImplementsInterface(UExampleInterface::StaticClass()))
	{
		IExampleInterface* ExampleInterface = Cast<IExampleInterface>(AllActors[0]);
		ExampleInterface->CommonCall();
		ExampleInterface->Execute_BlueprintCall(AllActors[0]);
		//IExampleInterface::Execute_BlueprintCall(AllActors[0]);
	}
	// ExampleInterface->BlueprintCall();
}

非常简单粗暴好用的测试代码,我把需要测试的Actor全部都已经放到了场景中
TArray<AActor*> AllActors;
UGameplayStatics::GetAllActorsWithInterface(this, UExampleInterface::StaticClass(), AllActors);
将场景中,也就是代码GetWorld()实现了UExampleInterface的Actor全部找出来,我的场景中只有一个蓝图Actor实现了这个接口
在编译器层面来说,这个接口是有类型的,使用UExampleInterface这个来约束,可以通过静态方法取到UClass类型
AllActors[0]->GetClass()->ImplementsInterface(UExampleInterface::StaticClass())安全编码

IExampleInterface* ExampleInterface = Cast<IExampleInterface>(AllActors[0]);
因为接口是假的,使用类来模拟的,当然可以Cast了,子类转父类,基本对象语言法则

ExampleInterface->CommonCall();遵循C++语言的接口方法可以直接调用
如果是调用多功能接口方法,比如一个需要调用到蓝图,并且C++层也需要的话,不能直接调用,直接调用程序会崩溃

#1. 通过反射来调用(不推荐,我用的时候卡了一下,我以为UE崩溃了)
IExampleInterface::Execute_BlueprintCall(AllActors[0]);
使用反射生成的静态方法来直接调用,好处是到处都可以调用,有点类似蓝图,方便省事
#2. 通过反射生成方法来调用
ExampleInterface->Execute_BlueprintCall(AllActors[0]);
好处是效率高,编码统一,这个方法是反射生成的,加了前缀Execute_
无论使用哪一种方式,我们都需要指定一个Target,执行目标接口的方法=执行目标的方法


经验总结
有时候,我们的内容不全部C++层的,这个时候如果从C++层调用到蓝图,那么怎么桥接过去,可以使用
接口,接口有桥接C++和蓝图的功能
比如:动画蓝图我使用的纯蓝图制作(好像动画蓝图只能蓝图的,因为有连接端子,并且蓝图层也不支持自定义;
引擎都会有一些非常底层的功能,不允许用户自定义的,比如Unity的Transform也不能被继承重写)
这个时候我需要在C++层调用到动画蓝图,这个时候不能直接通过对象来调用接口的方法,
因为C++和蓝图并不是统一作用域,使用对象的方式来调用接口的方法,根本获取不到这个对象

在这里插入图片描述

UAnimInstance* TargetAnimBP = MeshComp->GetAnimInstance();
if (TargetAnimBP->GetClass()->ImplementsInterface(UCombo_Interface::StaticClass()))//OK
{
	//ICombo_Interface* Interface = Cast<ICombo_Interface>(TargetAnimBP);//NULL
	//Interface->SetNextComboSection(NextSectionName);					 //Crash
	ICombo_Interface::Execute_SetNextComboSection(TargetAnimBP, NextSectionName);//Best
}

动画蓝图并不是从C++继承过去的,可以看到并没有继承接口,这个动画蓝图实现了我们C++层定义的接口,
UAnimInstance* TargetAnimBP = MeshComp->GetAnimInstance();自定义动画蓝图可以这样获取,比较特殊的

TargetAnimBP->GetClass()->ImplementsInterface(UCombo_Interface::StaticClass())
可以通过,说明C++层的接口可以被正确的识别

ICombo_Interface* Interface = Cast<ICombo_Interface>(TargetAnimBP);//NULL
Interface->SetNextComboSection(NextSectionName); //Crash
这里不行的,因为C++和蓝图并不是同一作用域的,只有纯C++层的接口才可以使用对象来调用

ICombo_Interface::Execute_SetNextComboSection(TargetAnimBP, NextSectionName);//Best
但使用反射生成的全局静态方法可以正确执行,静态的接口方法作用全局,对象作用C++层


  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值