[Bug] Scale not set on components after PostLoad or PostLoadSubobjects

Hi.

To give some context, I’m create a sort of ‘guide’ sphere component for use when making a level. It’s a sphere used to indicate the distance from which a camera will view an object (can be changed on the object properties.) I have this sphere created automatically. I use PostLoad, PostLoadSubojects and PostEditChangeProperty to trigger the sphere being created/resized. This all works fine.

The issue arises with the scale of the spheres. I want the sphere to be a constant size, not depending on the scale of the object itself. I have since discovered bAbsoluteScale - this does not change the nature of the bug.

The issue is that after PostLoad and PostLoadSubojects the scale of components is 1,1,1 (I checked this via a breakpoint in VS.) None of the actors loaded in to the map have that scale. I’ve tried GetActorScale() and scanning the actor components for primitive components (and checking they are not my sphere components) and getting the scale directly from RelativeScale3D. Still 1,1,1. Changing the camera distance (and triggering the PostEditChangeProperty method) returns the correct scale.

Relevant code:

#if WITH_EDITOR

void ABCSpaceBody::PostEditChangeProperty( FPropertyChangedEvent& stPropertyChangedEvent )
{
	Super::PostEditChangeProperty( stPropertyChangedEvent );

	if ( stPropertyChangedEvent.Property == NULL )
		return;

	if ( stPropertyChangedEvent.Property->GetName() == "fCameraDistanceMin" || stPropertyChangedEvent.Property->GetName() == "RelativeScale3D" )
		UpdateCameraDistanceSpheres();
}


void ABCSpaceBody::PostLoad()
{
	Super::PostLoad();

	InitCameraSpheres();
}


void ABCSpaceBody::PostLoadSubobjects( FObjectInstancingGraph* oOuterInstanceGraph )
{
	Super::PostLoadSubobjects( oOuterInstanceGraph );

	InitCameraSpheres();
}

#endif // WITH_EDITOR


void ABCSpaceBody::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	InitCameraSpheres();
}


void ABCSpaceBody::InitCameraSpheres()
{
	UWorld* oWorld = GetWorld();
	
	if ( oWorld == NULL )
		oWorld = GWorld;

	if ( oWorld == NULL )
		return;

	switch ( oWorld->WorldType )
	{
		case EWorldType::Game:
		case EWorldType::PIE:
			if ( cCameraDistanceMinComponent != NULL )
				cCameraDistanceMinComponent->DestroyComponent();

			if ( cCameraDistanceMaxComponent != NULL )
				cCameraDistanceMaxComponent->DestroyComponent();

			break;

#if WITH_EDITOR
		case EWorldType::Editor:
		case EWorldType::Preview:
			UpdateCameraDistanceSpheres();
			break;
#endif
	}
}


#if WITH_EDITOR

void ABCSpaceBody::UpdateCameraDistanceSpheres()
{
	// *****
	// When triggered from PostLoad and PostLoadSubojects, always returns 1,1,1
	// Works fine from PostEditChangeProperty
	// *****
	FVector vScale = GetActorScale();

	// Invert object scale
	vScale.X = 1.f / vScale.X;
	vScale.Y = 1.f / vScale.Y;
	vScale.Z = 1.f / vScale.Z;

	if ( cCameraDistanceMinComponent == NULL )
	{
		NewObjectAssign( cCameraDistanceMinComponent, USphereComponent, "Camera Min Distance Sphere" );
		cCameraDistanceMinComponent->AttachTo( GetRootComponent(), NAME_None, EAttachLocation::SnapToTarget );
		cCameraDistanceMinComponent->InitSphereRadius( 10.f );
		cCameraDistanceMinComponent->bDrawOnlyIfSelected = true;
	}

	cCameraDistanceMinComponent->SetSphereRadius( max( 10.f, fCameraDistanceMin ) );
	cCameraDistanceMinComponent->SetRelativeScale3D( vScale );

	if ( cCameraDistanceMaxComponent == NULL )
	{
		NewObjectAssign( cCameraDistanceMaxComponent, USphereComponent, "Camera Max Distance Sphere" );
		cCameraDistanceMaxComponent->AttachTo( GetRootComponent(), NAME_None, EAttachLocation::SnapToTarget );
		cCameraDistanceMaxComponent->InitSphereRadius( 10.f );
		cCameraDistanceMaxComponent->bDrawOnlyIfSelected = true;
	}
	
	vScale *= BC_CAMERA_ORBIT_MAX_ZOOM_OUT;

	cCameraDistanceMaxComponent->SetSphereRadius( max( 10.f, fCameraDistanceMin ) );
	cCameraDistanceMaxComponent->SetRelativeScale3D( vScale );
}

Your problem probably comes from the editor copying over the needed properties after PostLoad.

Let me guess, it only occurs on Blueprint recompile, yes?

I have kind of the same problem. I think one could introduce a new (editor only) virtual void PostLoadAfterRecompile() or something like that in UObject, then called at the end of kismetreinstanceutilities.cpp’s function after the real copying is done.