[Possible bug] Changing blueprint actor's component properties reconstructs all of its components

Hello everyone,

few months into using UE5, our team has come across this odd behavior that makes it practically impossible to change the values of component properties in the editor’s Details Panel when these components are on instances of blueprint actors. To better illustrate this issue, I am including the steps needed to reproduce it (tested this on both version 5.0.3 and 5.1.0 Preview 2 and got the same results):

  1. Create a blank C++ project
  2. Create a C++ class that inherits from UActorComponent with an editable property. Extend its BeginPlay() function with a test log to see when the function is called.
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class MYPROJECT_API UMyActorComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere)
	int32 MyProperty;
	
public:
	virtual void BeginPlay() override;
};
void UMyActorComponent::BeginPlay()
{
	Super::BeginPlay();

	UE_LOG(LogTemp, Display, TEXT("UMyActorComponent's BeginPlay() called!"));
}
  1. Create a C++ class that inherits from AActor with a property that holds an instance of the previously created component. Extend the constructor of the class with the creation of this instance using CreateDefaultSubobject().
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY(VisibleAnywhere)
	TObjectPtr<UMyActorComponent> MyActorComponent;

public:
	AMyActor();
};
AMyActor::AMyActor()
{
	MyActorComponent = CreateDefaultSubobject<UMyActorComponent>(TEXT("MyActorComponent"));
}
  1. Run the engine and create a new level.
  2. Create a blueprint class that inherits from Actor and add the previously created component to it using the Add new component button in the Components Tab.
  3. Add these actors into the created level:
    a. One instance of the blueprint class actor.
    b. One instance of the C++ based actor.
    c. One instance of the basic empty actor. Add the previously created component to it through the Add new component button in its Details Panel.
  4. Save all changes and start a PIE session. Observe that all three components output a log message in the Output Log window as expected.
  5. Select either the C++ based actor or the basic template actor. Select the component in the Details Panel and change the value of its property. Observe that the value changes as expected and no log messages are created.
  6. Do the same with the blueprint based actor. Observe that the change leads to the component outputting another log message.

Basically, what happens here is that for some reason changing property values in a blueprint actor’s components causes it to throw away and completely recreate all of its components. This would be even more noticeable if we were to add another component to the actors that also outputs a log message on BeginPlay. This component would also produce a log message on the blueprint actor despite us not even touching it. This is how we have originally come across this issue when it would reset animations on skeletal meshes.

What’s also weird is that the change gets applied on the recreated component. In some cases we would encounter that changing nested object’s properties inside the component would reset the changed property’s value. In the test example, this can be achieved by marking the property as Transient.

The issue applies to both actor and scene components, regardless of whether the component itself is C++ or blueprint based. We have also not been able to find any UCLASS or UPROPERTY specifier, nor an option in the blueprint details that would fix this. Then again, we are fairly new to the engine so we have not had the time to delve deep into the whole CDO ecosystem and similar behind-the-scenes engine details.

Is this the expected behavior or indeed a bug? It would be weird if this was intended as it would limit our ability to test and iterate, or limit the usability of blueprints. On the other hand, I have not been able to find any forum posts or bug reports regarding this so we might be missing something obvious. Any help is greatly appreciated.

4 Likes

same issue, create a function in blueprint , add skeketalmesh/staticmesh/splinemesh component in this function . call in editor. Add this blueprint into the created level. call the function in detail. we can see actor add a component in level. then save this level, Play in editor, you can’t see anything .

Notice: this level is a sub-level and always loaded 。 if it is a persistent level ,this will not happen。

i also help official give a explain。

Ah, this problem bugs me too!

UE seems to trigger a BP reconstruction while playing whenever a component property in a BP actor is edited from the Details Panel. I believe this is just for rerunning the Construction Script. However, this reconstruction causes the actor and all its components to be destroyed and recreated again. This can lead to a lot of problems, like member variables being reset and component references broken.

I’ve struggled a lot making custom code to make sure data is not lost during BP reconstruction, and I’m surprised this issue is not mentioned more. Are one not supposed to be able to safely edit component properties during play? This feature is really important for our current project.

Anyway, below are some discoveries I’ve made related to this:

  • To check whether a Component was created “normally” or because of a BP reconstruction, read the global flag
    GIsReconstructingBlueprintInstances from within the component’s BeginPlay().

  • UPROPERTY Actor pointers that point to an actor being reconstructed seem to be recovered automatically by the engine, i.e. the pointer is redirected to point to the recreated actor.

  • However, UPROPERTY Component pointers to components inside a reconstructed actor are not automatically recovered. They become null, and may even temporarily point to the “old” component, so you need to find those components again.

  • The value of UPROPERTY member variables that are Transient or do not have at least VisibleDefaultsOnly are reset during reconstruction, causing data loss. This is annoying because your class’ internal private UPROPERTY data will be lost.

  • The value of non-UPROPERTY member variables are also lost.

  • For variables defined directly in the BP actor (i.e. not C++) they need “Instance Editable” = true to not be reset during reconstruction. Unfortunately that makes all of them visible in the Details Panel.

  • There is a C++ framework for passing “ComponentInstanceData” from the old component to the new component manually. Brief explanation below:

  1. Create a component-specific instance data struct that inherits FActorComponentInstanceData (or FSceneComponentInstanceData etc). This struct is temporarily created during BP reconstruction to store data that should be passed over from old to new component.
  2. Override UActorComponent::GetComponentInstanceData(). This will be called on your old component just before reconstruction. Implement it by instantiating your instance data struct, fill it with your data, and return it.
  3. Override UActorComponent::ApplyComponentInstanceData(). This will be called on your new component right after reconstruction. Implement it by applying data from your instance data struct to your new component.

For an actual example, see LightComponent.h, LightComponent.cpp which uses the above pattern to store lightmap related data.

I really wish EPIC would make things easier. For example, would be nice with a UPROPERTY specifier to enforce that private UPROPERTY variables are not reset during BP reconstruction. Also, some automatic component reference recovery would be good. Or even an option to completely turn off this in-game automatic BP reconstruction for specific actor classes. We don’t even use the construction scripts in our project.

5 Likes

Thought I was going crazy. I have the exact same issue. It makes it a problem for any sort of PIE debug tools that I’d like to use to debug procedural generation outputs. EPIC please fix

I finally found out how to fix this issue.

If you make sure to add the component to your actor blueprint via the C++ constructor, and save it into a UPROPERTY(VisibleAnywhere), then recreate your blueprint entirely (as it likely was corrupted from before), it works!

//in header
UPROPERTY(VisibleAnywhere)
UMyComponent* component;

//in cpp constructor
component = CreateDefaultSubobject<UMyComponent>("MyComponent");

after doing this, I can easily edit the components during PIE without having them reset themselves!

Adding any component manually via the blueprint editor (Add button) seems to have been the cause of this issue.