Blueprint to C++ Translating Tool!

Hey people!

I’m a C++ coder, like lot of people here, I know, but I love to prototype things in Blueprint for a fast workflow before putting it in C++. I think some people may do the same. The problem is that we have to rewrite all instructions manually to C++, and that’s kind not fun.

I was thinking in creating a sort of translating tool in order to make the transition from Blueprint to C++ much faster! That won’t be an easy task, but I would like to try it anyway!

========== Plan A #EDITED: not going to work!

The problem is that I don’t exactly know where to start… I think there is a Blueprint to C++ translator in UE4 source code already, more specifically the classe FKismetCompilerContext at the file KismetCompiler.h! I can see at the source that it generates an intermediate compiled code of the header and the body from a Blueprint… look at line 3614 and 3619 at KismetCompiler.cpp (4.8.2 release). But I need some tips of how to use these functions properly…

========== Plan B… maybe simpler

I also think it may be simpler and faster if I could take the Blueprint Nodes that the user have selected, simple copy the code (like a Ctrl+C) and process that in order to express a equivalent C++ translation. Like in the pictures attached here!

… you guys know that you can do a Ctrl+C from a Blueprint and it becomes ASCII text, ready to paste some where else!

… but for that I would need the Blueprint grammar for the output text!

Any advises?
Thanks!

[HR][/HR]
****EDITED:
========== plan C:

Now I prefer to analyse the built-in graph structure directly from inside the editor, and in order to do that I may create a plugin to use all Blueprint Editor utilities to access the data right the way!

I wish you all the best, this is a quite a must in mobile dev but I’m not a C++ coder…

Will be watching this :slight_smile:

A few things to bear in mind here:

  • Blueprint doesn’t translate directly to C++. (There’s no Blueprint > C++ Converter as such, it’s more a C++ > Bytecode compiler). Although the nodes are written in C++, they actually compile down into Bytecode, not Binary.
  • It’s already possible to jump straight into the code-version of a node, just right-click it and hit ‘Go To Code Definition’, and it’ll take you straight there.

If you want a chain of nodes to basically copy-paste their code from C++ into another text window, you could literally just lookup the code for each node in sequence and copy/paste it together in the same way. It probably still won’t be perfect and you might end up with a whole bunch of Scope (::slight_smile: operators, but it probably wouldn’t be too difficult.

Thank you, !

Ok, plan A is not going to work… I’ve imagined that it was compiling to bytecode, but I was not sure.

Yes, for sure, we can look at the node definition in C++… but what about custom code written directly in Blueprint? Like the images attached to this thread for instance? Knowing the grammar of the Blueprint text output I could try to write a sort of translator. Do you know where can I find the grammar or anything that may help?

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.

must have tool for BP users, i hope you can make it!!!

That’s awesome , thank you for sharing this!

I was thinking in how to translate small portions of code, like in the example that I made with the images attached to this thread… in order to do that I was hoping that I would get access to any sort of guide or grammar for the Blueprint text output, but now I see that I’ll need some more research before trying this.

I’ll post here any update about this project, if I get any results for this tool in the future.

Thanks again!

Wow must practise extra care - the translated C++ code has no ‘break’ in each ‘case’ statement.

I believe they are missed intentionally.

Hi Syed,

A [FONT=Times New Roman]break; line is not required for each [FONT=Times New Roman]case statement. However, you would need to keep in mind that if the case statement does not contain a break, execution will continue into the next case statement. So, in the example above, when the Begin Play node fires, it calls [FONT=Times New Roman]ExecuteUbergraph_NewBlueprint() with a parameter value of 1. [FONT=Times New Roman]ExecuteUbergraph_NewBlueprint() creates and initializes [FONT=Times New Roman]CurrentState to 0, then starts the loop that contains the switch statement. Since [FONT=Times New Roman]CurrentState is currently 0, the [FONT=Times New Roman]case 0 statements are run. This sets [FONT=Times New Roman]CurrentState equal to the parameter that was passed in (1) and breaks out of the switch statement, causing the loop to start again (since [FONT=Times New Roman]CurrentState is not equal to -1).

The second time through the loop, [FONT=Times New Roman]CurrentState is 1, so the [FONT=Times New Roman]case 1 statements are run. [FONT=Times New Roman]Case 1 contains nothing but commented lines and no break, so it continues into the [FONT=Times New Roman]case 2 statements. In the [FONT=Times New Roman]case 2 statements, the Blueprint’s [FONT=Times New Roman]Counter variable is set to 0. There is again no break in [FONT=Times New Roman]case 2, so execution continues into the [FONT=Times New Roman]case 3 statements. In the [FONT=Times New Roman]case 3 statements, the value of [FONT=Times New Roman]Counter is converted to a string, then that string is added to the end of the existing string "Counter is set to: ". The result is then printed to the screen. There is once again no break since Begin Play runs immediately into the first Tick of the game when it completes. [FONT=Times New Roman]CurrentState is also set to -1 at this point, which will terminate the loop as soon as the switch statement ends.

The [FONT=Times New Roman]case 4 statements do the same thing as the [FONT=Times New Roman]case 1 statements: nothing. The [FONT=Times New Roman]case 5 statements increment the [FONT=Times New Roman]Counter variable, and the [FONT=Times New Roman]case 6 statements again output the string and value to the screen (there may be a logic error in [FONT=Times New Roman]case 6, it doesn’t look quite like what I had in mind). [FONT=Times New Roman]Case 6 still does not contain a break, so execution continues into [FONT=Times New Roman]default, which does have a break. The switch statement ends at this point, as does the loop.

With the next Tick, [FONT=Times New Roman]ExecuteUbergraph_NewBlueprint() is called again and the value 4 is passed into it. The first time through the loop, [FONT=Times New Roman]case 0 gets run. The next time through the loop the switch statement starts with [FONT=Times New Roman]case 4. If you have been paying attention, you will notice that at this point the code has entered an infinite loop since the only place where [FONT=Times New Roman]CurrentState is set to -1 to exit the loop is in [FONT=Times New Roman]case 3, and we never return there. That means I must provide another warning for this tool: The code provided may not actually be usable code.

For that, you may want to look at the pseudocode that is produced when you select several nodes in an event graph and paste it into a text editor. Since this pseudocode can be pasted into a different event graph and it will recreate the nodes that were originally copied, you should be able to set something up to parse through that and create actual native code.

It would be cooler if there was a “custom code” node which would let you inject C++ code directly into a blueprint graph – similar to how the material editor has a custom node which lets you inject your own custom HLSL code.

Yeah, i have actually used the Generate Native Code twice and both times it has been quite useful especially when prototyping something in bp and then wanting to see if the speed up from bp to c++ is worth the effort to fully convert from bp. Nifty tool. now only if it gave more…logical c++ code.

Although that’s probably not impossible, it will make you build times incredibly long. Effectively you’d have to get the Hot Reload system working 100% first regardless of code change, and ensure that the graph editor can load your new stuff in. In fact, this is technically possible already - but it takes forever.

With C++, you sacrifice your compile time for pure speed. Blueprint takes the opposite approach. Blueprint is only possible nowadays since CPU’s are so quick, you’d never have gotten away with it 7/8 years ago.

“pseudocode” you mean the textual representation of a Blueprint Node when you copy and paste it in a text editor? For sure, that is my plan B!

I would implement this by creating a parser that would translate that pseudo code into nodes, and after all nodes have been created it would analyse every resultant node, connecting all of them, creating this way a graph so the program can analyse the flow and operators to finally translate that into C++ code.

… but it’s all already implemented inside the editor! I did a research here in the engine’s source code and I’ve found a function called GetSelectedNodes from FBlueprintEditor class. It returns a FGraphPanelSelectionSet, which is a typedef TSet<class UObject*>, and looking at some references in BlueprintEditor.cpp I see that I can cast those objects to UK2Node, like in line 4026 (I’m using the source from 4.8.2 release).

The problem is… how can I get a reference to the active BlueprintEditor object? Of course, putting a button on the BlueprintEditor Window may solve the problem of getting this reference, but I don’t know how to use Slate properly yet. I would prefer to implement this as a plugin, instead of a built-in feature.

That may be my plan C!

Slayemin, I would prefer not to write C++ directly on Blueprint Editor, but to change the way that we visualize Blueprint Nodes in order to use them as lines of code, something more compact, mimicking C++, but still using the standard Blueprint compiler and all the standard systems behind it!

How you put a button or is a image ?

&stc=1&d=1437720499

Hi, I want develop a software in “background” .exe from visual studio, that this program will do get all data from (CTRL+C) its purpose is to get the code c ++.

Note : I need know if exist this method ? if not exist I will create and after I will convert in a plugin

Isnt it already implemented via UFUNCTION() ? You will get its visual represent in blueprint editor, but still can change how it works in your cpp code

Hello everyone!! I just created a video showcasing the start my Blueprint to C++ conversion tool! Blueprint To C++ Conversion Plugin