c++loopsnodesunreal-engine4unreal-blueprint

Blueprint loop/for/while node with custom c++ blueprint function library in unreal engine 4 (ue4)


I need to create a custom blueprint node. I am using the blueprint function library.

The node will look like this:

Input: int timedelayforeachloop int numberofloops

output: exc loop exc completed

loop1.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "loop1.generated.h"

/**
 * 
 */

UENUM(BlueprintType)
enum class EMultiBranchEnum1 : uint8
{
    BranchA,
    BranchB
};


UCLASS()
class MYPROJECT2_API Uloop1 : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
        UFUNCTION(BlueprintCallable, meta = (DisplayName = "loop", CompactNodeTitle = "2as2", ExpandEnumAsExecs = "Branches"), Category = "1")
        //UFUNCTION(BlueprintCallable, Meta = (DisplayName = "Multi Branch1", ExpandEnumAsExecs = "Branches"), Category = 1)
        static void multiBranch(EMultiBranchEnum1& Branches, int loopqty);
        //EMultiBranchEnum1::BranchB;

};

loop1.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "loop1.h"

void Uloop1::multiBranch(EMultiBranchEnum1& Branches, int loopqty)
{

    int currloop1 = 0;
    int temp = 2;
    int i;
    for (i = 0; i < 10; i++){
        currloop1 = currloop1 + 1;
        Branches = EMultiBranchEnum1::BranchA;


    }

    if (temp > currloop1) {

        Branches = EMultiBranchEnum1::BranchB;
    }

    if(temp == 0) {

        Branches = EMultiBranchEnum1::BranchB;

    }


}

-- THE PROBLEM -- The for loop only runs the once (evident by the print node i have on branchA(It only prints a single time))

-- What should happen with the code below -- the loop should run the 10 times (my print node should print 10 times)


Solution

  • Instead of using UBlueprintFunctionLibrary, you should use UBlueprintAsyncActionBase. It will allow you to store state in the node and call things connected to the execution pins asynchronously.

    DelayLoop.h file:

    #include "CoreMinimal.h"
    #include "Kismet/BlueprintAsyncActionBase.h"
    #include "DelayLoop.generated.h"
    
    DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDelayOutputPin);
    
    /**
     * 
     */
    UCLASS()
    class TEST_API UDelayLoop : public UBlueprintAsyncActionBase
    {
        GENERATED_UCLASS_BODY()
    
    public:
        UPROPERTY(BlueprintAssignable)
        FDelayOutputPin Loop;
    
        UPROPERTY(BlueprintAssignable)
        FDelayOutputPin Complete;
    
        UFUNCTION(BlueprintCallable, 
                meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"), 
                Category = "Flow Control")
        static UDelayLoop* DelayLoop(const UObject* WorldContextObject, 
                const float DelayInSeconds, const int Iterations);
    
        virtual void Activate() override;
    
    private:
        const UObject* WorldContextObject;
        float MyDelay;
        int MyIterations;
        bool Active;
    
        UFUNCTION()
        void ExecuteLoop();
    
        UFUNCTION()
        void ExecuteComplete();
    };
    

    DelayLoop.cpp file:

    #include "DelayLoop.h"
    #include "Engine/World.h"
    #include "TimerManager.h"
    
    UDelayLoop::UDelayLoop(const FObjectInitializer& ObjectInitializer) : 
            Super(ObjectInitializer), WorldContextObject(nullptr), MyDelay(0.0f), 
            MyIterations(0), Active(false)
    {
    }
    
    UDelayLoop* UDelayLoop::DelayLoop(const UObject* WorldContextObject, 
            const float DelayInSeconds, const int Iterations)
    {
        UDelayLoop* Node = NewObject<UDelayLoop>();
        Node->WorldContextObject = WorldContextObject;
        Node->MyDelay = DelayInSeconds;
        Node->MyIterations = Iterations;
        return Node;
    }
    
    
    void UDelayLoop::Activate()
    {
        if (nullptr == WorldContextObject)
        {
            FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject."), 
                    ELogVerbosity::Error);
            return;
        }
        if (Active)
        {
            FFrame::KismetExecutionMessage(TEXT("DelayLoop is already running."), 
                    ELogVerbosity::Warning);
        }
        if (MyDelay <= 0.0f)
        {
            FFrame::KismetExecutionMessage(
                    TEXT("DelayLoop delay can't be less or equal to 0."), 
                    ELogVerbosity::Warning);
        }
        if (MyIterations <= 0)
        {
            FFrame::KismetExecutionMessage(
                    TEXT("DelayLoop iterations can't be less or equal to 0."), 
                    ELogVerbosity::Warning);
        }
    
        Active = true;
        for (int i = 0; i <= MyIterations; i++)
        {
            FTimerHandle IterationTimer;
            WorldContextObject->GetWorld()->GetTimerManager().SetTimer(
                    IterationTimer, this, &UDelayLoop::ExecuteLoop, MyDelay * i);
        }
    
        FTimerHandle CompleteTimer;
        WorldContextObject->GetWorld()->GetTimerManager().SetTimer(
                CompleteTimer, this, &UDelayLoop::ExecuteComplete, 
                MyDelay * (MyIterations+1));
                // If the Complete pin should happen at the same time as the last iteration
                // use `MyDelay * MyIterations` here instead
    
    }
    
    void UDelayLoop::ExecuteLoop()
    {
        Loop.Broadcast();
    }
    
    void UDelayLoop::ExecuteComplete()
    {
        Complete.Broadcast();
        Active = false;
    }
    

    This will get you a blueprint that looks like this:

    example use of blueprint

    Note: This code is heavily based on This Creating Asynchronous Blueprint Nodes guide by Daniel ~b617 Janowski, now hosted in the legacy wiki here