Quick answer: The most common cause is calling SetScalarParameterValue on a static material instance rather than a dynamic one. Static material instances (UMaterialInstanceConstant) cannot be modified at runtime.
Here is how to fix Unreal material instance not updating. You call SetScalarParameterValue or SetVectorParameterValue on a material instance, and the mesh does not change color, does not fade, does not do anything. The material looks exactly the same as it did before the call. No error, no warning — just a function call that silently does nothing. This is one of the most common material issues in Unreal Engine, and it comes down to the distinction between static and dynamic material instances.
The Symptom
You have a mesh in your scene with a material that exposes parameters — perhaps an Opacity scalar or an EmissiveColor vector. You call SetScalarParameterValue or SetVectorParameterValue in C++ or Blueprint, and the visual result on screen does not change. The material stays exactly as it appeared when the level loaded.
You might verify that the code is running by adding a log statement right after the parameter set call. The log prints, the value you pass in looks correct, but the mesh's appearance is unchanged. In some cases, you may even read the parameter value back from the material instance and it appears to have been set — yet the visual does not reflect it.
The frustrating part is that changing the same parameter in the Material Instance editor during play-in-editor works perfectly. The disconnect between editor and runtime behavior makes this problem especially confusing.
What Causes This
1. Using a static material instance instead of a dynamic one. This is the root cause in most cases. When you get a material from a mesh component using GetMaterial(), you receive a UMaterialInterface pointer that typically points to a UMaterialInstanceConstant — a pre-authored, immutable asset. Calling parameter setters on this object either does nothing or modifies the shared asset in memory, which can affect all objects using that material in unpredictable ways. For per-object runtime changes, you must create a UMaterialInstanceDynamic.
2. Parameter name mismatch. The parameter name passed to SetScalarParameterValue must match the parameter name defined in the parent material exactly, including capitalization and spacing. If the material has a parameter called Emissive Strength and you pass EmissiveStrength or emissive strength, the call silently fails. There is no runtime error for a mismatched parameter name.
3. Dynamic material instance not applied to the mesh. If you create a UMaterialInstanceDynamic manually with UMaterialInstanceDynamic::Create() but forget to assign it back to the mesh component with SetMaterial(), you are modifying an instance that is not being rendered. The mesh continues to display its original material.
4. Wrong material index. Meshes can have multiple material slots. If you create a dynamic instance for slot 0 but the material you want to modify is on slot 1, you are changing the wrong material. The visual you are looking at on screen corresponds to a different material index than the one you are modifying in code.
The Fix
Step 1: Create a dynamic material instance from the mesh component. The simplest and most reliable method is to use CreateDynamicMaterialInstance on the mesh component itself. This creates the dynamic instance, assigns it to the mesh, and returns a pointer in one call.
// MyActor.h
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor();
virtual void BeginPlay() override;
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* MeshComp;
UPROPERTY()
UMaterialInstanceDynamic* DynMaterial;
void SetEmissiveColor(FLinearColor NewColor);
void SetOpacity(float NewOpacity);
};
// MyActor.cpp
AMyActor::AMyActor()
{
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
RootComponent = MeshComp;
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
// Create dynamic material instance for slot 0
DynMaterial = MeshComp->CreateDynamicMaterialInstance(0);
if (!DynMaterial)
{
UE_LOG(LogTemp, Error, TEXT("Failed to create dynamic material instance"));
return;
}
UE_LOG(LogTemp, Log, TEXT("Dynamic material created from: %s"),
*DynMaterial->GetBaseMaterial()->GetName());
}
Step 2: Set parameters on the dynamic instance using the exact parameter names. Open the parent material in the Material Editor to confirm the exact names of your parameters.
void AMyActor::SetEmissiveColor(FLinearColor NewColor)
{
if (!DynMaterial) return;
// Parameter name must match the material parameter exactly
DynMaterial->SetVectorParameterValue(FName(TEXT("EmissiveColor")), NewColor);
UE_LOG(LogTemp, Log, TEXT("Set EmissiveColor to R=%f G=%f B=%f"),
NewColor.R, NewColor.G, NewColor.B);
}
void AMyActor::SetOpacity(float NewOpacity)
{
if (!DynMaterial) return;
DynMaterial->SetScalarParameterValue(FName(TEXT("Opacity")), NewOpacity);
}
Step 3: Handle multiple material slots. If your mesh has more than one material, create dynamic instances for each slot you need to modify.
// Create dynamic instances for all material slots
void AMyActor::CreateAllDynamicMaterials()
{
int32 NumMaterials = MeshComp->GetNumMaterials();
for (int32 i = 0; i < NumMaterials; i++)
{
UMaterialInstanceDynamic* DMI = MeshComp->CreateDynamicMaterialInstance(i);
if (DMI)
{
UE_LOG(LogTemp, Log, TEXT("Created dynamic material for slot %d: %s"),
i, *DMI->GetBaseMaterial()->GetName());
}
}
}
Related Issues
If your material changes work at runtime but the whole game crashes when you package the build, see our guide on packaged build crashes on startup, which covers missing asset references that can affect materials. If you are triggering material changes from an overlap volume and the overlap itself is not firing, check out overlap events not triggering.
CreateDynamicMaterialInstance. Not GetMaterial. Not Cast. Create.