UE4中的C++接口使用教程

发表于2017-04-22
评论2 8k浏览
从Unreal Engine 官方论坛上,看到了这篇关于C++接口方面的文章,感觉大家会在以后的UE4开发中会用到,为了让大家可以更熟练的去使用C++接口,下面就给大家介绍下关于在UE4 4.11+中C++ 使用接口的教程。

接口是不同的对象中拥有相同的函数,一般情况下,这些对象中的相同函数具有不同的功能。任何使用接口的类都必须实现这些接口。

这让你在写游戏中Actor的代码时更加的灵活方便。当你在C++和蓝图里可以触发事件,不同的Actor采用不同的方法来处理它们。

例如,本教程中实现像TimeBasedBehaviour这种类型的接口,它有一个ReactToHighNoon函数,并且有一堆Actor以不同的方式来响应这个函数。

又比如,继承自接口的Flower Actor覆写了其中的ReactToHighNoon方法,然后你可以创建一个事件比如SunReachedHighNon ,可以在任何一个可以获得Actor的地方触发这个事件(如关卡蓝图,Actor或静态蓝图库),检查Actor是否继承自接口,如果是继承自你需要的接口就可以调用该接口类中的所有函数,Actor将根据具体的实现和定义来执行不同的行为。

只要你有一个指向该对象的指针,调用时不需要知道它的具体类型。只要能成功将类型转换成接口类型的对象,你就可以调用此接口类定义下的接口函数了。

我们可以定义两种接口函数:一个是需要有c++默认实现的接口,标记为BlueprintNativeEvent(下面例子中的ReactToHighNoon()函数),以及一个不需要有默认实现的Blueprint Implement able Event(下面例子中的ReactToMidnight()函数)。

创建接口

以下是ReactsToTimeOfDay接口创建示例。
(如果希望将该接口当做事件,返回类型必须是void。如果你希望该函数能够在蓝图编辑器中被覆写,则必须具有非void的返回类型。)

Reacts To Time Of Day. h
01// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
02  
03#pragma once
04  
05#include "ReactsToTimeOfDay.generated.h"
06  
07/* must have BlueprintType as a specifier to have this interface exposed to blueprints
08   with this line you can easily add this interface to any blueprint class */
09UINTERFACE(BlueprintType)
10class MYPROJECT_API UReactsToTimeOfDay : public UInterface
11{
12    GENERATED_UINTERFACE_BODY()
13};
14  
15class MYPROJECT_API IReactsToTimeOfDay
16{
17    GENERATED_IINTERFACE_BODY()
18  
19public:
20    //classes using this interface must implement ReactToHighNoon
21    UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MyCategory")
22        bool ReactToHighNoon();
23  
24    //classes using this interface may implement ReactToMidnight
25    UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "MyCategory")
26        bool ReactToMidnight();
27  
28};

ReactsToTimeOfDay.cpp
01// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
02  
03#include "MyProject.h"
04#include "ReactsToTimeOfDay.h"
05  
06UReactsToTimeOfDay::UReactsToTimeOfDay(const class FObjectInitializer& ObjectInitializer)
07    : Super(ObjectInitializer)
08{
09  
10}

使用C++类中的接口
你需要使用多重继承,继承自我们创建的IReactsToTimeOfDay类。
第一个继承的类是你的基类,此处使用一个ASkeletalMeshActor作为示例。

Flower.h
01//..other includes may appeAR here depending on your class
02#include "ReactsToTimeOfDay.h"
03#include "ASkeletalMeshActor.generated.h"
04  
05UCLASS()
06class AFlower : public ASkeletalMeshActor,  public IReactsToTimeOfDay
07{
08    GENERATED_BODY()
09  
10public:
11    /*
12    ... other AFlower properties and functions declared ...
13    */
14  
15    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "MyCategory")
16        bool ReactToHighNoon();
17        virtual bool ReactToHighNoon_Implementation() override;
18  
19  
20};

  virtual bool ReactToHighNoon_Implementation()override; 
这一行说明这个函数是从接口继承的。

UFUNCTION(BlueprintCallable,BlueprintNativeEvent,Category =“MyCategory”)
bool ReactToHighNoon();
说明你的类可以在蓝图中调用和覆写这个函数。BlueprintNativeEvents标记说明在蓝图中可以覆写这个C++函数。

注意:这里没有定义标记为BlueprintImplementableEvent的函数ReactToMidnight。 BlueprintImplementableEvent只需在我们的接口中声明,蓝图来具体实现他的功能。

Flower.cpp
01//other flower.cpp code
02  
03bool AFlower::ReactToHighNoon_Implementation()
04{
05    //Default behaviour for how flower would react at noon
06    //OpenPetals();
07    //AcceptBugs();
08    //...
09  
10    return true;
11  
12}

可以这样写来实现此接口

Frog.h
01//..other includes may appear here depending on your class
02#include "ReactsToTimeOfDay.h"
03#include "AFrog.generated.h"
04  
05UCLASS()
06class AFrog : public ACharacter,  public IReactsToTimeOfDay
07{
08    GENERATED_BODY()
09    /*
10    ... other AFrog properties and functions declared ...
11    */
12  
13    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "MyCategory")
14        bool ReactToHighNoon();
15        virtual bool ReactToHighNoon_Implementation() override;
16  
17};

Frog.cpp
01//other Frog code
02  
03bool AFrog::ReactToHighNoon_Implementation()
04{
05    //Default behaviour for how a frog would react at noon
06    //GoSwim();
07    //...
08  
09    return true;
10}

确定一个Actor是否实现了想要的接口

要确定一个Actor是否在C++或蓝图中实现你想要的接口,只需将该对象转换为接口类的对象,如果返回NULL,则该对象并不具备你想要的接口。如果成功了,就可以使用转换过的指针调用接口函数了。
01//Example: somewhere else in code we are trying to see if our object reacts to time of day
02  
03//Some pointer is defined to any class inheriting from UObject
04UObject* pointerToAnyUObject;
05  
06//....
07  
08  
09    IReactsToTimeOfDay* TheInterface = Cast(pointerToAnyUObject);
10    if (TheInterface)
11    {
12        //Don't call your functions directly, use the 'Execute_' prefix
13        //the Execute_ReactToHighNoon and Execute_ReactToMidnight are generated on compile
14        //you may need to compile before these functions will appear
15        TheInterface->Execute_ReactToHighNoon (pointerToAnyUObject);
16        TheInterface->Execute_ReactToMidnight (pointerToAnyUObject);
17    }
18  
19 

注意事项;
1.调用C++中的接口函数时,永远不要直接调用函数,使用带有Execute前缀的函数进行调用
2.你的函数必须具有返回值。如果你的函数返回void类型,UE4会将它当做一个事件,导致你不能覆写它(它不会出现在蓝图中的可覆写函数列表中)。如果你不需要一个返回值,就可以像上面的那样返回一个无用值。

Madgic接口

TheInterface-> Execute_ReactToHighNoon()
从上面的代码可以看出,这个函数是调用接口的,你甚至不需要知道对象的类型,只需要知道它是否支持你需要的接口。
根据实际的对象调用覆写后的函数产生不同的结果。这就是多态性。

在蓝图中覆写接口函数

按照上面说的做完后,C++中的接口函数将出现在蓝图里。

同样,接口函数必须具有返回值才能显示在此列表中,否则将被当做事件,不能被覆写。

你可以触发只有某些对象才会响应的全局事件,每个Actor都可以以自己独特的方式对事件做出回应。
虽然这个听起来比较复杂,但是它让代码在设计上更加简单,并且比使用不同的类具有更好性能!

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引