In this article, we'll see how higher-order/functional programming can be done with Blueprint on Unreal Engine 4.
Actor as an Environment
Create a new Actor Blueprint.
Open the Level Blueprint and spawn an instance of the Actor class on the BeginPlay event.
You can put an instance of the actor directly on the level, but every time modifying the Actor Blueprint, the level also somehow updated. This is annoying for source control.
Event as a Function
We use Events to represent functions. You can also use Blueprint functions, but in this article we consider events as functions.
Let's create a Hello World!
Another example, Celsius to Fahrenheit converter, can be like this:
Returning Value with Another Event
As seen above, events can behave like functions. But unlike functions, an event can't return a value.
To pass some value from the function to the caller, let's create an another event to receive the result from a function.
Then, the Celsius to Fahrenheit converter with returned value can be like below:
Recursive Call
An event doesn't have variable scope, it's not possible to call an event recursively without using variables.
Let's see a factorial function implemented in JavaScript:
function fact(x) {
if (x == 1) {
return 1
}
return x * fact(x - 1)
}
In this example, fact()
is called from inside fact()
function. With JavaScript, the value of x
is stored in the call stack. So, each x
in a different level can hold different value.
To emulate this, we can create another Actor instance to represent another stack frame.
Before implementing a Blueprint version of factorial function, let's create a utility function, a real Blueprint function, to spawn an instance.
With this function, let's make Factorial function.
Continuation Passing Style
On above examples, we had used an event to receive a returned value from a function (event). But this is not very flexible. The factorial function can't return the result to the top level caller.
To make this more flexible, we can use event dispatchers. With an event dispatcher, we can specify different kind of events to handle returned value accordingly.
Let's see the Fibonacci function. It can be written in JavaScript like below:
function fib(x) {
if (x < 2) {
return x
}
return fib(x - 1) + fib(x - 2)
}
In this code, fib()
is called twice. Therefore the returned value from each of them needs to be handled differently.
In Blueprint, it can be implemented like below:
Events result_Fibonacci1
and result_Fibonacci2
handle the returned value from each inner function call.
Additionally, you can simplify this graph a little bit by "Collapse to Function".
Note that if you collapse some nodes including binding an event to a event dispatcher, then the generated function will have a Delegate input.
Below is the result:
With an event dispatcher and events to handle the returned value, we can code a function with continuation passing style.
Map
A function which maps an array to an another array.
For loop.
(TBD)
Fold
A function which accumulates all the values in an array into a value.
(TBD)
Top comments (2)
Some things are best done in code. If you're running a blueprint "function" that requires spawning an otherwise useless Actor, you've probably found something that is far better done in code than in Blueprint.
Yeah, this is an experiment to see the limit of Blueprint. I’m also a programmer, so it’s much easier to code in C++ to me:)