How to call OpenLevel Function using SetTimer?

I want to call UGameplayStatics::OpenLevel(const UObject* WorldContextObject, FName LevelName, bool bAbsolute, FString Options) using SetTimer, but I am unable to do so. I keep getting errors while defining FTimerDelegate. If you know how I can call the OpenLevel function after a given period of time, then please tell me.

auto Delegate = FTimerDelegate::CreateLambda([this] { UGameplayStatics::OpenLevel(this, ...); });

FTimerHandle TimerHandle;
FName Level2_Fname = FName(*Level2);

FTimerDelegate Delegate = FTimerDelegate::CreateLambda([this] { UGameplayStatics::OpenLevel(this, Level2_Fname); });
GetWorldTimerManager().SetTimer(TimerHandle, Delegate, 4.f, false);

This code is throwing error.

Error (When passing Level2_Fname to delegate) :

FName Level2_Fname

an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture listC/C++(1735)
error C3493: 'Level2_Fname' cannot be implicitly captured because no default capture mode has been specified

This code is equivalent to

struct CompilerGeneratedName
{
    AActor* Capture; // if `this` is AActor*
    CompilerGeneratedName(AActor* Cap) : Capture(Cap) {} // constructor
    void operator() const
    {
         UGameplayStatics::OpenLevel(this, Level2_Fname); // What is Level2_Fname?
    }
};
CompilerGeneratedName Lambda;
FTimerDelegate Delegate = FTimerDelegate::CreateLambda(Lambda);

So you would need to add Level2_Fname to the capture list

FTimerDelegate Delegate = FTimerDelegate::CreateLambda([this, Level2_Fname] { UGameplayStatics::OpenLevel(this, Level2_Fname); });

This code is now working, but the problem is that as it opens the new level, the Unreal Engine crashes.

Where do you have this code?

I am actually making the simple shooter by adding some extra features. I wants to load level2 after 5sec as the player win level 1.

ComplexShooterPlayerController.h :

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ComplexShooterPlayerController.generated.h"

/**
 * 
 */
UCLASS()
class COMPLEXSHOOTER_API AComplexShooterPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	void GameHasEnded( AActor* EndGameFocus, bool bIsWinner) override;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

private:

	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<class UUserWidget> HUD;
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<class UUserWidget> HUD2;

	UPROPERTY()
	UUserWidget* HUDWidget;

	UPROPERTY(EditAnywhere)
	TSubclassOf<class UUserWidget> WonLevel1;

	UPROPERTY(EditAnywhere)
	TSubclassOf<class UUserWidget> WonLevel2;

	UPROPERTY(EditAnywhere)
	TSubclassOf<class UUserWidget> Lost;

	FString CurrentLevel;

	UPROPERTY(EditAnywhere)
    FString MainMenuLevel = TEXT("MainMenuLevel");

	UPROPERTY(EditAnywhere)
    FString Level1 = TEXT("Map1");

	UPROPERTY(EditAnywhere)
    FString Level2 = TEXT("Demonstration");
};

ComplexShooterPlayerController.cpp :

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


#include "ComplexShooterPlayerController.h"
#include "Blueprint/UserWidget.h"
#include "Kismet/GameplayStatics.h"
#include "TimerManager.h"

void AComplexShooterPlayerController::BeginPlay()
{

    CurrentLevel = UGameplayStatics::GetCurrentLevelName(this, true);

    if(CurrentLevel == Level1 && HUD)
    {
        HUDWidget = CreateWidget(this, HUD);
    }
    else if(CurrentLevel == Level2 && HUD2)
    {
        HUDWidget = CreateWidget(this, HUD2);
    }
    
    if(HUDWidget)
    {
        HUDWidget->AddToViewport();
    }
}

void AComplexShooterPlayerController::GameHasEnded(AActor* EndGameFocus, bool bIsWinner)
{
    Super::GameHasEnded(EndGameFocus, bIsWinner);

    FTimerHandle TimerHandle;

    if(HUDWidget)
    {
        HUDWidget->RemoveFromViewport();
    }

    if(bIsWinner && CurrentLevel == Level1 && WonLevel1)
    {
        UUserWidget* WonWidget = CreateWidget(this, WonLevel1);
        if(WonWidget)
        {
            WonWidget->AddToViewport();
        }

        FName Level2_Fname = FName(*Level2);

        
>      FTimerDelegate Delegate = FTimerDelegate::CreateLambda([this, Level2_Fname] { UGameplayStatics::OpenLevel(this, Level2_Fname); });**
>      GetWorldTimerManager().SetTimer(TimerHandle, Delegate, 4.f, false);

        // UGameplayStatics::OpenLevel(this, Level2_Fname);
    }
    else if (bIsWinner && CurrentLevel == Level2 && WonLevel2)
    {
        UUserWidget* WonWidget2 = CreateWidget(this, WonLevel2);
        if(WonWidget2)
        {
            WonWidget2->AddToViewport();
        }

        FName MainMenuFname = FName(*MainMenuLevel);

>      FTimerDelegate Delegate = FTimerDelegate::CreateLambda([this, MainMenuFname] { UGameplayStatics::OpenLevel(this, MainMenuFname); });
>      GetWorldTimerManager().SetTimer(TimerHandle, Delegate, 4.f, false);

        // UGameplayStatics::OpenLevel(this, MainMenuFname);
    }
    else if (!bIsWinner && Lost)
    {
        UUserWidget* LostWidget = CreateWidget(this, Lost);
        if(LostWidget)
        {
            LostWidget->AddToViewport();
            GetWorldTimerManager().SetTimer(TimerHandle, this, &APlayerController::RestartLevel, 5.f, false); //setting timer to restart the level after five seconds of lossing the game
        }
    }
}

And that’s both scenarios crashing? Might be better to just use the world directly as the player controller might be gone by the time timer ends.

[World = GetWorld(), ...] { UGameplayStatics::OpenLevel(World, ...); }

P.S. Why store the names in FString if all you’re doing is create an FName out if it. Why not use FName directly?

Still, the Unreal Engine is crashing, as it opens the next level.

**I stored the names in FString because in the code there are some places where I need to compare the names of the level to other FString. **

Code :

FName Level2_Fname = FName(*Level2);
FTimerDelegate Delegate = FTimerDelegate::CreateLambda([World = GetWorld(), Level2_Fname] { UGameplayStatics::OpenLevel(World, Level2_Fname); });
GetWorldTimerManager().SetTimer(TimerHandle, Delegate, 4.f, false);

@DanM

Sorry, I was going to test this myself yesterday but didn’t get round to it as there were a lot of questions. I’m a bit busy today but hopefully I’ll find time in this evening.

No problem. Try this when you get some time. :grin:

It worked fine without crashing. I think it would be a good idea to install the editor symbols so you get the name of the engine function in the crash report. You can install them from the options menu.
options

I don’t know how, but after installing editor symbols, the code is working perfectly fine.

But as I want to ask you what the work of editor symbols is and how to use them, I also tried to search for any videos explaining editor symbols but found none, so if you could attach a link to any video explaining editor symbols briefly or explain it to me in your own words, that would be great for me. And thanks a lot for your help in resolving the issue. :grin:

That is very unlikely to be what fixed your issue, you probably just needed to rebuild or verify your engine.

Installing the editor symbols would just give you debug symbols for the engine and editor. Meaning the output in your screenshot, instead of saying “UnrealEditor”, “UnrealEditor_Engin” etc. it’ll tell you what functions those actually are. Just like the one line in the output that says “UnrealEditor_ComplexShooter…” because you have the debug symbol for that.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms