We repeatedly encounter an issue with Blueprints whose RootComponent is a USceneComponent set to Static (with a UBoxCollision and a UStaticMeshComponent inside, also set to Static). Sometimes, when saving these Blueprints, their Mobility is automatically changed by Unreal to Movable.
This is sometimes problematic for certain Blueprints that are spawned by PCG: if they are set to Movable, they get spawned at the origin instead of their correct spawn location.
Thanks for the report. I’ve just been assigned this case.
Unreal does not allow a Static component to be attached to a Movable parent. If such a configuration is detected in a BP hierarchy, the engine may update mobility to Movable to maintain a valid setup. However, the opposite does not occur, a component will not be automatically demoted to a more static mobility than its parent.
Based on your description, the RootComponent is being changed from Static to Movable despite having Static child components. I was not able to reproduce this behavior in UE 5.6 using a similar setup.
Could you please provide detailed repro steps to reproduce the mobility change on my end?
If this also occurs in a blank project, sharing a minimal repro project where the issue reproduces would be very helpful.
Thank you for your help, I added this to the actor code. I only modified ForceStaticMobilityOnBlueprintSCS by adding a recursive call for the parent BP nodes. In my case, we have several BPs that inherit from a BP which itself inherits from our custom C++ Actor class. And with all that, by doing a test where I deliberately set the RootComponent mobility to Movable, I do get an error message on save, with the Static mobility being reset.
My modification in ForceStaticMobilityOnBlueprintSCS:
if (BP->ParentClass.Get())
{
if (UBlueprintGeneratedClass* BPGCParent = Cast<UBlueprintGeneratedClass>(BP->ParentClass))
{
if (UBlueprint* BPParent = Cast<UBlueprint>(BPGCParent->ClassGeneratedBy))
{
ForceStaticMobilityOnBlueprintSCS(BPParent, BPGC);
}
}
}
I’ll keep you updated if we manage to reproduce the bug, so we can maybe figure out where it’s coming from.
Hello, we just managed to reproduce the bug. However, it is not during saving that the mobility changes, but when loading the asset.
We have:
BP_Prop, which inherits from Actor. Inside it, there is the DefaultRootComp created by Unreal, as well as other components. This RootComponent is set to Static (so it overrides its default Movable state). And in the Construction Script, to try to fix this bug, we were forcing the RootComponent mobility to Static using SetMobility.
BP_Prop_Structure_Wall, which inherits from BP_Prop.
BP_Prop_Wall, which inherits from BP_Prop_Structure_Wall.
Bp_Prop_Wall_Basic_1, which inherits from BP_Prop_Wall.
Apart from the first BP_Prop, which overrides Movable to Static, all the others keep this Static state.
This morning, I was told that Bp_Prop_Wall_Basic_1 had switched back to Movable, even though its last push on Perforce dates back to September 1st, 2025. So the check I just added in PreSave was not triggered, because this is not where the mobility changed.
I added a check in PostLoad, and it correctly detects when loading Bp_Prop_Wall_Basic_1 that the root is not Static (and it properly sets it back to Static).
The only change I can see is actually mine to add this check two days ago. I modified the parent class of BP_Prop to go from Actor to a new custom class in order to put the check in PreSave. I also modified BP_Prop_Structure_Wall to set a bool to true that enables the check (because I did not want this on all BP_Prop). But apart from that, I did not touch the mobility.
I also notice a display bug: on BP_Prop, the Mobility variable has the arrow indicating that the value is overridden, but on BP_Prop_Structure_Wall this arrow also appears even though I did not override the mobility. If I click on it, it switches to Movable. However, on BP_Prop_Wall and Bp_Prop_Wall_Basic_1, this arrow no longer appears. I tried by creating three new BPs inheriting from each other, and indeed from the third BP onward, the arrow no longer appears.
Could creating the RootComponent in the constructor of the new C++ class fix this bug? (even if it probably means losing the potential RootComponent modifications on all the BP_Prop)
Since the issue cannot be reproduced, one possible contingency is to enforce the RootComponent mobility to Static at Blueprint save time.
Assuming the actor’s components are defined directly in the Blueprint (and the class derives from a native base), this can be done by overriding PreSave on the native parent class and correcting the Blueprint’s root component template before serialization.
If you cannot reproduce it locally or consistently then we can work aroudn it by enforcing the mobility to static on this specific actor upon Blueprint save.
The approach would be to override PreSave on the native actor class. Retrieve the owning BP and iterate on the SCS root nodes and enforce Static mobility. Then, let the save continue normally.
Example:
// MyActor.h
...
public:
#if WITH_EDITOR
// Called as part of saving/serializing the object graph.
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
#endif
...
private:
#if WITH_EDITOR
void EnforceStaticRoot();
#endif
In the implementation:
// MyActor.cpp
#include "MyActor.h"
#include "Engine/Blueprint.h"
#include "Engine/BlueprintGeneratedClass.h"
#include "Engine/SimpleConstructionScript.h"
#include "Engine/SCS_Node.h"
#include "Components/SceneComponent.h"
#include "Kismet2/BlueprintEditorUtils.h"
static void ForceStaticMobilityOnBlueprintSCS(UBlueprint* BP, UBlueprintGeneratedClass* BPGC)
{
if (!BP || !BP->SimpleConstructionScript || !BP->GeneratedClass)
{
return;
}
const TArray<USCS_Node*> Nodes = BP->SimpleConstructionScript->GetRootNodes();
for (USCS_Node* Node : Nodes)
{
if (!Node) continue;
UActorComponent* Template = Node->GetActualComponentTemplate(BPGC);
USceneComponent* SceneTemplate = Cast<USceneComponent>(Template);
if (!SceneTemplate) continue;
if (SceneTemplate->Mobility != EComponentMobility::Static)
{
SceneTemplate->Modify();
SceneTemplate->Mobility = EComponentMobility::Static;
}
}
}
void AMyActor::PreSave(FObjectPreSaveContext SaveContext)
{
// Before serialization happens, make sure the archetype/CDO has correct mobility.
EnforceStaticRoot();
Super::PreSave(SaveContext);
}
void AMyActor::EnforceStaticRoot()
{
if (!HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
{
return;
}
// If components are setup via BP, update root node mobility on SCS
UBlueprintGeneratedClass* BPGC = Cast<UBlueprintGeneratedClass>(GetClass());
UBlueprint* BP = BPGC ? Cast<UBlueprint>(BPGC->ClassGeneratedBy) : nullptr;
if (BP) {
ForceStaticMobilityOnBlueprintSCS(BP, BPGC);
return;
}
}
This ensures the root mobility is corrected to Static if it ever gets updated to Movable before saving the BP asset.
Thanks for the update and the extra details. I’ve updated the workaround with your suggestion and set up a scenario similar to yours with multiple inheritance chains from a BP_SomeActor blueprint.
In my case, by the second child level (BP_SomeActor_Child_Child) the mobility was changed during load and appeared as Movable. Upon saving, the mobility was reset to Static, which was the correct state before loading the assets. I’ll need to investigate this behavior further and get back to you.
In the meantime, I can confirm that if the parent BP class is inheriting from a native C++ class and the Root Scene Component is created in C++ with Static mobility set, this will avoid the issue. I initialized the root component like this locally:
// Initialize the RootComponent as a SceneComponent
RootSceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootSceneComponent"));
RootSceneComponent->SetupAttachment(RootComponent);
RootSceneComponent->SetMobility(EComponentMobility::Static);
RootComponent = RootSceneComponent;
With this change, you can discard enforcing the mobility in PreSave or PostLoad.
Please let me know if this helps. I’ll follow up soon with more information regarding the BP mobility reset issue. Thanks again for your help in reproducing this issue.
You may log in to the Unreal Issues site and increase the counter for UE-163285 to help prioritize the fix. The team is aware that this issue is ongoing and it has been recently updated, thanks to our discussion.
In the meantime, you can continue creating the RootComponent in C++ with static mobility to avoid this problem.
Please let me know if you need anything else regarding this case. Otherwise, I’ll go ahead and close it.