HUD UMaterialInstanceDynamic null pointer in DrawHUD()

In version 4.6 my HUD class was working.
With the new version 4.7 some problems appeared.

My HUD class contains materials.

protected:
	/** Texture for the battery indicator. */
	UPROPERTY()
	UTexture2D* BatteryTexture;
	/** Fill texture for the battery indicator. */
	UPROPERTY()
	UTexture2D* BatteryFillTexture;
	/** The material instance used to fill the energy battery */
	UPROPERTY()
	UMaterialInstanceDynamic* EnergyFillMatInst;
	/** The material instance used to fill the power battery */
	UPROPERTY()
	UMaterialInstanceDynamic* PowerFillMatInst;

In the constructor I initialize materials

AKPlayerHUD::AKPlayerHUD(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

	static ConstructorHelpers::FObjectFinder<UTexture2D> BatteryTextureOb(TEXT("/Game/HUD/Textures/t_BatteryEmpty"));
	static ConstructorHelpers::FObjectFinder<UTexture2D> BatteryFillTextureOb(TEXT("/Game/HUD/Textures/T_BatteryFill-128x128"));
	static ConstructorHelpers::FObjectFinder<UMaterialInstance> EnergyFillMatInstOb(TEXT("/Game/Materials/M_BatteryLevel_Inst"));
	static ConstructorHelpers::FObjectFinder<UMaterialInstance> PowerFillMatInstOb(TEXT("/Game/Materials/M_PowerLevel_Inst"));

	BatteryTexture = BatteryTextureOb.Object;
	BatteryFillTexture = BatteryFillTextureOb.Object;

	BatteryHeight = 100.f;
	// Calculate the scale for the battery display
	BatteryMaterialScale = BatteryHeight / BatteryTexture->GetSizeY();

	BatteryDimensions = FVector2D(BatteryTexture->GetSizeX()*BatteryMaterialScale, BatteryTexture->GetSizeY()*BatteryMaterialScale);
	BatteryFillDimensions = FVector2D(BatteryFillTexture->GetSizeX()*BatteryMaterialScale, BatteryFillTexture->GetSizeY()*BatteryMaterialScale);

	if (EnergyFillMatInstOb.Succeeded())
	{
		EnergyFillMatInst = UMaterialInstanceDynamic::Create(EnergyFillMatInstOb.Object, this);
	}
	if (PowerFillMatInstOb.Succeeded())
	{
		PowerFillMatInst = UMaterialInstanceDynamic::Create(PowerFillMatInstOb.Object, this);
	}
}

No problem. The pointers are valid.
When DrawHUD() function is called the pointers EnergyFillMatInst and PowerFillMatInst
are NULL. The BatteryTexture and BatteryFillTexture are still valid.

void AKPlayerHUD::DrawHUD()
{
	Super::DrawHUD();

	if (Enabled == false)
		return;

	// Get The Character
	AKPlayerCharacter* Player = Cast<AKPlayerCharacter>(GetOwningPawn());
	float offsetX = (BatteryOffset.X > 0.f) ? BatteryOffset.X : 200;
	float offsetY = (BatteryOffset.Y > 0.f) ? BatteryOffset.Y : 10;

	// Draw the energy battery on the left
	DrawBattery(Canvas->SizeX, Canvas->SizeY,
		(Canvas->SizeX / 2.0f) - offsetX, Canvas->SizeY - BatteryDimensions.Y - BatteryOffset.Y,
		Player->BasicAttributes.Energy, FName("EnergyLevel"),
		Player->BasicAttributes.MaxEnergy, FName("MaxEnergyLevel"),
		EnergyFillMatInst);

	// Draw the power battery on the right
	DrawBattery(Canvas->SizeX, Canvas->SizeY,
		(Canvas->SizeX / 2.0f) + offsetX, Canvas->SizeY - BatteryDimensions.Y - BatteryOffset.Y,
		Player->BasicAttributes.Power, FName("PowerLevel"),
		Player->BasicAttributes.MaxPower, FName("MaxPowerLevel"),
		PowerFillMatInst);
}

Why the UMaterialInstanceDynamic pointers are reset to NULL ?
Something different with 4.7 version regarding UMaterialInstanceDynamic pointer.

Txs for your help.

I would suggest not creating the DynamicMaterialInstances in the constructor, it is mainly there for your default initialization.

You should create them either in your PostInitializeComponents method of the AActor or in BeginPlay, those two are the equivalent methods to the BPs construction script and the BeginPlay event. I normally create those in the BeginPlay because the instances are purely gameplay related.

I would also suggest to add the transient flag to the variables, this will skip the serialization of those properties in case you serialize the actor.

Cheers,
Moss

Unfortunately I cannot do this code in the PostInitialize()

static ConstructorHelpers::FObjectFinder<UTexture2D> BatteryTextureOb(TEXT("/Game/HUD/Textures/t_BatteryEmpty"));
     static ConstructorHelpers::FObjectFinder<UTexture2D> BatteryFillTextureOb(TEXT("/Game/HUD/Textures/T_BatteryFill-128x128"));
     static ConstructorHelpers::FObjectFinder<UMaterialInstance> EnergyFillMatInstOb(TEXT("/Game/Materials/M_BatteryLevel_Inst"));
     static ConstructorHelpers::FObjectFinder<UMaterialInstance> PowerFillMatInstOb(TEXT("/Game/Materials/M_PowerLevel_Inst"));

The ObjectFinder must be in the constructor.

I suspect the EnergyFillMatInst and PowerFillMatInst are reset due to the garbage collector.

Regards,
D.

You just have to call UMaterialInstanceDynamic::Create in PostBegin of PostInitializeComponents, just hoild a default deference to your base materials. This is how it’s normally done, you would do the same in Blueprints ^^.

Thank you for your help.

I change the way of including the MaterialInstance.

In the class, I’ve included a MaterialInstance pointer

	/** The material used to fill the energy battery */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ActionBar)
	UMaterialInstance* EnergyFillMatInst;

in the constructor, I load and keep the material instance object.

....
	static ConstructorHelpers::FObjectFinder<UMaterialInstance> EnergyFillMatOb(TEXT("/Game/HUD/Materials/M_BatteryLevel_Inst"));
...
	if (EnergyFillMatOb.Succeeded())
		EnergyFillMatInst = EnergyFillMatOb.Object;

And finally, in PostInitializeComponents, I create the dynamic material instance.

...
	Super::PostInitializeComponents();

	if (EnergyFillMatInst)
		EnergyFillMatInstDyn = UMaterialInstanceDynamic::Create(EnergyFillMatInst, this);
...

And it is working fine. In addition to that, we can select another material instance from the BP Editor.

Txs again for your help.
D.