Unreal Engine interfaces
When we develop in C++, in Java, or in any object-oriented programming language, we often use the OOP concept of interfaces. In C++, they are generally implemented using totally abstract class without members, i class containing only pure virtual functions.
However, when developing blueprint C++ classes with Unreal Engine, it’s not possible to directly use this kind of syntax. Indeed, Unreal Engine has a specific syntax for the interfaces. It is detailed in the Unreal Engine wiki, here, but what we are proposing in this article is a short listing of code of the steps “declaring an interface”, “implementing an interface”, “using an interface”, and also some details about TScriptInterface.
Declaring an interface.
Let’s say we want to have an interface BroadcastListener with a single method OnBroadcastReceived(const FString& Message). This is how to declare it (in a BroadcastListener.h file):
UINTERFACE(BlueprintType)
class BROADCAST_API UBroadcastListener : public UInterface
{
GENERATED_BODY()
};
class BROADCAST_API IBroadcastListener
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Broadcast")
int32 OnBroadcastReceived(const FString& Message);
};
We can note that it is mandatory to define both UBroadcastListener and IBroadcastListener using the presented syntax. Also, to use UInterface, we need to include UObject/Interface.h, and the function have to return something, even if we ignore the result after.
Implementing the interface.
Now that we have defined the interface, we want to define an implementation of this interface. Again, we cannot invent the syntax to use, we have to use the following one. For the header file:
UCLASS(BlueprintType)
class BROADCAST_API ULogBroadcastListener : public UObject, public IBroadcastListener // We inherit both UObject and IBroadcastListener
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Broadcast")
int32 OnBroadcastReceived(const FString& Message); // This is the prototype declared in the interface
virtual int32 OnBroadcastReceived_Implementation(const FString& Message) override; // This is the declaration of the implementation
};
We can note here that the name of the implementation function must follow exactly this scheme: InterfaceFunctionName_Implementation.
Now, for the source file:
int32 ULogBroadcastListener::OnBroadcastReceived_Implementation(const FString& Message)
{
UE_LOG(BroadcastLog,Warning,TEXT("Message: %s"), *Message);
}
Of course, we can implement any logic here. This ULogBroadcastListener only logs the message.
Using the interface.
We will now see how this interface can be used, i how a reference to the interface can be stored and how we can call the methods defined by the interface.
Storing a reference to the interface
Let’s say we want to have a IBroadcastListener as a property of a class, so that a user can bind a listener of its choice. This is how it will be defined in the UCLASS:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Broadcast")
TScriptInterface<IBroadcastListener> BroadcastListener
The key point here is the use of TScriptInterface, mandatory to store a reference to an interface.
Setting the reference programmatically
Let’s say we want to programmatically set the value of the property BroadcastListener. Here’s the way:
ULogBroadcastListener* LogBroadcastListener = NewObject<ULogBroadcastListener>(); // Instantiating the ULogBroadcastListener
BroadcastListener.SetObject(LogBroadcastListener); // BroadcastListener is of type TScriptInterface
BroadcastListener.SetInterface(Cast<IBroadcastListener>(LogBroadcastListener));
Calling a method of the interface
Now, when we want to call the method defined by the interface (for instance for firing the event of a broadcast received), we will use the following syntax:
UObject* BroadcastListenerObject = BroadcastListener.GetObject();
IBroadcastListener::Execute_OnBroadcastReceived(BroadcastListenerObject, Message); // Message is of type FString
The key point here is in the call to the OnBroadcastReceived method: we need to make a static call on a function of interface named Execute_NameOfTheInterfaceMethod. The first parameter of this function is the object we want to call the function on, and thereafter we have all the parameters we want to pass to this function.