Thanks Marc,
So this makes more sense than what I thought the code did at first. I thought that one of the main reasons for the property list simply was to store what was generated (or touched) by the UCS in order to avoid serializing it as it is regenerated anyway. An optimization, with the nice side-effect of avoiding the bug I was seeing, when triggered by the UCS instead of OnConstruction. I missed the instance issue.
Your last sentence sums up very well why the change fixes my issue, but ain’t good generally. This is my code in simplified form:
void AMBModule::OnConstruction(const FTransform& Transform)
{
UStaticMeshComponent* Component = Cast<UPrimitiveComponent>(GetRootComponent());
UMaterialInstanceDynamic* Mat = Component->CreateDynamicMaterialInstance(MaterialIndex);
Mat->SetScalarParameterValue(InstanceValueName, 12.f);
}
Since OverrideMaterials get assigned a transient material by that code, and the property was not touched by my UCS, but IS overriden in the BP’s detail panel, then it is tagged Instanced. So the transient pointer is what gets serialized, and is reset to NULL somewhere along the way. My fix worked because it marked OverrideMaterials as not instanced?
Now normally when calling Component->CreateDynamicMaterialInstance() AFTER I saved my level, the code would read the NULL from OverrideMaterials, and then fetch the StaticMesh’s material (see UStaticMeshComponent::GetMaterial) and Instance that one, and all is good. Probably why it works for most people. But in my case, since I also did Instance to another material, OnConstruction instantiates the material from the StaticMesh, instead of the one configured in the blueprint’s details.
So now I’m thinking the right fix would be to stop storing dynamically instanced materials in OverrideMaterials (UMeshComponent::SetMaterial), have another array for those, and make sure it never gets serialized. What do you think?