How to keep widgets on screen after changing levels

How can you access in blueprint FGameViewportWidgetSlot in order to set bAutoRemoveOnWorldRemoved to false for a UserWidget?

If it is not accessible in blueprint, it is fine if you must do this in C++, but the same question stands except the part with “blueprint”.

Straight in blueprint I tried this in game instance, but it doesn’t work, the widget doesn’t remain after level is changed:

//Edit: It does work, I just had something in my blueprints that was removing it automatically when the new level was done loading and also all the functions were done.

But see the solution for it is better.

.h

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

#pragma once

#include "CoreMinimal.h"

#include "Kismet/BlueprintFunctionLibrary.h"
#include "StaticUserWidget.generated.h"

class UUserWidget;

/**
 * 
 */
UCLASS()
class YOUR_API UStaticUserWidget : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
	UFUNCTION(BlueprintCallable)
	static void MakeStatic(UUserWidget * Widget);

};

.cpp

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


#include "StaticUserWidget.h"
#include "Blueprint/GameViewportSubsystem.h"
#include "Blueprint/UserWidget.h"



void UStaticUserWidget::MakeStatic(UUserWidget* Widget)
{
	if (Widget != nullptr) 
	{
		UWorld* World = Widget->GetWorld();
		if (World)
		{
			if (UGameViewportSubsystem* Subsystem = UGameViewportSubsystem::Get(World))
			{
				FGameViewportWidgetSlot GVS = Subsystem->GetWidgetSlot(Widget);
				GVS.bAutoRemoveOnWorldRemoved = false;
				Subsystem->SetWidgetSlot(Widget, GVS);
			}
		}
	}
}

Normal empty scene => loaded level, widget persists

1 Like

Actually I am dumb and what I posted does work :sweat_smile:

The problem was that before I knew about his, I had a different system and when the new map was loaded and after some functions were also done I was removing automatically the loading widget, and before I was calling KeepWidget from Game Instance, I was also setting it as loading widget.

Meaning something like this:

And my game is automatically removing LoadingW when it knows the player can now control its character.

First time when I tried it was in C++, and I did like you, BUT in C++ I forgot to call Subsystem->SetWidgetSlot().

A lot more code and bp compared to a single static library call. No game instance or cast required.

Oh, I didn’t say that what you did is wrong, when it comes to it, the C++ variant is better for C++/Hybrid project, I just made it clear that you can do it in blueprints only too.

Oh ok. Well good that you were able to get it running.

For projects using Blueprints only, you need to create a function (preferably in a Blueprint Function Library so it can be accessed from anywhere), and then call that function after you add the widget to viewport:

The input for the function is of type Widget.

If you want, you can also make a macro for it:

Now for projects that are hybrid, meaning you don’t mind adding some C++ functions for your Blueprint project you can add in an existing or new Blueprint Function Library the following:

In the .h file of the Blueprint Function Library:

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

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Blueprint/GameViewportSubsystem.h" //You need to add this
#include "Library_Name.generated.h"

/**
 *
 */
UCLASS()
class YOUR_GAME_API ULibrary_Name : public UBlueprintFunctionLibrary
{
	UFUNCTION(BlueprintCallable, Category = "Your_Category_Name")
	static bool KeepWidget(UWidget* widget);
};

And now in .cpp file of the Blueprint Function Library:

#include "Library_Name.h"

bool ULibrary_Name::KeepWidget(UWidget* Widget)
{
	if (Widget)
	{
		if (Widget->GetWorld())
		{
			if (UGameViewportSubsystem* Subsystem = UGameViewportSubsystem::Get(Widget->GetWorld()))
			{
				FGameViewportWidgetSlot ViewportSlot = Subsystem->GetWidgetSlot(Widget);
				ViewportSlot.bAutoRemoveOnWorldRemoved = false;
				Subsystem->SetWidgetSlot(Widget, ViewportSlot);
				return true;
			}
		}
	}
	return false;
}

You can make this function void if you want, or any other type for your check in case of a problem. I choose bool, if it returns true then everything is fine and the widget will remain even after changing levels, but if it returns false there was a problem. For a more complete function with checks you can add for each if else block a different returned value so that you know what was the problem.

1 Like

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