Download

Refactor by moving BP components to C++

Hello

I have a BP Actor-derived class (BP_MyActor)

It contains several BP components that define various behaviors.

For performance reasons (compute-heavy code), it is now time to move some of these components to C++. Let’s call such a component BP_MyComp.

However, since BP_MyComp is rather big, I do not want to delete it right away but I want, for some time, to be able to switch between the BP implementation and the in-progress C++ implementation.

Being a C++ programmer, my first thought was :

    1. let’s create an interface IMyComp where I declare the BP_MyComp public functions.
    1. Make BP_Comp implement IMyComp and reconnect the implemented interface events to their proper implementation (UE unfortunately does not “understand” this refactoring and tells me the existing function clash with the new interface ones… I have to rename the old functions and move their BP graph into the new interface event implementation graph)

This works OK. I can see my C++ defined interface and implement it.

Then, I wanted to remove the BP_MyComp statically defined component from BP_MyActor (the one that was added with the big green “+ Add Component” button in the actor BP editor) and turn the component into a variable.

The idea was to have a variable of type IMyComp and, at runtime, decide (on a flag or some .ini
setting) whether to instantiate the BP_MyComp or a new Cpp_MyComp, then assign the result to the IMyComp variable, then eventually dynamically add it by calling AddComponent on the actor.

It does not work.

It is simply not possible to declare a BP variable with an interface type. It works for BP interfaces, but not C++ interfaces. Unless I have missed a specific macro keyword.

How would you guys approach such a refactoring where BP code is gradually moved to C++ with A/B testing?

A base class maybe? Am I allowed to reparent BP_MyComp to a pure C++ base class and use it as the type of a property ?

I am confused by the way UE interfaces are meant to be used. This looks like such a basic pattern…

Thanks in advance!

I was able to create a variable in a random blueprint that was of the type ReactToTriggerInterface. Is your interface blueprintable?

You could have an interface, a C++ class that inherits that interface, then the BP_MyComp can derive from the C++ class. From there you can make functions that are implementable in either C++ or Blueprints. Like so

/**A version of React To Trigger that can be implemented in C++ or Blueprint. */
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category=Trigger Reaction)
bool ReactToTrigger();

To move things to C++, I use implementation inheritance instead.
Yeah, I’m not a big fan of implementation inheritance, but it’s the Way Of Life in Unreal Engine.

So, assuming I wanted to port a Component blueprint to C++, I’d create a new ActorComponent or SceneComponent called something like MyComponent in C++. It might even be empty to begin with, but it would inherit from whatever base Unreal component class your BP component is inheriting from.

Then I’d go into the component definition in Blueprint and choose “File → Reparent” to make it inherit from my MyComponent instead of the base component type. This should work like a no-op. Make sure you can compile and run at this point.

Now, you can move things from your BP class into your MyComponent C++ class, one field or function at a time if you want. Declare it in C++, remove it from BP, compile and run to make sure it works, repeat.

At some point, you’ve moved all your blueprint functionality into the C++ class, and the blueprint class is empty. Now you can go to all blueprints that use the blueprint-component-class, and reparent them to inherit directly from your MyComponent instead of the blueprint component class.

Finally, you can remove the blueprint class at this point.

2 Likes

Hello,
Thanks for your reply.
That was exactly my plan. However, once this is done, I cannot declare, in my parent actor, a variable that is of the interface type.
I need it to be of that type, not the concrete one, because I want to be able to create a different concrete impl based on a runtime condition.

I can’t make my interface Blueprintable , because the UBT complains that my interface should derive from UObject.

Here’s the code that works in terms of implementation, but does not allow me to declare a BP variable with that type:

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "CipCompInterface.generated.h"

UINTERFACE(MinimalAPI)
class UCipCompInterface : public UInterface
{
    GENERATED_BODY()
};

class COMPINTERFACEPOC_API ICipCompInterface
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = Ball)
    void PerformCubeMove(float DeltaTime);
};

But adding UCLASS(Blueprintable) before class COMPINTERFACEPOC_API ICipCompInterface triggers the following error :

|Error||Class ‘ICipCompInterface’ must inherit UObject or a UObject-derived class

Using a base class, from which I inherit both my C++ and BP implementation does work, and although less elegant imho, that’s the option I have chosen.

Hi jwatte
Thanks for your excellent suggestion.
I was afraid that I could not reparent the (big) existing Blueprint component but this works.
I was hoping that the editor would “understand” that reparenting to a base class that contains declarations for existing events/functions would automagically “rewire” them, but this is not the case. I need to move the graph from the older events to the newly implemented ones, and change the calling sites.
That’s ok, though.

Thanks again :slight_smile:

BTW, I forgot to mention that I really want to do A/B testing.

This is why I settled for an alternative version of your suggestion where the base class doesn’t do anything, and I choose between two concrete implementations of this base class upon actor creation (I am doing it in BeginPlay but I might want to move it somewhere earlier if this proves dangerous)

The only downside is that I cannot add the component the standard way and need to call Add XXX Comp explicitly (+ store it inside a variable for easier use).

An interface needs to derive from UInterface, and it needs to be BlueprintType, not Blueprintable. Blueprintable means it has data members, which interfaces never do.

Your actual interface functions need to be BlueprintNativeEvent, and thus follow that convention where your C++ implementation goes in the _Implementation function variant.

Hello
Thank you very much. Using BlueprintType indeed allowed me to declare variables inside my actors with this type.

However, I wanted to use this for UActorComponent subclasses (since my existing BP code is an UActorComponent) and I receive the following error :

C:/wb/sandbox/CompInterfacePoc/Source/CompInterfacePoc/CipCppCompFromInterface.h(12)  : Error: Interface class 'UCppCipCompFromInterface' cannot inherit from non-interface class 'ActorComponent'

I decided to go with the “common C++ base class” method and, so far, it appears to work nice.

My only issue at the moment is that I can’t have functions that are both BlueprintNativeEvent (to implement them in both my BP and C++ components) and that are Server RPC. I must go trough a wrapper function. No big deal but it makes the code a little messier.

Thanks for everyone for their kind help.