概述

  • 简单的说,接口提供一组公共的方法,不同的对象中继承这些方法后可以有不同的具体实现。
  • 任何使用接口的类都必须实现这些接口。
  • 实现解耦
  • 解决多继承的问题

蓝图使用

使用方法

三种调用方法的区别

调用流关卡蓝图的接口函数

C++ 使用接口

本例使用一个Box Trigger 出发overlap 调用 Drone实例的接口

添加接口类

定义接口

声明蓝图可调用接口函数

  • 用UFUNCTION 宏 BlueprintCallable 声明蓝图可调用,还必须使用 BlueprintImplementableEventBlueprintNativeEvent 说明,而且函数不能为虚函数

  • 如果不想蓝图重载,只是想使用 BlueprintCallable 以支持蓝图起到单纯的调用作用,可以通过将接口标记为 UINTERFACE(meta = (CannotImplementInterfaceInBlueprint)) 来解决

  • 通过声明 virtual 虚函数,使得派生类可重载

代码

  • ReactToTriggerInterface.h

    #pragma once
    #include "CoreMinimal.h"
    #include "UObject/Interface.h"
    #include "ReactToTriggerInterface.generated.h" // 无需更改
    // UINTERFACE类不是实际的接口;它是一个空白类,它的存在只是为了向虚幻引擎反射系统确保可见性。
    UINTERFACE(MinimalAPI)
    class UReactToTriggerInterface : public UInterface
    {
    GENERATED_BODY()
    }; //开头字母"U"必须改为"I"。
    class TIPS_API IReactToTriggerInterface
    {
    GENERATED_BODY() public: // 纯虚函数,实现类必须实现接口
    virtual void ReactToTrigger_PureVirtual() = 0; // 虚函数,在接口本身的 .h 或 .cpp 文件中提供默认实现.实现类可覆盖
    virtual void ReactToTrigger_Virtual(); //实现类可以在蓝图和C++中实现接口
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Trigger Reaction")
    void ReactToTrigger_NativeEvent1(int32 number); //实现类可以在蓝图和C++中实现接口
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Trigger Reaction")
    bool ReactToTrigger_NativeEvent2(int32 number); //实现类在蓝图中实现接口
    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "Trigger Reaction")
    void ReactToTrigger_ImplementableEvent();
    };
  • ReactToTriggerInterface.cpp

    #include "ReactToTriggerInterface.h"
    void IReactToTriggerInterface::ReactToTrigger_Virtual()
    {
    // unimplemented(); //该宏将在执行代码行时发出调试语句, 中断
    UE_LOG(LogTemp, Warning, TEXT("ReactToTrigger_Virtual 被调用, From 接口本身"));
    }

实现接口

  • MyDrone.h 此类继承自Pawn类,无关本文主题

    #include "ReactToTriggerInterface.h"
    #include "MyDrone.generated.h" UCLASS()
    class TIPS_API AMyDrone : public ADrone, public IReactToTriggerInterface
    {
    GENERATED_BODY() public:
    virtual void ReactToTrigger_PureVirtual() override; //可蓝图调用,貌似通用写法
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Trigger Reaction")
    void ReactToTrigger_NativeEvent1(int32 number);
    virtual void ReactToTrigger_NativeEvent1_Implementation(int32 number) override; //蓝图可调用,,貌似和上面没区别
    virtual bool ReactToTrigger_NativeEvent2_Implementation(int32 number) override; //void ReactToTrigger_ImplementableEvent();需要在蓝图实现
    };
  • MyDrone.cpp

    #include "MyDrone.h"
    
    void AMyDrone::ReactToTrigger_PureVirtual()
    {
    UE_LOG(LogTemp, Warning, TEXT("ReactToTrigger_PureVirtual 被调用, From MyDrone"));
    } void AMyDrone::ReactToTrigger_NativeEvent1_Implementation(int32 number)
    {
    UE_LOG(LogTemp, Warning, TEXT("ReactToTrigger_NativeEvent1 被调用, From MyDrone"));
    } bool AMyDrone::ReactToTrigger_NativeEvent2_Implementation(int32 number)
    {
    UE_LOG(LogTemp, Warning, TEXT("ReactToTrigger_NativeEvent2 被调用, From MyDrone"));
    return true;
    }

  • 也可以蓝图重载

调用接口的类

  • c++ 调用接口可以先判断该类是否有实现接口

    // 如果OriginalObject实现了UReactToTriggerInterface,则bisimplemated将为true。
    bool bIsImplemented = OriginalObject->GetClass()->ImplementsInterface(UReactToTriggerInterface::StaticClass()); // 如果OriginalObject实现了UReactToTrigger,bIsImplemented将为true。
    bIsImplemented = OriginalObject->Implements<UReactToTriggerInterface>(); // 如果OriginalObject实现了UReactToTriggerInterface,则ReactingObject将为非空。
    IReactToTriggerInterface* ReactingObject = Cast<IReactToTriggerInterface>(OriginalObject);
  • 原生 虚函数调用按照原生C++调用即可

  • UFUCNTION()修饰的接口函数则以 I接口名::Execute_函数名( 接口实例, 函数参数) 调用

代码

  • MyTriggerBox.h

    #pragma once
    #include "CoreMinimal.h"
    #include "Engine/TriggerBox.h"
    #include "MyTriggerBox.generated.h" UCLASS()
    class TIPS_API AMyTriggerBox : public ATriggerBox
    {
    GENERATED_BODY() public: virtual void BeginPlay() override; UFUNCTION()
    void HandleOverlap(AActor* OverlappedActor, AActor* OtherActor );
    };
  • MyTriggerBox.cpp

    #include "MyTriggerBox.h"
    #include "Components/BoxComponent.h"
    #include "ReactToTriggerInterface.h" void AMyTriggerBox::BeginPlay()
    {
    //放在构造函数好像不起作用
    OnActorBeginOverlap.AddDynamic(this, &AMyTriggerBox::HandleOverlap);
    } void AMyTriggerBox::HandleOverlap(AActor* OverlappedActor, AActor* OtherActor )
    {
    UClass* ActorClass = OtherActor->GetClass();
    if (ActorClass->ImplementsInterface(UReactToTriggerInterface::StaticClass()))
    {
    UE_LOG(LogTemp, Warning, TEXT("是否实现接口判断方法一"));
    IReactToTriggerInterface* ReactToTriggerInterface1 = CastChecked<IReactToTriggerInterface>(OtherActor); ReactToTriggerInterface1->ReactToTrigger_PureVirtual();
    ReactToTriggerInterface1->ReactToTrigger_Virtual(); IReactToTriggerInterface::Execute_ReactToTrigger_NativeEvent1(OtherActor, 16);
    IReactToTriggerInterface::Execute_ReactToTrigger_NativeEvent2(OtherActor, 32);
    IReactToTriggerInterface::Execute_ReactToTrigger_ImplementableEvent(OtherActor); } if (OtherActor->Implements<UReactToTriggerInterface>())
    {
    UE_LOG(LogTemp, Warning, TEXT("是否实现接口判断方法二")); IReactToTriggerInterface::Execute_ReactToTrigger_NativeEvent1(OtherActor, 16);
    IReactToTriggerInterface::Execute_ReactToTrigger_NativeEvent2(OtherActor, 32);
    } IReactToTriggerInterface* ReactToTriggerInterface2 = Cast<IReactToTriggerInterface>(OtherActor);
    if (ReactToTriggerInterface2)
    {
    UE_LOG(LogTemp, Warning, TEXT("是否实现接口判断方法三"));
    IReactToTriggerInterface::Execute_ReactToTrigger_ImplementableEvent(OtherActor);
    } }

测试结果

参考

【UE4 C++ 基础知识】<9> Interface 接口的更多相关文章

  1. 【UE4 C++ 基础知识】<11>资源的同步加载与异步加载

    同步加载 同步加载会造成进程阻塞. FObjectFinder / FClassFinder 在构造函数加载 ConstructorHelpers::FObjectFinder Constructor ...

  2. 【UE4 C++ 基础知识】<12> 多线程——FRunnable

    概述 UE4里,提供的多线程的方法: 继承 FRunnable 接口创建单个线程 创建 AsyncTask 调用线程池里面空闲的线程 通过 TaskGraph 系统来异步完成一些自定义任务 支持原生的 ...

  3. 【UE4 C++ 基础知识】<3> 基本数据类型、字符串处理及转换

    基本数据类型 TCHAR TCHAR就是UE4通过对char和wchar_t的封装 char ANSI编码 wchar_t 宽字符的Unicode编码 使用 TEXT() 宏包裹作为字面值 TCHAR ...

  4. Java基础知识:Collection接口

    *本文是最近学习到的知识的记录以及分享,算不上原创. *参考文献见文末. 这篇文章主要讲的是java的Collection接口派生的两个子接口List和Set. 目录 Collection框架 Lis ...

  5. 【UE4 C++ 基础知识】<8> Delegate 委托

    概念 定义 UE4中的delegate(委托)常用于解耦不同对象之间的关联:委托的触发者不与监听者有直接关联,两者通过委托对象间接地建立联系. 监听者通过将响应函数绑定到委托上,使得委托触发时立即收到 ...

  6. java基础知识回顾之接口

    /* abstract class AbsDemo { abstract void show1(); abstract void show2(); } 当一个抽象类中的方法都是抽象的时候,这时可以将该 ...

  7. 【UE4 C++ 基础知识】<14> 多线程——AsyncTask

    概念 AsyncTask AsyncTask 系统是一套基于线程池的异步任务处理系统.每创建一个AsyncTas,都会被加入到线程池中进行执行 AsyncTask 泛指 FAsyncTask 和 FA ...

  8. 《Java基础知识》Java接口和抽象类的区别

    抽象类 抽象类必须用 abstract 修饰,子类必须实现抽象类中的抽象方法,如果有未实现的,那么子类也必须用 abstract 修饰.抽象类默认的权限修饰符为 public,可以定义为 public ...

  9. JAVA基础知识|抽象类与接口类

    一.抽象类 抽象类:拥有抽象方法的类就是抽象类,抽象类要使用abstract声明 抽象方法:没有方法体的方法,必须要使用abstract修饰 为什么要使用抽象类,抽象方法? 举例来说,如果你定义了一个 ...

随机推荐

  1. Linux下用Sed查找IP地址

    ip addr|sed -n '9p'|egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'|sed -nr 's#^.*inet (.*) b ...

  2. 解决CSDN文章下载后,依然请求跳转至首页出错的问题

    1. 搜索关键字:"onerror"     然后找到如下所示代码: <div style="display:none;">     <img ...

  3. Identity用户管理入门二(显示用户列表)

    在Controllers中新建AccountController,并在构造方法(函数)中注入SignInManager,UserManager UserManager   用户管理(注册,查找,修改, ...

  4. IDEA SpotBugs代码安全审计插件

    IDEA SpotBugs代码安全审计插件 在寻找idea代码审计插件的时候,发现Findbugs已经停止更新,无法在idea2020.01版本运行,由此找到SpotBugs SpotBugs介绍 S ...

  5. 1.docker概述及其历史

    一. 为什么会出现docker? 不用说, 肯定是时代进步的产物. 那么, 他为什么能火? 一定是解决了痛点问题. docker也不是一下子就火起来了, 他的火也是有一个过程的, 我们先来看看为什么会 ...

  6. golang中的左值VS右值

    对应关系 左值 可寻址 右值 不可寻址 可寻址:可以通过&取地址符,获取内存地址; 可寻址,也就是分配了内存; 不可寻址:根本没有分配内存; 常量const 常量通常只支持数字/字符串/布尔, ...

  7. Linux中不用用户可以使用相同的uid

    usermod -u 513 -o  tom 使得用户tom可以使用uid等于513,即使513已经被其他用户使用了

  8. k8s核心资源之namespace与pod污点容忍度生命周期进阶篇(四)

    目录 1.命名空间namespace 1.1 什么是命名空间? 1.2 namespace应用场景 1.3 namespacs常用指令 1.4 namespace资源限额 2.标签 2.1 什么是标签 ...

  9. 一文带你了解.Net读写锁

    本文主要讲解.Net基于ReaderWriterLockSlim讲解读写锁 基础概念 读写锁是一个具有特殊用途的线程锁,适用于频繁读取且读取需要一定时间的场景,共享资源的读取操作通常是可以同时执行的, ...

  10. HDU1166敌兵布阵(线段树单点更新)

    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点.       对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b ...