Hi everyone,
There is actually already a way to turn Blueprints into native code in the Engine, though it is fairly well hidden and the result can be more confusing than helpful for some people. You are certainly welcome to try to create something that improves on this.
In order to be able to use this feature, you have to set a keyboard shortcut. This is where it gets tricky, though. You need to set a keyboard shortcut for a command named “Generate Native Code” but if you search for that in the Keyboard Shortcuts section of the Editor Preferences window, you won’t find it unless you had previously had a Blueprint open in the Blueprint Editor window. So if you don’t see that command listed, open a Blueprint, then look for it again. Once you have a keyboard shortcut set, open the Blueprint you would like to generate native code for and use your new shortcut. A window will appear asking you where you would like to save the .h and .cpp files that will be created. Once you have selected the location, click the Generate button, and it will create the files for you. When you open the files, you will see why I say that it can be more confusing than helpful. The code that is generated rarely looks much like the code that you would have written when creating a class to be turned into a Blueprint.
For example, I created a basic Actor class with no modifications named AMyActor, then created a Blueprint from that class named NewBlueprint. In the Blueprint I added and set a Static Mesh Component, then created a variable named Counter, set it to 0 at Begin Play and printed its value to the screen, then on Tick I increment Counter and output the new value to the screen. The graph looks like this:
The resulting code that is generated is the following:
// NewBlueprint_C.h
#pragma once
#include "NewBlueprint_C.generated.h"
UCLASS(Blueprintable)
class ANewBlueprint_C : public AMyActor
{
public:
GENERATED_UCLASS_BODY()
UPROPERTY(BlueprintReadWrite, NonTransactional, meta=(Category="Default"))
UStaticMeshComponent* StaticMesh;
UPROPERTY(BlueprintReadWrite, NonTransactional, meta=(Category="Default"))
USceneComponent* DefaultSceneRoot;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, meta=(DisplayName="Counter", Category="Default"))
int32 Counter;
UPROPERTY(Transient, DuplicateTransient)
float K2Node_Event_DeltaSeconds;
UFUNCTION(meta=(EntryPoint="0"))
void ExecuteUbergraph_NewBlueprint(int32 EntryPoint);
UFUNCTION(meta=(DisplayName="Tick", ToolTip="Event called every frame", DeltaSeconds="0.0"))
void ReceiveTick(float DeltaSeconds);
UFUNCTION(meta=(DisplayName="BeginPlay", ToolTip="Event when play begins for this actor."))
void ReceiveBeginPlay();
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true", DisplayName="Construction Script", ToolTip="Construction script, the place to spawn components and do other setup.
@note Name used in CreateBlueprint function
@param Location The location.
@param Rotation The rotation.", Category))
void UserConstructionScript();
};
// NewBlueprint_C.cpp
#include "TestAddCode.h"
#include "NewBlueprint_C.h"
#include "GeneratedCodeHelpers.h"
#include "Runtime/Engine/Classes/Engine/SimpleConstructionScript.h"
#include "Runtime/Engine/Classes/GameFramework/Actor.h"
#include "Runtime/Engine/Classes/Components/SceneComponent.h"
#include "Runtime/Engine/Classes/Components/StaticMeshComponent.h"
#include "Runtime/Engine/Classes/Kismet/KismetStringLibrary.h"
#include "Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h"
#include "Runtime/Engine/Classes/Kismet/KismetMathLibrary.h"
#include "Runtime/Engine/Classes/Engine/SCS_Node.h"
ANewBlueprint_C::ANewBlueprint_C(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {}
void ANewBlueprint_C::ExecuteUbergraph_NewBlueprint(int32 EntryPoint)
{
FString CallFunc_Conv_IntToString_ReturnValue{};
FString CallFunc_Concat_StrStr_ReturnValue{};
int32 CallFunc_Add_IntInt_ReturnValue{};
FString CallFunc_Conv_IntToString_ReturnValue2{};
FString CallFunc_Concat_StrStr_ReturnValue2{};
int32 CurrentState = 0;
do
{
switch( CurrentState )
{
case 0:
// K2Node_FunctionEntry_177
CurrentState = EntryPoint;
break;
case 1:
// K2Node_Event_183
// Debug site.
// Wire debug site.
case 2:
// K2Node_VariableSet_43
// Debug site.
Counter = 0;
// Wire debug site.
case 3:
// K2Node_VariableGet_90
// K2Node_CallFunction_2525
CallFunc_Conv_IntToString_ReturnValue = UKismetStringLibrary::Conv_IntToString(Counter);
// K2Node_CommutativeAssociativeBinaryOperator_72
CallFunc_Concat_StrStr_ReturnValue = UKismetStringLibrary::Concat_StrStr(TEXT("Counter is set to: "), CallFunc_Conv_IntToString_ReturnValue);
// K2Node_CallFunction_2526
// Debug site.
UKismetSystemLibrary::PrintString(this, CallFunc_Concat_StrStr_ReturnValue, true, true, FLinearColor(0.000000,0.660000,1.000000,1.000000));
// Wire debug site.
CurrentState = -1;
case 4:
// K2Node_Event_182
// Debug site.
// Wire debug site.
case 5:
// K2Node_VariableGet_89
// K2Node_CommutativeAssociativeBinaryOperator_73
CallFunc_Add_IntInt_ReturnValue = UKismetMathLibrary::Add_IntInt(Counter, 1);
// K2Node_VariableSetRef_5
// Debug site.
Counter = CallFunc_Add_IntInt_ReturnValue;
// Wire debug site.
case 6:
// K2Node_VariableGet_89
// K2Node_CommutativeAssociativeBinaryOperator_73
CallFunc_Add_IntInt_ReturnValue = UKismetMathLibrary::Add_IntInt(Counter, 1);
// K2Node_CallFunction_2523
CallFunc_Conv_IntToString_ReturnValue2 = UKismetStringLibrary::Conv_IntToString(CallFunc_Add_IntInt_ReturnValue);
// K2Node_CommutativeAssociativeBinaryOperator_71
CallFunc_Concat_StrStr_ReturnValue2 = UKismetStringLibrary::Concat_StrStr(TEXT("Counter is set to: "), CallFunc_Conv_IntToString_ReturnValue2);
// K2Node_CallFunction_2524
// Debug site.
UKismetSystemLibrary::PrintString(this, CallFunc_Concat_StrStr_ReturnValue2, true, true, FLinearColor(0.000000,0.660000,1.000000,1.000000));
// Wire debug site.
default:
break;
}
} while( CurrentState != -1 );
return;
}
void ANewBlueprint_C::ReceiveTick(float DeltaSeconds)
{
// K2Node_FunctionEntry_178
// K2Node_VariableSet_44
K2Node_Event_DeltaSeconds = DeltaSeconds;
// K2Node_CallFunction_2527
ExecuteUbergraph_NewBlueprint(4);
return;
}
void ANewBlueprint_C::ReceiveBeginPlay()
{
// K2Node_FunctionEntry_179
// K2Node_CallFunction_2528
ExecuteUbergraph_NewBlueprint(1);
return;
}
void ANewBlueprint_C::UserConstructionScript()
{
// K2Node_FunctionEntry_128
// Debug site.
// Wire debug site.
return;
}
If you look at the code, you will see in the header file where the Static Mesh Component and Counter are both declared. Then if you walk through the code in the source file, you will see that it does the same thing the Blueprint’s event graph does, but the code is most likely not written the same way that you would have written it yourself. The more complex the event graph is, the more confusing this code can become.
WARNING: If you try to do this with a complex Blueprint, it may have some problems. I also make no guarantees that you can take this code and turn it back into a Blueprint.