The Many Faces of Interfaces in Unreal Engine 4

April 8, 2021

One of the oft-quoted pitfalls of the inheritance model is multiple inheritance, which can lead to memory overlaps, unexpected behavior, and hard to track bugs (although not everyone agrees). However, multiple inheritance using interfaces, classes which contain only overridable functions, can ameliorate many of these concerns while providing flexibility and code decoupling. For this reason, the use of multiple inheritance with interfaces abounds.

In fact, Unreal Engine 4 does not permit multiple inheritance except via interfaces. Yet the syntax for using interfaces with the Engine can be clunky and easy to forget. This article will provide a comprehensive overview of interfaces in the Engine and describe the various methods for implementing such interfaces.

The Definition of an Interface

An interface provides a way to ensure that potentially unrelated classes implement a common functionality. For example, consider a game like Grand Theft Auto V where countless objects can be interacted with, from a car, to a telephone, to a helicopter. When the player overlaps one of these objects, one way to interact is to check whether the object is part of a predefined set of classes (Car, Phone, Helicopter) and then call the appropriate interaction method if it is.

This can get unwieldy quickly as you add classes though. As an initial matter, you have to keep that list of interactable classes constantly up to date. In addition, each class may have a different interact method signature, requiring different inputs or returning different values. To top it off, your cost of determining whether a class is interactable increases linearly - if the list contains fifty classes, you may have to check all fifty before determining that a class is or is not interactable.

This can be solved easily by creating an Interactable interface with, at a minimum, an Interact method. Now, on overlap, all we need to do is check whether the class implements Interactable. If it does, call the Interact method on it. The result is a much neater, less coupled, easier to maintain solution.

Forms of Interfaces

Interfaces in Unreal Engine 4 can take many forms, depending on whether the interface or its functions are intended to be used in C++, in Blueprints, or both.

Base Form

Every interface must have at least two elements: a UInterface and an IInterface. The UInterface should look like this:

UINTERFACE()
class UInteractableInterface : public UInterface
{
    GENERATED_BODY()
}

Here, UInteractableInterface derives from UInterface, the base class for Engine interfaces (located at UObject/Interface.h). The name of the class ends with Interface by convention, but this is optional. Finally, as with other reflected classes, the body of the class contains a GENERATED_BODY() macro which will be used by the UnrealHeaderTool to create reflection data.

The IInterface should look like this:

class IInteractableInterface
{
    GENERATED_BODY()

public:

    // ... functions go here
}

The name of the IInterface must exactly match the name of the UInterface but with an I prefix. This class must also contain a GENERATED_BODY() macro. Then, define desired functions. Note that neither the UInterface nor the IInterface may contain UPropertys. Attempting to include such a member will result in the following error:

'Member variable declaration' is not allowed here

You can get away with including non-UProperty properties, but you should avoid doing so, as it defeats the principle of making interfaces functionality-only.

Now to the implementation.

Native Implementation Only

To make an interface exclusively implementable in C++, add the CannotImplementInterfaceInBlueprint meta specifier to your UInterface, like so:

UINTERFACE(meta=(CannotImplementInterfaceInBlueprint))

In this type of interface, you can use pure and defined virtual functions, but you may not use BlueprintImplementableEvent or BlueprintNativeEvent functions.

virtual void DefinedVirtualFunction() {};
virtual void PureVirtualFunction() = 0;

True to its name, this interface cannot be implemented in Blueprints. However, it can be called from Blueprints.

UFUNCTION(BlueprintCallable)
virtual void ExposedVirtualFunction() {}

Mixed Implementation

An interface allows implementation in C++ and Blueprints by default. Official documentation suggests that you should also decorate the UInterface with the Blueprintable specifier, but as of 4.26, behavior with or without this specifier appears identical.

UINTERFACE(Blueprintable)

In this type of interface, you can use pure and defined virtual functions as before, but such functions cannot be BlueprintCallable. However, you can also introduce BlueprintImplementableEvents, which can be implemented only in Blueprints, and BlueprintNativeEvents, which can be implemented in both C++ and Blueprints. Both types can be BlueprintCallable.

UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
void BPFunction();

UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void BPNativeFunction();

Implementing an Interface

To implement an interface natively, simply inherit from its IInterface variant and define its respective methods.

UCLASS()
class UImplementor 
    : public UObject
    , public INativeInterface
    , public IMixedInterface
{
    GENERATED_BODY()

public:

    void DefinedVirtualFunction() override 
    {
        // do something
    };

    void PureVirtualFunction() override
    {
        // do something else
    }

    void BPNativeFunction_Implementation() override
    {
        // do something native
    }
};

Notice that with BlueprintImplementableEvent overrides, you must add the _Implementation suffix.

To implement an interface in Blueprints, navigate to the Class Settings tab in the toolbar at the top of the Blueprint editor and look for the Interfaces panel.

Calling an Interface

There are two methods for determining if a class is an interface, and which one you use depends on how the interface was declared. If the interface is native-only (cannot be implemented in Blueprints), simply cast the target class to the desired IInterface, and then use the interface like any other class.

void Foo(UImplementor* PotentialInterface)
{
    if (INativeInterface* Interface = Cast<INativeInterface>(PotentialInterface))
    {
        Interface->PureVirtualFunction();
    }
}

If the interface is mixed, the cast will fail for Blueprint classes that implement the interface. Instead, we ask the class whether it implements the UInterface using the Implements template method. Then we call the generated static proxy method of the function we want to call from IInterface, passing the object that implements the interface as the first parameter. This function will be prefixed with Execute_.

void Foo(UImplementor* PotentialInterface)
{
    if (PotentialInterface->Implements<UMixedInterface>())
    {
        IMixedInterface::Execute_BPNativeFunction(PotentialInterface);
    }
}

And that's it. Nailing the syntax is no cakewalk, but once you get the hang of it, interfaces will be a powerful tool in your Unreal Engine 4 toolbelt.



© 2021 Mustafa Moiz.