A call to SetActorLabel causes an actor’s components to be reset back to default states if you are running the game outside the main editor process.
SetActorLabel calls PostEditChangeProperty, which checks ReregisterComponentsWhenModified(). It looks like this is true when outside the main editor process, causing ReregisterAllComponents and RerunConstructionScripts to reset the actor’s components.
This was tricky to debug because I didn’t expect SetActorLabel to have such a drastic side-effect and because the problem only happened when running multiple players, making me initially suspect a networking issue.
I am attaching an Unreal project that can be run to reproduce this problem with Unreal 4.7.1.
Unzip and open ReproProject.uproject. Pull down the dropdown next to the Play button, choose Advanced Properties, and set:
Number of Players: 2
Use Single Process: Unchecked
Editor Multiplayer Mode: Play as Listen Server
Then press the play button.
RESULT: Main editor game window has a green block. Second game window has a red block.
EXPECTED: Both windows have green blocks.
Onscreen text shows the material associated with the actor’s component and it can be seen that they change when SetActorLabel is called.
Here is the relevant code that spawns the actor, changes its material, and calls SetActorLabel:
#include “ReproProject.h”
#include “Spawner.h”
ASpawner::ASpawner()
{
Spawned = nullptr;
}
void ASpawner::BeginPlay()
{
Spawned = GetWorld()->SpawnActor<AActor>(SpawnedClass);
for (UActorComponent* MeshActorComponent : Spawned->GetComponentsByClass(UStaticMeshComponent::StaticClass()))
{
UStaticMeshComponent *Mesh = Cast<UStaticMeshComponent>(MeshActorComponent);
check(Mesh);
Mesh->SetMaterial(0, MaterialToSet);
}
PrintMeshMaterials();
Spawned->SetActorLabel(TEXT("Spawned Actor with Label"));
PrintMeshMaterials();
}
void ASpawner::PrintMeshMaterials()
{
for (UActorComponent* MeshActorComponent : Spawned->GetComponentsByClass(UStaticMeshComponent::StaticClass()))
{
UStaticMeshComponent *Mesh = Cast<UStaticMeshComponent>(MeshActorComponent);
check(Mesh);
UMaterialInterface* Material = Mesh->GetMaterial(0);
GEngine->AddOnScreenDebugMessage(-1, 1000.0f, Material == MaterialToSet ? FColor::Green : FColor::Red, Material->GetName());
}
}