• UEC++ 接口


    词义广泛,用来陈述功能,选项,与其他程序结构进行沟通的方式。接口抽象出了交互结构,提供了两个未知逻辑交互的便捷性。对于编程中,如何更好的设计低耦合程序起到了至关重要的作用。设计者可以在互不关心的情况下,进行友好的程序设计,并且通过接口来完成设计的整合交互。

    虚幻引擎中,加入了接口设计,从一定程度上“去掉了”多继承。接口可以帮助我们解决在不同类型的类之间却有相同行为的特性。接口的设计增加了代码编写的便捷性。

    例如在设计射击类游戏时,我们需要子弹与场景中的物体进行交互,场景中的桌椅板凳,角色,怪物(都是独立的对象)都希望受到子弹的攻击伤害。那么子弹在打到目标后要逐一排查,审查目标是否属于上述的对象!这很麻烦!但是我们可以通过接口,增加上述目标具有受伤的能力。当子弹打到目标时,我只需要检查目标是否继承受伤的接口,如果有,则调用接口函数即可!

    构建接口类:

    我们可以直接在虚幻编辑器中继承接口类,然后完成构建

    编写接口:

    • 如果在C++中希望获得接口能力,则需要继承接口。需要注意的是,必须继承I开头的接口名称,并且继承修饰为public。不要尝试重写接口中的函数!
    • 如果接口中的函数使用BlueprintNativeEvent说明,则在继承类中可以编写同名函数,并用后缀“_Implementation”进行标记。
    • 如果接口中的函数使用BlueprintImplementableEvent说明,则无法在C++的继承类中实现接口函数

    实现接口:

    继承I类接口完毕后,可以选择的将接口中的函数进行定义。如果需要定义,则需要将接口中函数说明是BlueprintNativeEvent的函数进行定义。

    注意,不要省略override,函数的返回值,参数列表需要和接口的一致

    调用操作:

    调用函数,持有继承接口对象指针,第一步先转换到I类指针,调用Execute_接口函数名,参数第一位需要传递原对象指针,后面直接按照原函数参数填入即可

    整体代码演示:

    复制代码
    //  TestInterface.h
    UINTERFACE(MinimalAPI)
    class UTestInterface : public UInterface
    {
        GENERATED_BODY()
    };
    
    /**
     * 1、U类中不能去写接口函数,只能用来检查是否继承了接口类
     * 2、接口函数,必须写在I类中,并且必须写共有域中
     * 3、接口函数在接口类中不能进行定义
     * 
     */
    class MX_API ITestInterface
    {
        GENERATED_BODY()
    
        // Add interface functions to this class. This is the class that will be inherited to implement this interface.
    public:
        // 定义接口函数
        UFUNCTION(BlueprintNativeEvent)
        void Notify_None();
        UFUNCTION(BlueprintNativeEvent)
        int32 Notify_RetVal();
        UFUNCTION(BlueprintNativeEvent)
        int32 Notify_RetVal_Params(int32 Num);
    };
    
    /////////////////////////////////////////////////////////////
    // Actor2.h
    public:    
        // Called every frame
        virtual void Tick(float DeltaTime) override;
    
        // 实现接口
        virtual void Notify_None_Implementation() override;
        virtual int32 Notify_RetVal_Implementation() override;
        virtual int32 Notify_RetVal_Params_Implementation(int32 Num) override;
    
    // Actor2.cpp
    void AActor2::Notify_None_Implementation()
    {
        UKismetSystemLibrary::PrintString(this, TEXT("----无参无返回值----"));
    }
    
    int32 AActor2::Notify_RetVal_Implementation()
    {
        UKismetSystemLibrary::PrintString(this, TEXT("----无参有返回值----"));
        return 0;
    }
    
    int32 AActor2::Notify_RetVal_Params_Implementation(int32 Num)
    {
        UKismetSystemLibrary::PrintString(this, TEXT("----有参有返回值----"));
        return Num;
    }void AActor2::Notify_None_Implementation()
    {
        UKismetSystemLibrary::PrintString(this, TEXT("----无参无返回值----"));
    }
    
    int32 AActor2::Notify_RetVal_Implementation()
    {
        UKismetSystemLibrary::PrintString(this, TEXT("----无参有返回值----"));
        return 0;
    }
    
    int32 AActor2::Notify_RetVal_Params_Implementation(int32 Num)
    {
        UKismetSystemLibrary::PrintString(this, TEXT("----有参有返回值----"));
        return Num;
    }
    /////////////////////////////////////////////////////////
    //Actor1.cpp
        ac2 = GetWorld()->SpawnActor(AActor2::StaticClass());
        // 检查是否继承了接口
        ITestInterface* testInterface = Cast(ac2);
        // 如果继承了接口,就执行接口函数
        if (testInterface) {
            testInterface->Execute_Notify_None(ac2);
            testInterface->Execute_Notify_RetVal(ac2);
            testInterface->Execute_Notify_RetVal_Params(ac2,10);
        }
    复制代码

    测试结果:

     

     

     包裹接口:

    借助模板类TScriptInterface可以将接口包裹,用于使用UPROPERTY描述,并且可以暴露到蓝图中。使用时同普通接口一样使用。接口不支持智能指针的管理,所以需要使用TS类进行管理

        UPROPERTY(EditAnywhere)
            TScriptInterface TestInterface;

    蓝图继承接口:

    如果接口在蓝图中被继承,则需要注意下面的问题

    • 如果函数没有返回类型,则在蓝图中当作事件Event使用
    • 如果函数存在返回类型或是存在传递引用参数,则在蓝图中当作函数使用
    • 接口函数说明符使用BlueprintNativeEvent或是BlueprintImplementableEvent标记都可以在蓝图中找到

    总结:

    • 接口函数需要定在I开头的类中,不要修改访问域public关键字,声明需要使用宏标记BlueprintNativeEvent或BlueprintImplementableEvent
    • 如需继承接口,继承I类,继承关系public
    • 接口中的函数禁止重写
    • 在继承类中实现接口函数,并添加后缀_Implementation,需要注意,函数前加入虚函数关键字virtual,函数结尾加override关键字(可以不添加,但是建议加上,加强函数编写正确性检查),在CPP文件中实现逻辑
    • 调用函数,持有继承接口对象指针,第一步先转换到I类指针,调用Execute_接口函数名,参数第一位需要传递原对象指针,后面直接按照原函数参数填入即可
    • 检查某一个类是否实现了对应接口可以使用如下语法进行检查
      • obj->GetClass()->ImplementsInterface(U类型::StaticClass());
      • act->GetClass()->ImplementsInterface(UMyInterface::StaticClass());
      • act是对象指指针

    接口的优缺点:

    优点:

    • 具备多态特性,接口衍生类支持里氏转换原则
    • 接口可以使得整个继承系统更加的干净单一
    • 接口可以规范类的具体行为
    • 接口可以隔离开发中的开发耦合,我们只需要针对接口去编码,无需关心具体行为
    • 接口继承可以使得继承关系中出现真正的操作父类

    缺点:

    • 丢失了C++中的广泛继承特性
    • 接口拘束了类型的属性拓展,无法进行更详细的内容定义
    • 继承关系中容易让人混淆,接口本身不具备真正的继承特性
  • 相关阅读:
    Ansible
    SpringBoot2.4框架
    SpringCloud - Spring Cloud Netflix 之 Hystrix Dashboard仪表盘监控(九)
    R语言数据预处理:使用dplyr包进行数据预处理、使用mutate函数、factor函数把数值型变量转化为因子类型变量并使用labels函数指定因子标签
    【计算机毕设之基于Java的贫困生资助管理系统-哔哩哔哩】 https://b23.tv/LrumkKI
    一种可信万兆加密分流认证装置研究
    三.镜头知识之FOV
    Web系统常见安全漏洞介绍及解决方案-CSRF攻击
    数据准备之日志采集发展历程
    【数字IC前端入门】Questasim使用
  • 原文地址:https://www.cnblogs.com/limu-zy/p/16721699.html