Interruptable Latent Blueprint Nodes

Hi guys!

I’m currently trying to build a new latent blueprint node that has a number of features. The node should have multiple outputs - for the sake of example, one that fires once the task the node has completed it’s action (e.g. playing a sound), and one that fires if the node has been interrupted by a condition having been met (e.g. a keypress). It’d be good if the condition were to appear much like an input.

I’m a little unsure of how to go about this, or even how feasible it may be as it’s not a typical BP setup (though was easy to do in Kismet). How would people approach this problem?

You can do something like this with a BP function, not a node though. Might be interesting, kind of like Yield or a Generator in JS. You may be able to construct the functions for this using a mixture of events, although I’m not sure how well that would work to meet your needs - BP Events into a function before you could use it as you wish.

All nodes in blueprint are extention of UK2Node

So if you want to go beyond of UFUNCTION borders this is something for you

you might try to use Tick too

Thanks Bob - what do you mean by a BP ‘function’? I’m not familiar with the term. I had been looking at extending from K2Node_BaseAsycTask - but the more I looked at what I was doing, the more awkward to implement it was beginning to seem. It doesn’t seem like it’s easy to have an interruptable latent node. I’m considering just creating an object with functions instead…

I used an enum class ref and ExpandEnumAsExecs.

UENUM(BlueprintType)
enum class OUT_EXEC : uint8
{
    Completed,
    Interrupted
};

Your latent function could look like:

UFUNCTION(BlueprintCallable, Category="MyCategory", meta=(ExpandEnumAsExecs="exec", Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", interupt="false"))
void Generate_async(UObject* WorldContextObject, UPARAM(ref) bool interrupt, AActor* Some_actor, OUT_EXEC& exec, struct FLatentActionInfo LatentInfo);

then you would pass interrupt and exec reference as well as Some_actor pointer down to your subclass of FPendingLatentAction. Your UpdateOperation(FLatentResponse& Response) implementation could look something like this:

virtual void UpdateOperation(FLatentResponse& Response) override
{
    // Do stuff with Some_actor. Maybe an iteration that takes a long time.
    Some_actor->DoWork(iter++);
	bool finish_flag = Some_actor->WorkIsCompleted();
	if (finish_flag)
	{
            exec = OUT_EXEC::Completed;
	}
    else if (interrupt) // don't run this if finish_flag is already true
    {
           Some_actor->CancelWork();
           exec = OUT_EXEC::Interrupted;
    }
	Response.FinishAndTriggerIf(finish_flag || interrupt, ExecutionFunction, OutputLink, CallbackTarget);
}

Now when you want to interrupt this, you just need to set the bool variable that you passed in as a reference to true. A down side of this is that you can only call latent nodes from an event graph so you either have a bunch of boolean variables in your blueprint or you can wrap all this up in a separate object that can be created and disposed of when it’s all done.

Here is an image to help me explain how it works.

That Generate Async node is a latent node that I created using what I explained above. This particular node will pass information off to a thread pool if the pool is not too full. If it is too full it sets my OUT_EXEC exec to Failed and triggers the response, otherwise it adds the work to the pool and when the work is completed sets the OUT_EXEC exec to Success and triggers the response. As a result, the appropriate output exec pin gets called.

2 Likes

Thanks for this, I’m sure someone might find it useful - of course this thread is now actually over two years old and things have changed a lot since it was originally posted :wink:

ha ha that’s funny! I didn’t even pay attention to the date. I had just finished a lot of work with latent nodes and figured I’d go to the answer hub and answer questions about them. Hopefully this will help someone.

Thank you for this post. Very, very useful and remember you can call to:

exec = OUT_EXEC::AnotherValue;
Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget);

when you don’t want finish the latent action yet

Greetings