Download

Accepted answer (in UE4 answerhub) to: "How can I create Blueprints with multiple exec output pins in C++?" Does not work for multi exec + data?

I am trying to add a “better for loop” in my blueprint function library.
I came across the question in the title, and implemented the following psuedo-code from the answer from that question:

 // .h
 
 UENUM(BlueprintType)
 enum class EMyEnum : uint8
 {
     BranchA,
     BranchB
 };
 
 UFUNCTION(BlueprintCallable, Category = "Stuff", Meta = (ExpandEnumAsExecs = "Branches"))
 void DoSomeBranch(int32 SomeInput, EMyEnum& Branches)
 
 
 // .cpp
 
 void AMyClass::DoSomeBranch(int32 SomeInput, EMyEnum& Branches)
 {
     if (SomeInput == 1)
     {
         Branches = EMyEnum::BranchA;
     }
     else
     {
         Branches = EMyEnum::BranchB;
     }
 }

It works perfectly, however when I tried to implement it as a “better for loop” like so:

// .h

UENUM(BlueprintType)
enum class EmultiExecutionHandler : uint8
{
	Loop_Body,
	Completed
};

UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = handled, Category = "Utilities|Flow Control", DefaultToSelf, HideSelfPin, DisplayName = "MEH_Cfor", KeyWords = "m,me,meh,mehc,mehcf,mehcfo,mehcfor,cfor,for,loop"))
void mehCfor(int32 iterStart, int32& index, int32 comparison, int32 iterEnd, bool positive, int32 amount, EmultiExecutionHandler& handled);


// .cpp

void UMyBlueprintFunctionLibrary::mehCfor(int32 iterStart, int32& index, const int32 comparison, const int32 iterEnd, const bool positive, const int32 amount, EmultiExecutionHandler& handled)
{
	if(positive)
	{
		if(comparison == 0)
			for(iterStart; iterStart == iterEnd; iterStart += amount) {
				if(iterStart != iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				if(iterStart == iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 1)
			for(iterStart; iterStart <= iterEnd; iterStart += amount) {
				if(iterStart > iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 2)
			for(iterStart; iterStart >= iterEnd; iterStart += amount) {
				if(iterStart < iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 3)
			for(iterStart; iterStart < iterEnd; iterStart += amount) {
				if(iterStart >= iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 4)
			for(iterStart; iterStart > iterEnd; iterStart += amount) {
				if(iterStart <= iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 5)
			for(iterStart; iterStart != iterEnd; iterStart += amount) {
				if(iterStart == iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
	}
	else
	{
		if(comparison == 0)
			for(iterStart; iterStart == iterEnd; iterStart -= amount) {
				if(iterStart != iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 1)
			for(iterStart; iterStart <= iterEnd; iterStart -= amount) {
				if(iterStart > iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 2)
			for(iterStart; iterStart >= iterEnd; iterStart -= amount) {
				if(iterStart < iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 3)
			for(iterStart; iterStart < iterEnd; iterStart -= amount) {
				if(iterStart >= iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 4)
			for(iterStart; iterStart > iterEnd; iterStart -= amount) {
				if(iterStart <= iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
		if(comparison == 5)
			for(iterStart; iterStart != iterEnd; iterStart -= amount) {
				if(iterStart == iterEnd)
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Loop_Body;
				}
				else
				{
					index = iterStart;
					handled = EmultiExecutionHandler::Completed;
				}
			}
	}
}

It seems to either loop always on zero if zero is within the range, or it never resolves to “handled = EmultiExecutionHandler::Loop_Body;” and instead always returns with “handled = EmultiExecutionHandler::Completed;” even when used like the standard for loop.

Any help much appreciated!

The issue that you’ve encountered is because functions are only ever evaluated once. Even when they have multiple exec outs like your first example, the best a function can do is choose one to activate and then be done. They have no way to to be re-executed and choose a different output.

If you look at the macro used for loops, you’ll notice that they involve a physical loop in the exec at some point to return control back to the loop after the body has been completed. You just can’t do that from within a single function.

That’s the bad news. The good news is that there is a C++ solution should you be so inclined and that is Custom K2 Nodes. There’s two flavors of them, the easier one basically allows you to create node macros from C++ and do all sorts of amazing things. I built a For-Each node that worked for Maps (the container, not the asset) which was basically impossible to do from the blueprint macro editor.

Early this year I wrote a tutorial Improving UE4 Blueprint Usability with Custom Nodes that tried to explain most of the knowledge I had gleaned since the existing docs and tutorials were basically non-existent.

I would be curious to know what you feel needs to be improved with the for-loop. It’s never really struck me as a node that has any real problems.

Thank you so much for the quick, informative reply!

Unfortunately I am in a bit of a time crunch right now, and I’ve found a different way to accomplish what I want, but in a short while I will have more time to go over what you’ve written regarding K2 nodes, because I believe it will be a more elegant solution.
As far as I know there is no way to have the default blueprint node for loop handle decrementing, which is what I need in this case.

Thanks to your comment I did just realize that I can copy the blueprint for loop macro and just edit it, that is probably the easiest solution in my case!