Quick answer: Use IMyInterface::Execute_FunctionName(Target) — not direct virtual calls. Define the C++ side with UFUNCTION(BlueprintNativeEvent) and override the _Implementation suffix.

You author a UInterface called IInteractable with an Interact function. You implement it in C++ on a parent class and in Blueprint on a child. When C++ code calls InteractableActor->Interact(), the linker complains. When you call it via Cast<IInteractable>, the Blueprint child is silently skipped. Both approaches feel correct but neither works for the full interface contract.

How UInterfaces Differ from Plain C++ Virtuals

UInterfaces are a Blueprint-friendly variant of multiple inheritance. They’re not pure C++ interfaces — they participate in the UObject reflection system, can be implemented in Blueprints, and must dispatch through a generated thunk to support both languages.

Each interface function with BlueprintNativeEvent or BlueprintImplementableEvent gets three generated forms:

The dispatcher checks whether Target implements the interface. If yes, it routes to either the Blueprint graph (if overridden) or the C++ _Implementation.

Step 1: Declare the Interface Correctly

// IInteractable.h
UINTERFACE(MinimalAPI, Blueprintable)
class UInteractable : public UInterface { GENERATED_BODY() };

class MYGAME_API IInteractable
{
    GENERATED_BODY()
public:
    UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="Interaction")
    void Interact(AActor* Instigator);
};

Step 2: Implement on a C++ Class

// MyDoor.h
UCLASS()
class AMyDoor : public AActor, public IInteractable
{
    GENERATED_BODY()
public:
    virtual void Interact_Implementation(AActor* Instigator) override;
};

// MyDoor.cpp
void AMyDoor::Interact_Implementation(AActor* Instigator) {
    UE_LOG(LogTemp, Log, TEXT("Door opened by %s"), *Instigator->GetName());
}

Note the _Implementation suffix on both declaration and definition. Without it, the engine won’t find your implementation and Blueprint child overrides won’t inherit it correctly.

Step 3: Call via Execute_

// caller
if (Target && Target->Implements<UInteractable>())
{
    IInteractable::Execute_Interact(Target, this);
}

Execute_Interact handles both:

Don’t call Target->Interact(this) directly. The dispatch is statically resolved and won’t find Blueprint overrides.

Common Mistake: Cast<IInteractable>

You might be tempted to:

if (IInteractable* I = Cast<IInteractable>(Target))
    I->Interact_Implementation(this);   // finds only C++ implementations

This works for C++ implementations but misses Blueprint-only ones — Cast<I> only matches if the class’s native UClass inherits the interface. A Blueprint that adds the interface in its class settings doesn’t satisfy this. Always check Implements<U> and dispatch through Execute_.

BlueprintImplementableEvent vs BlueprintNativeEvent

Use BlueprintNativeEvent when there’s a sensible default; use BlueprintImplementableEvent for pure callbacks that only make sense per-derived-Blueprint.

Verifying

Build a test: a Blueprint actor that adds BPI_Interactable in its class settings and overrides Interact to print a string. From C++, call Execute_Interact on that actor. The Blueprint string should print. Then a C++ subclass with its own Interact_Implementation — that one should print its C++ log.

“Direct calls = native only. Execute_ = both languages. Always go through the static dispatcher.”

Put a checkf(Target->Implements<U>(), TEXT(…)) guard in dev builds — saves hours of “why isn’t this firing” debugging.