NativeOnInitialized may replace Initialize and Setup

@sampattuzzi
In this and the previous lecture, we implemented “Initialize” and “Setup” in the main menu widget.
I was looking around in the source code of UE and found the function “OnInitialized”. According to the docs:

Called once only at game time on non-template instances. While Construct/Destruct pertain to the underlying Slate, this is called only once for the UUserWidget . If you have one-time things to establish up-front (like binding callbacks to events on BindWidget properties), do so here.

So it seems that the best place to put our init code along with the setup code should be done here. But, Unreal being Unreal, this function is meant to be implemented in blueprints. Also, it is not virtual so we can not override it. However, there is another, undocumented (again, Unreal being Unreal) function called NativeOnInitialized.
Looking at the source code, we find that the NativeOnInitialized function is virtual, and also calls OnInitialized. I tried overriding this, placing all init code (with the dynamic bindings) along with the code from Setup here, and it seems to work. This way, the GameInstance never has to call Setup. Instead, the menu widget takes care of it all (except that we have to set the menu interface).
I think this is a really clean solution with very little coupling between game instance and the menu.

For completion, this is my function in MainMenu.cpp:

void UMainMenu::NativeOnInitialized()
{
	Super::NativeOnInitialized();

	LOG("Setting up bindings");

	if (Host)
	{
		// OnClicked does not take any arguments according to source code
		Host->OnClicked.AddDynamic(this, &UMainMenu::HostServer);
	}

	if (Join)
	{
		Join->OnClicked.AddDynamic(this, &UMainMenu::JoinServer);
	}

	LOG("Setting up mouse behaviour and input");

	// Show mouse cursor, taken from https://docs.unrealengine.com/en-US/Engine/UMG/HowTo/CreateMainMenu/index.html
	// This is also possible: APlayerController *PC = UGameplayStatics::GetPlayerController(this, 0);
	UWorld* World = GetWorld();
	if (!ensure(World != nullptr)) return;

	APlayerController* PC = World->GetFirstPlayerController();
	if (PC)
	{
		// We get an error that the widget is not focusable otherwise
		this->bIsFocusable = true;

		// Create and set the input mode to focus on widget, and do not lock mouse
		FInputModeUIOnly InputMode; // The pawn should not be controlled by the input here, only UI
		InputMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
		InputMode.SetWidgetToFocus(this->TakeWidget()); // Get the needed SWidget from the UWidget
		PC->SetInputMode(InputMode);
		PC->bShowMouseCursor = true;
	}

	this->AddToViewport();
}

fyi, my LOG function is just the macro
#define LOG(msg) UE_LOG(LogTemp, Warning, TEXT("[%s@line: %d] - %s"), TEXT(__FUNCTION__), __LINE__, *FString(msg))
for easier debugging

Hope this helps someone.
Cheers!

1 Like

Awesome find! I’m always being surprised by the things I couldn’t’ find in Unreal.

1 Like

What surprises me most is the lack of documentation on some parts that seem to be quite important or useful. Oh well, sometimes we’re lucky :slight_smile:

2 Likes

Privacy & Terms