Implementing "OnLevelRemovedFromWorld"

Hi, I’m going to share my implementation of this lecture because Sam did an edit later on and talked about a better approach using UUserWidget::OnLevelRemovedFromWorld. I’m doing this in case someone is struggling. So here it is:

#include "MyUserWidget.h"
#include "MenuInterface.h"
#include "Components/Button.h"

bool UMyUserWidget::Initialize()
{
	bool Succes = Super::Initialize();
	if (!Succes) return false;

	if (!ensure(Host != nullptr)) return false;
	Host->OnClicked.AddDynamic(this, &UMyUserWidget::HostServer);

	return true;
}

void UMyUserWidget::SetMenuInterface(class IMenuInterface* MenuInterface)
{
	this->MenuInterface = MenuInterface;
}

void UMyUserWidget::Setup()
{
	//Add Widget to viewport
	this->AddToViewport();

	UWorld* World = GetWorld();
	if (!ensure(World != nullptr)) return;

	//Get hold of the player controller
	PlayerController = World->GetFirstPlayerController();
	if (!ensure(PlayerController != nullptr)) return;

	//Setting the input mode so it's able to interact only with the UI
	FInputModeUIOnly InputModeData;
	InputModeData.SetWidgetToFocus(this->TakeWidget());
	InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);

	PlayerController->SetInputMode(InputModeData);

	//Makes the mouse cursor show on the screen
	PlayerController->bShowMouseCursor = true;
}

//Method called when the world is being destroyed
//Override method, change the input mode to game only and hides the mouse cursor
void UMyUserWidget::OnLevelRemovedFromWorld(ULevel * InLevel, UWorld * InWorld)
{
	Super::OnLevelRemovedFromWorld(InLevel, InWorld);

	FInputModeGameOnly InputModeData;

	PlayerController->SetInputMode(InputModeData);
	PlayerController->bShowMouseCursor = false;
}

void UMyUserWidget::HostServer()
{
	///UE_LOG(LogTemp, Warning, TEXT("Hosting Server!"));

	MenuInterface->Host();
}

The above chunk of code is placed in the MainMenu.cpp. As you can see, implementing the UUserWidget::OnLevelRemovedFromWorld method is the only thing neccesary to add to make this work as expected.

Greetings! Keep the momentum going!

7 Likes

Would it have made sense to put our setup() code inside Init() and getting rid of the setup() method as well?

Thanks for sharing this. It’s great!

@sgyffy, don’t know if it would work from within Init. have you had a go?

@sampattuzzi I just tired it. I Copied everything from Setup() into Initialize() and removed Setup() entirely and the menu works flawlessly.

This is as of lecture 34, I have no idea if it will cause issues later on, but I highly doubt it.

This all comes down to choices really: either keep control of when the menu should be setup and teardown by having the 2 function and calling them as needed, or just let the menu decide on its own what to do when.

In our case I don’t see a case where we would want to create an instance of the menu but not display it right away, so I’m going with Initialize() and OnLevelRemovedFromWorld().

Since you edited the video to mention OnLevelRemovedFromWorld, I belive you should also mention the possibility of getting rid of Setup.

That’s really good to know.

@sampattuzzi I just finish this section. There was an issue with using OnLevelRemovedFromWorld().

The issue is that when we start implementing the InGameMenu, when we want to remove the InGameMenu, we need to call TearDown(), however, I have no such method.

So what I did is, I moved all the code from OnLevelRemovedFromWorld() to RemoveFromParent() (remember to call the Super class !).

All OnLevelRemovedFromWorld() does by default in the engine, is calling RemoveFromParent() and I bind my “ExitButton” button to call RemoveFromParent directly.

Now both of my Menus are working without me ever needing to call a Setup() or TearDown() method. They are auto setup’ed in Initialize() and cleaned up when Removed from parent.

1 Like

Nice one, thanks for sharing this tit-bit.

I did get get a hard crash with the player controller returning nulll with the remove from parent methoid on startup. Dont know why that methoid was being called but an ensure return stoped the crash and the program functioned as designed after.

Hey could you share your code please?
I m trying to use Teardown() and it didnt work for now, so Im gonna implement this OnLevelRemovedFromWorld()
THank you

Works nicely. Here is my MainMenu.h file declaration:

public:
	void SetMenuInterface(IMenuInterface* MenuInterface);

	void Setup();

	virtual void OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld) override;
private:
	UPROPERTY(EditAnywhere, meta = (BindWidget))
	class UButton* HostButton;

	UPROPERTY(EditAnywhere, meta = (BindWidget))
	class UButton* JoinButton;

	UFUNCTION()
	void HostServer();

	IMenuInterface* MenuInterface;

protected:
	virtual bool Initialize();
};

Make sure you remove the TearDown(); code from the GameInstance.cpp file. Also, not getting the PlayerController kept giving errors so I’m not sure how @Edgar_Hernandez didn’t get errors for not getting the PlayerController again. I just added the World and PlayerController bit of code to my function.

My implementation of MainMenu.cpp:

void UMainMenu::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
{
	Super::OnLevelRemovedFromWorld(InLevel, InWorld);

	UWorld* World = GetWorld();
	if (!World) { return; }
	APlayerController* PlayerController = World->GetFirstPlayerController();
	if (!PlayerController) { return; }

	FInputModeGameOnly InputModeData;

	PlayerController->SetInputMode(InputModeData);
	PlayerController->bShowMouseCursor = false;
}
1 Like

Privacy & Terms