How it works...

To make it possible to test this recipe in the Level Blueprint, we need to make the interface functions callable via Blueprint, so we need the BlueprintCallable specifier on our UFUNCTION.

However, in a UInterface, the compiler expects the interface to be implementable via both C++ and Blueprint by default. This conflicts with BlueprintCallable, which is merely saying that the function can be invoked from Blueprint, not that it can be overridden in it.

We can resolve this conflict by marking the interface as CannotImplementInterfaceInBlueprint. This enables the use of BlueprintCallable as our UFUNCTION specifier, rather than BlueprintImplementableEvent (which has extra overhead due to the extra code allowing for the function to be overridden via Blueprint).

We define IsDead and Die as virtual to allow them to be overridden in another C++ class that inherits this one. In our default interface implementation, IsDead always returns false. The default implementation of Die prints a death message to the screen, and then destroys the object implementing this interface if it is an Actor.

We can now create a second interface called Undead, which inherits from Killable. We use public UKillable/public IKillable in the class declarations to express this.

Of course, as a result, we need to include the header file that defines the Killable interface. Our new interface overrides the two functions that Killable defines to provide more appropriate definitions of IsDead/Die for Undead. Our overridden definitions have Undead already dead by returning true from IsDead. When Die is called on Undead, we simply print a message with Undead laughing at our feeble attempt to kill it again, and do nothing.

We can also specify default implementations for our Undead-specific functions, namely Turn() and Banish(). When the Undead are Turned, they flee, and for demonstration purposes, we print a message to the screen. If an Undead is Banished, however, they are annihilated and destroyed without a trace.

To test our implementation, we create two Actors that each inherit from one of the two interfaces. After we add an instance of each actor to our level, we use the Level Blueprint to access the level's BeginPlay event. When the level begins to play, we use a message call to try and call the Die function on our instances.

The messages that print out are different and correspond to the two function implementations, showing that the Zombie's implementation of Die is different, and has overridden the Snail's.