hi everyone, I was wondering how the blueprint system works in run time. does ue create c++ equivalents of those nodes and their flow ? or are these nodes contained as an object and controlled by a runtime service or a context in c++ ? how does ue know which nodes are available ? i cant seem to find a proper documentation.
Blueprint is a VM, just like Scratch.
any details ? whats inside the VM ?
The majority of the nodes are just C++ functions that you can call from BP, just like you would expect from a “regular” in-game scripting language (for example, Lua).
So as roshan said, BP is bytecode script which runs in a VM. Its not too surprising that you can’t locate documentation for this, as I don’t imagine there would be (outside of maybe UDN, Iunno, I don’t have access). That being said, you can start putting it together yourself if you’re keen on digging through source (actually the best way to learn new things about UE4 IME).
I’d strongly recommend picking up Visual Assist X (VAX) - https://wholetomato.com/ - as even using the eval version will greatly help you.
First, your BP will go through compilation.
Any collapsed graphs or macro nodes are “expanded”, meaning the compiler basically pastes the inner graph on top of where the macro/graph node would be, and connects the pins “through”. Several function nodes actually expand as well (such as spawn actor). Any impure node data input pin which uses pure nodes will result in that pure node chain being “copied” onto itself - there are multiple calls, one for each impure node which “needs” the result.
There’s more to it than that, of course, but a fine place to get started is
Note that expansion is a step that occurs as a virtual call to the K2_Node itself, meaning the actual specifics are scattered about in various K2Node files.
Now that your BP has undergone compilation, it exists as a stream of … well … bytes. More specifically, opcodes and their parameters. If you go take a look at
you’ll be able to see a lot of
IMPLEMENT_VM_FUNCTION macros in use. These are the operations which run for a given opcode. All of these functions end up in a global jump table, and the opcode is the index/offset into that table.
Functions that are jumped into in this fashion can use the FFrame type (passed as a parameter) to do things like pull their parameters and signal errors. You can see a lot of handy macros for interacting with this type in
One of these functions is generated for any BlueprintCallable native function, and these are called thunks. You can provide your own thunk if needed, however unless you’re doing particularly advanced things you shouldn’t ever need to do this. The array/map/set libraries use custom thunks, for instance.
So, to summarize. The compiler takes your nodes and puts the instructions they represent into a byte buffer, which is then used at runtime to call into functions which know how to read the byte stream the compiler creates.
if you look at “FKCHandler_ExecutionSequence.Compile”, there’s a really good example of emitting instructions.