Introduction
I wrote this blog post to fill a few gaps in the documentation I’d found for C++ interfaces in Unreal. Specifically I have a few tips for what I think are common usage scenarios that no blog post I found talked about.
Note: This blog post is based on Unreal Engine version 4.25.3
What are Interfaces?
Interfaces are great in any language. They let you define a set of functionality that anything can implement, and not be tied to a rigid class hierarchy. My favourite implementation of this is actually in Go, where you don’t even have to declare that you’re implementing an interface – if you happen to have the right combination of functions on your type, you just are. Lovely.
C++ technically doesn’t have interfaces, but it does have multiple inheritance, which can be made to emulate it. This is what UE4 does, with a bunch of boilerplate tacked on to make it a bit more explicit, and to support the reflection system.
Why another blog post about this?
There are a bunch of blog posts out there teaching you how to use UE4 C++ interfaces, and I used them to get up to speed (I’m not an expert, I only started using them recently).
But I found pretty quickly that they skip over a few things that I needed almost immediately. For example they generally assume you’re working entirely within C++, when I think a lot of us are working in mixed C++ / Blueprint environments. They also didn’t talk about what you need to do when you want interface variables. So I felt I could add something from what I’ve learned in the last few weeks.
I could just refer to the other tutorials for the basics, but I decided to make this post self-contained and cover C++ interfaces from the ground up as well.
So this post will cover:
- Creating and implementing a C++ interface
- Implementing this interface directly in Blueprints as well as C++
- Storing variables typed to the interface in C++ and Blueprints
Declaring the C++ Interface
I like to declare my interfaces in C++ and not as Blueprint Interfaces, because it gives me more options; C++ interfaces can be used in both C++ and Blueprints, while Blueprint Interfaces can only be used in Blueprints.
Let’s say we’re creating an interface called DoSomeThings
which, well, does some things. First we need a header file:
#pragma once
#include "CoreMinimal.h"
#include "DoSomeThings.generated.h"
UINTERFACE(MinimalAPI)
class UDoSomeThings : public UInterface
{
GENERATED_BODY()
// This will always be empty!
};
class YOURPROJECT_API IDoSomeThings
{
GENERATED_BODY()
public:
// ... Here is where we will define UFUNCTION interface methods
};
As you can see, you have a UDoSomeThings
and IDoSomeThings
– there doesn’t seem to be any link between them, but the naming convention actually does this automatically thanks to UE header magic.
Adding methods
So next, you’ll want to add some methods to the interface, which you always do inside the IDoSomeThings
class. Lets start with something simple:
class YOURPROJECT_API IDoSomeThings
{
GENERATED_BODY()
public:
/// Get the number of things
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Things")
int GetNumberOfThings();
};
There’s a couple of things to note there:
- I’ve made it
BlueprintCallable
, because I want to be able to use it in Blueprints - I’ve said
BlueprintNativeEvent
because I want to give C++ classes the option of implementing it
Why BlueprintNativeEvent?
Usually when you declare BlueprintNativeEvent
it means you immediately want to add a C++ implementation, named in this case GetNumberOfThings_Implementation
. But, since this is just the interface definition, you don’t have to do that yet. You wait until you actually implement this interface on a C++ class to do that.
However: I’ve discovered that this doesn’t mean you must have a C++ implementation. In my testing you can totally implement this interface directly in Blueprints with no C++ intermediate class if you want, the BlueprintNativeEvent
just allows you to have C++ implementors as well.
As such I don’t see why you’d use any other specifier; the documentation says the other options are: no specifier (can’t be implemented in BP), or BlueprintImplementableEvent
(must be implemented in BP, can’t be implemented in C++). Since BNE seems to let you do anything I can’t see why the others are useful, in interfaces specifically. 🤷♂️
Implementing the Interface in C++
Here’s a minimal C++ implementation of the interface on an Actor called SomeThingsActor:
#pragma once
#include "CoreMinimal.h"
#include "DoSomeThings.h"
#include "SomeThingsActor.generated.h"
UCLASS(Blueprintable)
class YOURPROJECT_API ASomeThingsActor : public AActor, public IDoSomeThings
{
GENERATED_BODY()
public:
virtual int GetNumberOfThings_Implementation() override;
};
#include "SomeThingsActor.h"
int ASomeThingsActor::GetNumberOfThings_Implementation()
{
return 1;
}
Easy, right? The only interesting thing is that because the interface definition was set to BlueprintNativeEvent
, we implement GetNumberOfThings_Implementation
rather than GetNumberOfThings
.
Implementing the Interface in Blueprints
As I mentioned earlier, BlueprintNativeEvent
doesn’t stop you implementing this interface directly in Blueprints if you want – at least in my experience. I don’t know if this is by accident or by design. It’s specific to interfaces, if you use BlueprintNativeEvent
on a regular UCLASS
you must have a C++ implementation, but it’s in the same class in that case.
You can also subclass a Blueprint class from a C++ implementation like ASomeThingsActor
above, and override the method. But you don’t have to, with interfaces.
Here’s how you’d implement it directly. On your Blueprint class:
- Click on “Class Settings” in the toolbar
- In the “Details” pane under Interfaces, click “Add” next to “Implemented Interfaces”.
- Pick the interface:
- Under “My Blueprint” you’ll see the interface definition:
- Double-click it and implement however you like:
Detecting & Calling the Interface
Calling in C++
A lot of information online will tell you that you can just use Cast<>
to both detect and access an interface in C++:
auto I = Cast<IDoSomeThings>(Actor);
if (I)
{
int Num = I->GetNumberOfThings();
}
It’s true that this works if Actor
is a C++ implementation of IDoSomeThings
, or a Blueprint subclass of a C++ implementor.
If, however, you implement this interface directly on a Blueprint, Cast<IDoSomeThings>
will always return nullptr. Always. This is because the C++ side has no idea the actor implements this. This is particularly nefarious because nothing will look wrong until you happen to use a trivial direct BP implementation and suddenly a bunch of things will start to fail silently.
To detect interfaces reliably, whether they’ve been implemented as C++ or Blueprints, you need avoid using Cast<>
and use UE4’s explicit interface utilities which use the full reflection system and can therefore detect Blueprints.
First, detecting if the interface is implemented:
/// Either of these 2 are valid
/// Option 1 does more sanity checks, is null-safe on Actor
bImpl = UKismetSystemLibrary::DoesImplementInterface(Actor, UDoSomeThings::StaticClass())
/// Option 2 skips some checks, you must ensure Actor is non-null
bImpl = Actor->GetClass()->ImplementsInterface(UDoSomeThings::StaticClass())
Once you’ve detected if the interface is present, you must call it via the interface wrappers, not directly on the instance:
if (bImpl)
{
int Num = IDoSomeThings::Execute_GetNumberOfThings(Actor);
}
This will work universally, whether the interface has been implemented in C++ or Blueprints, so it seems best to always use this method. It’s probably less efficient, but personally I tend to use interfaces at a fairly high level so am not particularly concerned.
Calling in Blueprints
This is a little simpler because there’s really only one way to do it; you can call any interface on any object:
Notice the envelope icon which shows that this is a “Message” to the receiving object, which is silently ignored if that interface is not implemented. It works whether the interface was implemented in C++ or Blueprints. When you pick the function from the list, it displays like this:
If you specifically need to know whether an object implements an interface, you can test with the “Does Implement Interface” node:
Storing Interfaces As Variables
Sometimes you want to hold on to a reference to an interface, as the general type.
Blueprint Variables
In Blueprints this is pretty easy. You need to make sure your “U” interface class has the BlueprintType
specifier, which allows it to be stored in Blueprint variables:
UINTERFACE(MinimalAPI, BlueprintType)
class UDoSomeThings : public UInterface
Once you’ve done that, you can just create variables with this type inside your Blueprints like any other variable:
C++ variables
I usually prefer to define my variables in C++, because then I can use them in C++ code or Blueprints.
In C++ things are slightly more tricky, because you can’t store a IDoSomeThings
pointer as a UPROPERTY()
, because they don’t play nice with the garbage collector. We need something that holds on to the UObject.
TScriptInterface (Prone to error!)
Spoiler: You might wonder in a minute why I talk about TScriptInterface only to tell you in the end not to use it. Well, it’s because it’s actually suggested by the compiler if you try to use an interface pointer as a UPROPERTY. That’s how I found it, only to discover its problems later.
Unreal provides a TScriptInterface<>
type which is designed to wrap an object as a specific interface. It’s the official suggested way to hold on to both the object reference safely and the interface it’s supposed to be presenting. You can use it like this:
UPROPERTY(BlueprintReadWrite)
TScriptInterface<IDoSomeThings> SomethingInstance;
TScriptInterface<>
is used as a value type, so rather than null when not initialised, the default state is an empty wrapper. To test whether it’s empty or not, use the bool()
overloaded operator:
if (SomethingInstance)
{
// OK this points to something useful
}
The simplest way to assign an object which implements the interface to this property is to use the assignment operator:
if (UKismetSystemLibrary::DoesImplementInterface(Actor, UDoSomeThings::StaticClass()))
{
SomethingInstance = Actor;
}
When you want to use this stored reference, you call it using the overloaded ->
operator, i.e.:
int Num = SomethingInstance->GetNumberOfThings();
Simple in theory, but there’s one major problem with this, and that’s that TScriptInterface<>
derives the interface pointer using Cast<>
. And as we established earlier, Cast<>
will always return nullptr if the instance in question is only implementing the interface at the Blueprint level, and none of its superclasses implement it in C++.
The thing is, you can still make TScriptInterface<>
work in this scenario, by instead calling:
int Num = IDoSomeThings::Execute_GetNumberOfThings(SomethingInstance.GetObject());
This is because the UObject pointer in the TScriptInterface is fine, but the interface pointer is null.
Also, calls in Blueprints will work fine with this TScriptInterface<>
variable, which can hide the problem. Various methods of TScriptInterface<>
will fail in C++ if your class only implements at a Blueprint level, so I consider it unsafe to use.
UObject: the more reliable way
I’ve now chosen to skip TScriptInterface
and instead just hold a UObject*
, because the object reference is the only bit of TScriptInterface
which is reliable in cases where the interface is only implemented in Blueprints anyway.
UPROPERTY(BlueprintReadWrite)
UObject* SomethingInstance;
This way you can just assign the “raw” object pointer to this variable and use the standard “safe” way to call it, avoiding Cast<>
as we previously discussed:
// I'm assuming here that we checked the object supported the interface before saving
// it to the variable; if not we should check for that as well as nulls
if (SomethingInstance)
{
int Num = IDoSomeThings::Execute_GetNumberOfThings(SomethingInstance);
}
The End
So, that’s what I’ve learned about interfaces in Unreal in a mixed C++ / Blueprint environment in the last couple of weeks. I’d written it all down in my personal notes but came to the conclusion my usage must be pretty common, and those little gotchas seemed worth putting down somewhere public for others to potentially benefit from.
I’ve done my best but I’m not an expert, if I’ve misunderstood anything in this blog post, please do let me know on Twitter. 😉