I’m trying to figure out a way to do tasks asynchronously in Python for Unreal Engine 5.4.
I found a tutorial which works for Blueprints (https://www.youtube.com/watch?v=_eVBKQQBW9k), where first the basis is implemted in C++ and then used in a Blueprint.
C++ header file:
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "PythonAsyncActionNode.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTaskOutput);
/**
*
*/
UCLASS()
class ASYNCPYTHON_API UPythonAsyncActionNode : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
// Property Specifier BlueprintAssignable is usable only with Multicast Delegates and exposes the property for assigning in Blueprint,
UPROPERTY(BlueprintAssignable)
FTaskOutput MultiThreadedWork;
UPROPERTY(BlueprintAssignable)
FTaskOutput FinishedWork;
// create static function marked with "BlueprintCallable" to act as constructor of our class,
// allowing to create and initialize an instance of the class from within a Blueprint graph,
UFUNCTION(BlueprintCallable, Category = "Asyncs\|PythonAsyncActionNode")
static UPythonAsyncActionNode* MultiThreadedNode();
virtual void Activate() override;
};
//============================================================================================
class BP_NonAbandonableTask : public FNonAbandonableTask
{
public:
BP_NonAbandonableTask(UPythonAsyncActionNode* BP_TaskInstance);
~BP_NonAbandonableTask();
//UE5 needs this
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(BP_NonAbandonableTask, STATGROUP_ThreadPoolAsyncTasks);
}
UPythonAsyncActionNode* CallingObject = nullptr;
void DoWork();
};
Use in Blueprint (omitting the function Find Primes as it is just for testing):
This works exactly as described in the video.
Now I want to take the leap to Python, but am stuck on how to use MultiThreadedWork and FinishedWork.
So far I have the created Python doc to the C++ code
class TaskOutput(MulticastDelegateBase):
r"""
DeclerationMacro(DelegateName)
**C++ Source:**
- **Plugin**: AsyncPython
- **Module**: AsyncPython
- **File**: PythonAsyncActionNode.h
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
...
class PythonAsyncActionNode(BlueprintAsyncActionBase):
r"""
Python Async Action Node
**C++ Source:**
- **Plugin**: AsyncPython
- **Module**: AsyncPython
- **File**: PythonAsyncActionNode.h
**Editor Properties:** (see get_editor_property/set_editor_property)
- ``finished_work`` (TaskOutput): [Read-Write]
- ``multi_threaded_work`` (TaskOutput): [Read-Write] Property Specifier BlueprintAssignable is usable only with Multicast Delegates and exposes the property for assigning in Blueprint,
see https:dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-uproperties
"""
@property
def multi_threaded_work(self) -> TaskOutput:
r"""
(TaskOutput): [Read-Write] Property Specifier BlueprintAssignable is usable only with Multicast Delegates and exposes the property for assigning in Blueprint,
see https:dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-uproperties
"""
...
@multi_threaded_work.setter
def multi_threaded_work(self, value: TaskOutput) -> None:
...
@property
def finished_work(self) -> TaskOutput:
r"""
(TaskOutput): [Read-Write]
"""
...
@finished_work.setter
def finished_work(self, value: TaskOutput) -> None:
...
@classmethod
def multi_threaded_node(cls) -> PythonAsyncActionNode:
r"""
X.multi_threaded_node() -> PythonAsyncActionNode
create static function marked with "BlueprintCallable" to act as constructor of our class,
allowing to create and initialize an instance of the class from within a Blueprint graph,
see https:store.algosyntax.com/tutorials/unreal-engine/ue5-multithreading-with-ublueprintasyncactionbase/
Returns:
PythonAsyncActionNode:
"""
...
and the implementation of the function I want to use for MultiThreadedWork and FinishedWork (find_primes and print_to_log, respectively) and the creation of an PythonAsyncActionNode:
import unreal
def find_primes():
(omitted)
def print_to_log():
(omitted)
node = unreal.PythonAsyncActionNode.multi_threaded_node()
Now I’m trying to set the editor properties
node.set_editor_property("multi_threaded_work", ... )
node.set_editor_property("finished_work", ... )
Just using the function names doesn’t work, throwing the error
LogPython: Error: node.set_editor_property("multi_threaded_work", find_primes)
LogPython: Error: TypeError: PythonAsyncActionNode: Failed to convert type 'function' to property 'MultiThreadedWork' (MulticastInlineDelegateProperty) for attribute 'multi_threaded_work' on 'PythonAsyncActionNode'
LogPython: Error: TypeError: NativizeProperty: Cannot nativize 'function' as 'MultiThreadedWork' (MulticastInlineDelegateProperty)
I tried using TaskOutput (even though it didn’t seem approproriate):
node.set_editor_property("multi_threaded_work", unreal.TaskOutput(find_primes, None))
node.set_editor_property("finished_work", unreal.TaskOutput(print_to_log(), None))
which led to the following error:
LogPython: Error: node.set_editor_property("multi_threaded_work", unreal.TaskOutput(find_primes, None))
LogPython: Error: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LogPython: Error: TypeError: TaskOutput: Failed to convert argument 0 (function) to 'Object'
LogPython: Error: TypeError: NativizeObject: Cannot nativize 'function' as 'Object' (allowed Class type: 'Object')
How do I set these properties to the respective functions?