Quick answer: Constraint references go stale when actors stream out and back in or load from a save. Re-call SetConstrainedComponents in BeginPlay with the current pointers, ensure both target components are SimulatePhysics(true), then re-initialize the constraint with InitializeComponent.
Here is how to fix Unreal PhysicsConstraintComponents that work in PIE but break in shipping after the actors involved are streamed out and back in, or after loading from a SaveGame. The constraint object itself survives, but its target references no longer point at live components. The bodies fall apart or freeze in place.
The Symptom
Two physics actors connected by a PhysicsConstraintComponent behave correctly when first spawned. When the player walks far away, the area unloads. Returning, the actors are present but no longer constrained — they fall, scatter, or stand frozen. Same effect after loading from a save.
What Causes This
Stale component pointers. SetConstrainedComponents takes raw pointers. After streaming reload, the actors are reconstructed and the original pointers become invalid.
Save/Load reset. SaveGame restores the constraint component but its ConstraintInstance needs explicit re-init with the now-valid components.
Physics not enabled on target. If a streaming reload sets actors to non-simulating (perhaps to save CPU during stream-in) and your code never re-enables, the constraint has nothing to act on.
Component name mismatch. If the constraint references components by FName and one was renamed, the lookup fails.
The Fix
Step 1: Re-bind constraints in BeginPlay.
// AHingedBoxActor.h
UCLASS()
class AHingedBoxActor : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* HingeBase;
UPROPERTY(VisibleAnywhere)
UStaticMeshComponent* HingedDoor;
UPROPERTY(VisibleAnywhere)
UPhysicsConstraintComponent* Hinge;
public:
virtual void BeginPlay() override;
};
// AHingedBoxActor.cpp
void AHingedBoxActor::BeginPlay()
{
Super::BeginPlay();
HingeBase->SetSimulatePhysics(false);
HingedDoor->SetSimulatePhysics(true);
Hinge->SetConstrainedComponents(
HingeBase, NAME_None,
HingedDoor, NAME_None);
}
Step 2: Reset constraint after save load.
void AHingedBoxActor::OnLoadGame()
{
HingedDoor->SetSimulatePhysics(true);
Hinge->BreakConstraint();
Hinge->SetConstrainedComponents(HingeBase, NAME_None, HingedDoor, NAME_None);
Hinge->InitializeComponent();
}
The Break + SetConstrainedComponents + InitializeComponent sequence forces a complete rebuild of the constraint instance.
Step 3: Use FConstraintInstance to apply saved settings.
// On save: capture profile
FConstraintProfileProperties profile = Hinge->ConstraintInstance.ProfileInstance;
SaveData.AngularLimit = profile.ConeLimit.Swing1LimitDegrees;
SaveData.LinearLimit = profile.LinearLimit.Limit;
// On load: reapply
Hinge->ConstraintInstance.SetAngularSwing1Limit(ACM_Limited, SaveData.AngularLimit);
Hinge->ConstraintInstance.SetLinearXLimit(LCM_Limited, SaveData.LinearLimit);
Hinge->InitializeComponent();
Step 4: Validate physics actors before constraint creation.
if (!HingeBase || !HingedDoor)
{
UE_LOG(LogTemp, Warning, TEXT("Constraint targets missing on %s"), *GetName());
return;
}
if (!HingedDoor->IsSimulatingPhysics())
{
HingedDoor->SetSimulatePhysics(true);
}
Step 5: For World Partition reloads, hook OnLevelLoaded.
void AHingedBoxActor::OnConstructionComplete()
{
if (UWorld* World = GetWorld())
{
World->OnLevelsChanged().AddUObject(this, &AHingedBoxActor::RebindConstraint);
}
}
void AHingedBoxActor::RebindConstraint()
{
Hinge->SetConstrainedComponents(HingeBase, NAME_None, HingedDoor, NAME_None);
}
Common Pitfalls
Setting SimulatePhysics(true) on a component that has no physics asset (no collision body) silently has no effect. The constraint then connects two non-simulating bodies and produces no motion. Verify the static mesh has a physics body in its FBX import / Body Setup.
Welding bodies (WeldTo) before constraining can produce surprising behavior. The weld root takes over physics, so the constraint may end up referencing the wrong body. Avoid welding bodies you intend to constrain to.
“Pointers go stale. Constraints need rebinding after every reload. SetConstrainedComponents + InitializeComponent rebuilds the link.”
Related Issues
For other physics issues, see Physics Constraint Drive. For World Partition streaming, see World Partition Streaming Hitch.
Rebind in BeginPlay. Re-init after save load. Keep both bodies simulating.