More inexplicable UMG / Widget disappearing acts...

I have a thread with a similar issue here - but it’s now become prevalent in another scenario in a different project, and I cannot understand it. I’m not posting this on Answerhub because (as has been the case for 3-4 months now) - AH is not loading for me. Even though there’s some C++ below, I’ve explained as clearly and as detailed as I can.

In our game, we have a bunch of objects that the player can ‘Target’. When they do this, a targeting widget is displayed around the object and resizes/relocates itself inside the viewport to fit around the object as best as possible. Following the changes in 4.9 that forced widgets to basically die / invalidate when they leave screen-space, I’ve since moved this to a special ‘update’ function inside the **master ** widget,which determines when a targeting widget should be visible. I’m posting full source code for this below with a detailed explanation as to what it does. Anybody can understand it.

In the master game widget, this function is called from ‘NativeConstruct()’ - and this creates a new ‘Target’ widget for each object the player can Target. Since the number of objects never changes and is static, I just create these at the start of the game so there is no need for creating new ones or deleting old ones. It basically iterates over all of the objects that are pooled in the GameMode, and creates and assigns a new targeting widget for it as a child of the master widget. Easy right?



void UGESGame_InGameWidget::InitializeTargetWidgets()
{
	// Just early-out if these haven't been set
	ASSERTV(TargetWidget != NULL, *FString::Printf(TEXT("InGameWidget::InitializeTargetWidgets has no TargetWidget set!")));

	const AGESGame_DefaultGameMode* GESMode = UGESGameplayStatics::GetGameMode(this->GetWorld());

	// Add new Target Widgets for Satellites & Debris
	for (int32 i = 0; i < MAX_DEBRIS + MAX_SATELLITES; i++)
	{
		UGESGame_TargetWidget* NewWidget = CreateWidget<UGESGame_TargetWidget>(GetOwningPlayer(), TargetWidget);
		ASSERTV(NewWidget != nullptr, TEXT("InGameWidget::InitializeTargetWidgets was unable to create a New Target Widget"));

		if (i < MAX_DEBRIS)
		{
			NewWidget->SetAssignedTarget(GESMode->GetDebrisPool()*);
		}
		else
		{
			NewWidget->SetAssignedTarget(GESMode->GetSatellitePool()[i - MAX_DEBRIS]);
		}

		NewWidget->AddToViewport(0);
		ActiveTargetWidgets.Add(NewWidget);
	}
}


In NativeTick() of the same master widget, I decide whether each one should be visible or not and where on screen it should be. Again, relatively simple. Now bear in mind that ‘ActiveTargetWidgets’ is of a SPECIFIC type of widget (the Targeting one), and NO other widgets are added to this array EVER. This code is relatively self-explanatory. It asks each child widget if it should be on screen, and if so, displays it and updates its visibility, position, color and size. This basically forces the widget to become visible and start ticking again when it enters screen-space.



void UGESGame_InGameWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
	Super::NativeTick(MyGeometry, InDeltaTime);

	FVector2D ScreenLocation = FVector2D::ZeroVector;
	for (int32 i = 0; i < ActiveTargetWidgets.Num(); i++)
	{
		// Check if valid, because this widget may have been removed at some stage and that would be bad.
		ASSERTV(ActiveTargetWidgets.IsValidIndex(i) == true, *FString::Printf(TEXT("InGameWidget::NativeTick() - Active Widgets is Invalid at Index %i"), i));
		UGESGame_TargetWidget* WidgetItr = ActiveTargetWidgets*;
		if (WidgetItr->CanBeSeen(ScreenLocation))
		{
			WidgetItr->SetVisibility(ESlateVisibility::Visible);
			WidgetItr->Update(InDeltaTime, ScreenLocation);
		}
		else
		{
			WidgetItr->SetVisibility(ESlateVisibility::Hidden);
		}
	}
}




inline bool UGESGame_TargetWidget::CanBeSeen(FVector2D& ScreenLocation) const
{
	if (AssignedTarget.IsValid() && OwningGESPlayer.IsValid() && OwningGESPlayer->GetControlledSatellite() && AssignedTarget->GetOrbiterActive() == true)
	{
		const bool bIsBeingTargettedByCurrSatellite = OwningGESPlayer->GetControlledSatellite()->GetTargetObject() && OwningGESPlayer->GetControlledSatellite()->GetTargetObject() == AssignedTarget.Get();
		const bool bIsBeingScanned = AssignedTarget->IsBeingScanned();

		// TODO - Get Decommission Status For This. Decommissioned Satellites should display, live ones shouldn't
		const bool IsLiveSatellite = OwnerSat != nullptr ? true : false;

		if (bIsBeingTargettedByCurrSatellite || (bIsBeingScanned && !IsLiveSatellite))
		{
			return !bHasBeenHidden && GetOwningPlayer()->ProjectWorldLocationToScreen(AssignedTarget->GetActorLocation(), ScreenLocation);
		}
	}

	return false;	
}



The only reason I’m posting the above details is because it seems linked (for no apparent reason) to the visibility of other child widgets of the master game widget, which makes absolutely zero sense and ONLY occurs once it’s deployed to a mobile platform.

The Master widget is displayed in the game at all times, it handles everything from the menu to the in-game UI (for simplicities sake among other things, we never change level) - and it has some UMG-created child widgets of it’s own added in the Designer. For example, these custom ‘Joystick’ widgets that you see in the screenshot below:

485a6872b8f98337eaf4f270507cb8f40434cfe4.jpeg

Now on PC, in PIE, Standalone AND Mobile preview - these Joysticks always display when they should. They behave exactly as they are meant to.

HOWEVER, on any mobile device that I actually deploy the game to, the joysticks disappear if there is NO targeting widget on-screen at that time. They only disappear *VISUALLY * however, I can still interact with them and control the vehicle but they just refuse to actually draw. The widgets are not even remotely linked to the targeting ones, and there is NO code that hides the joysticks like this. Absolutely nothing. I can only fathom that there is a deeper issue. Have two screenshots directly from an NVidia Shield (this also occurs on a Sony Xperia Z1 and all other devices I’ve tested on).

Screenshot 1 - Targetting widget is on-screen (the green brackets) - and the joysticks are displaying.

1a52ebce4f2216f801e1c603cb47922530d030e6.jpeg

Screenshot 2 - Move the camera until the targeting widget is off-screen and all of a sudden, the joysticks stop drawing.

4dac8ca836e72a2eee53628264d18ec10bece754.jpeg

This is super frustrating because the problem only occurs when it’s actually deployed to the device, so I’m powerless when it comes to debugging it. Not that it really matters anyway since this plain shouldn’t happen. This among with lots of other discretions is why I can never trust the mobile previewer.

I’m not bashing on UMG or slate, but this issue combined with the one in the other thread I linked above begs the question: What the heck is going on?