Defining a C++ Interface¶
You can register a C++ interface with the engine by using the UINTERFACE
macro. A registered interface is recognized by the editor and can be used to enforce structure to common functionality even through Blueprint classes.
You can create a new interface through the editor by selecting Tools > New C++ Class... from the main menu and then picking Unreal Interface. You could also manually create a new header file in either the Public or Private directory of your module and define the interface there.
Example¶
The following is an example of how to define a simple interface in C++ for interactable objects in the game world. The declaration looks slightly different depending on whether you want to expose the interface to Blueprint classes or not.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"
UINTERFACE(MinimalAPI/* (1)! */)
class UInteractable/* (2)! */ : public UInterface {
GENERATED_BODY()
};
class MODULENAME_API IInteractable/* (3)! */ {
GENERATED_BODY()
public:
virtual void Interact(); // Could also have gone with pure virtual.
};
- The class only needs to exist so that the editor can recognize the interface. There is no point in exposing it to other modules. See external linkage for more info.
- This class purely exists so that the editor can recognize the interface. It does not need to have any members or methods.
- The actual interface is defined here. Per naming convention, the interface class should be prefixed with an
I
.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"
UINTERFACE(MinimalAPI/* (1)! */, Blueprintable)
class UInteractable/* (2)! */ : public UInterface {
GENERATED_BODY()
};
class MODULENAME_API IInteractable/* (3)! */ {
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent/* (4)! */, BlueprintCallable/* (5)! */)
void Interact();
};
- The class only needs to exist so that the editor can recognize the interface. There is no point in exposing it to other modules. See external linkage for more info.
- This class purely exists so that the editor can recognize the interface. It does not need to have any members or methods.
- The actual interface is defined here. Per naming convention, the interface class should be prefixed with an
I
. - Allows us to override the method in both C++ and Blueprints. See
BlueprintNativeEvent
for more info. - Makes little sense to expose the interface to Blueprint classes if the method is not
BlueprintCallable
.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"
UINTERFACE(MinimalAPI/* (1)! */, Blueprintable)
class UInteractable/* (2)! */ : public UInterface {
GENERATED_BODY()
};
class MODULENAME_API IInteractable/* (3)! */ {
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent/* (4)! */, BlueprintCallable/* (5)! */)
void Interact();
};
- The class only needs to exist so that the editor can recognize the interface. There is no point in exposing it to other modules. See external linkage for more info.
- This class purely exists so that the editor can recognize the interface. It does not need to have any members or methods.
- The actual interface is defined here. Per naming convention, the interface class should be prefixed with an
I
. - The method can only be implemented in Blueprints. See
BlueprintImplementableEvent
for more info. - Makes little sense to expose the interface to Blueprint classes if the method is not
BlueprintCallable
.
Now, we can have any class implement the interface. For example, a Door
class could implement the Interactable
interface.
UCLASS()
class MODULENAME_API ADoor : public AActor, public IInteractable/* (1)! */ {
GENERATED_BODY()
public:
ADoor();
virtual void Interact() override;
};
- Note that you must inherit from
IInteractable
, notUInteractable
.
UCLASS()
class MODULENAME_API ADoor : public AActor, public IInteractable/* (1)! */ {
GENERATED_BODY()
public:
ADoor();
virtual void Interact_Implementation/* (2)! */() override;
};
- Note that you must inherit from
IInteractable
, notUInteractable
. - The
_Implementation
suffix is added due to the use ofBlueprintNativeEvent
specifier.
Finally, other actors (e.g., a pawn) can check if an object is interactable and call the Interact
method if it is.
void AMyPawnActor::MaybeInteract(AActor* OtherActor) {
if (auto Interface = Cast<IInteractable>(OtherActor)) {
Interface->Interact();
}
}
void AMyPawnActor::MaybeInteract(AActor* OtherActor) {
if (OtherActor->Implements/* (1)! */<UInteractable>()) {
IInteractable::Execute_Interact(OtherActor);
}
}
- Courtesy of the
UObject
base class, the method returnstrue
if the object implements the specified interface.
void AMyPawnActor::MaybeInteract(AActor* OtherActor) {
if (OtherActor->Implements/* (1)! */<UInteractable>()) {
IInteractable::Execute_Interact(OtherActor);
}
}
- Courtesy of the
UObject
base class, the method returnstrue
if the object implements the specified interface.
UNITERFACE
¶
Specifiers¶
UINTERFACE(Blueprintable)
Exposes the interface so that it can be implemented by a Blueprint class.
UINTERFACE(BlueprintType)
Exposes this class as a type that can be used for variables in Blueprints.
UINTERFACE(Category="Some Category")
Associates a category to the interface.
Categories can be nested by separating them with a pipe (|
) without spaces, e.g., Main Category|Subcategory
.
UINTERFACE(MinimalAPI)
Indicates that only the class's type information to be exported. See External Linkage for more info.
Meta Specifiers¶
UINTERFACE(meta=(DisplayName="Some Name"))
Specifies an alternate name for the interface to be displayed in the editor. UE will use the interface's name otherwise.
UINTERFACE(meta=(CannotImplementInterfaceInBlueprint))
Prevents Blueprint classes from implementing the interface. The interface would still be visible in the editor, and Blueprint classes could still call methods defined in the interface.
Pointers¶
While it is perfectly fine to use a UObject
pointer to hold a reference to an object that extends an interface, you can also explicitly indicate that a pointer is expected to implement an interface by using the TScriptInterface
template.
UPROPERTY(BlueprintReadWrite)
TScriptInterface<IInteractable> Interactable;
You can assign an object that may implement the interface to the pointer. If the assigned object does not implement the interface, Interactable
will evaluate to false
in a boolean context. Otherwise, you can call any of the methods defined in the interface on the object:
Interactable = OtherActor;
if (Interactable) {
Interactable->Interact();
}
Warning
The TScriptInterface
wrapper only works if the object's class (or one of its ancestors) implements the interface in C++. Pure Blueprint implementations will not work with this wrapper.