Cannot figure out getting dynamic material instance from default third person character

I was following the Battery Collector game tutorial to get a feel for using UE with C++, until one of the last parts has you dynamically change the character material color according to their power level. The video has you do it via blueprints. I tried this and could not get it to work despite following the tutorial exactly, so then I started looking for a C++ solution. Here is what I seem to be having a problem with:



void ABatteryCollectorCharacter::BeginPlay()
{
    Super::BeginPlay();
    UMaterialInstanceDynamic* matInstance = GetMesh()->CreateDynamicMaterialInstance(0, GetMesh()->GetMaterial(0));
}


I added BeginPlay() to the BatteryCollectorCharacter.cpp and from my understanding in order to change things in the material dynamically, I have to create a dynamic instance of it, change it, then set the main material to that newly edited dynamic one. The line right there where I am calling GetMesh->()CreateDynamicMaterialInstance() somehow seems to be acting like a local variable. It is not null right when that line is called, but as soon as BeginPlay() exits scope, matInstance is null afterward. I am trying to check it using if (matInstance) in another function where I’ll do some math to change the color, which follows:


void ABatteryCollectorCharacter::PowerChangeEffect()
{
    if (matInstance)
    {
        float const PowerRatio = FMath::Clamp((charPowerLvl / initialCharPowerLvl), 0.0f, 1.0f);
        FLinearColor const PowerRatioColor = FMath::Lerp(teal, orange, PowerRatio);

        matInstance->SetVectorParameterValue("BodyColor", PowerRatioColor);
        GetMesh()->SetMaterial(0, matInstance); // replace the mesh material with the modified one
    }
}

matInstance is declared as a private UMaterialInstanceDynamic * in the header file. Also, the PowerChangeEffect() is called every tick since the power level decreases every tick.

If anyone knows what mistake I might be making, I’d really appreciate some help.

You say you have defined matInstance in the header, but in your BeginPlay() you declare it as a local variable as well. In your header use



private:
UPROPERTY()
UMaterialInstanceDynamic* matInstance;



Then change BeginPlay to just be


 
void ABatteryCollectorCharacter::BeginPlay() 
{     
    Super::BeginPlay();     
    matInstance = GetMesh()->CreateDynamicMaterialInstance(0, GetMesh()->GetMaterial(0));
}


Thanks… I knew there had to be something small I was missing in my frustration.

As a short follow-up question: Do you have to make all properties and functions UFUNCTION or UPROPERTY? How do you know when those designators are needed? I noticed that the PowerChangeEffect() function was never being entered in the debugger until after I added the UFUNCTION() macro.

In a nutshell, any variable that requires metadata, reflection or references a UObject should be a UPROPERTY() since without it it’s not tracked for GC. Pretty much ditto for UFUNCTIONS. I suggest you read up here: