The U/I class split behind every C++ interface, which function specifier lets Blueprint override what, and how a call actually dispatches.
A C++ interface is always two types: a UINTERFACE (reflection-facing) and the actual I-prefixed class you implement against.
// HealthChangeListener.h
UINTERFACE(MinimalAPI, Blueprintable)
class UHealthChangeListener : public UInterface
{
GENERATED_BODY()
};
class IHealthChangeListener
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Health")
void OnHealthChanged(float NewHealth);
};
UHealthChangeListener (U-prefixed) is boilerplate you never touch — it just gives UHT a UObject-derived type to hang reflection data on.IHealthChangeListener (I-prefixed) is what you actually inherit from and implement. GENERATED_BODY() is needed inside this class too, not just the U-class — easy to forget.Blueprintable on the UINTERFACE lets a Blueprint class implement the interface from the editor (Class Settings → Implemented Interfaces). Without it, only C++ classes can implement it.class AMyCharacter : public ACharacter, public IHealthChangeListener. Multiple inheritance is normally avoided in UE C++, but interfaces are the sanctioned exception.| Specifier | Gives you |
|---|---|
BlueprintNativeEvent | A C++ default implementation and lets Blueprint override it. Usually what you want for a cross-language interface. Implement the default in C++ as OnHealthChanged_Implementation |
BlueprintImplementableEvent | Zero C++ default behavior — no _Implementation override possible or needed. Use when only Blueprint will ever provide the body |
Plain UFUNCTION() | C++-only. Blueprint can't override or implement it, even if the UINTERFACE itself is Blueprintable |
UINTERFACE Blueprintable only opens the door for a Blueprint class to implement the interface at all — it doesn't make any individual function overridable. That still comes down to BlueprintNativeEvent / BlueprintImplementableEvent on the function itself.
A call dispatches to whichever implementation is "closest" — a Blueprint override if present, otherwise the C++ _Implementation:
if (TargetActor->Implements<UHealthChangeListener>())
{
IHealthChangeListener::Execute_OnHealthChanged(TargetActor, 75.0f);
}
Implements<T>() checks whether an actor's class implements the interface at all, without knowing its concrete type — the same decoupling win Blueprint Interfaces give you (Chapter 2), just from C++. Execute_OnHealthChanged is UHT-generated per BlueprintNativeEvent/BlueprintImplementableEvent function and is always the correct way to call one, rather than calling _Implementation directly or casting to the interface and calling the function bare.
UFUNCTION() with no BlueprintNativeEvent/BlueprintImplementableEvent is C++-only, regardless of whether the UINTERFACE is Blueprintable.GENERATED_BODY() is needed inside the I-class, not just the U-class.BlueprintImplementableEvent instead of BlueprintNativeEvent — there's no _Implementation override possible or needed, so writing one is dead code.Answer each out loud before re-reading the chapter.