"Freeze a physics actor by going kinematic, not by destroying its Rigidbody"
▸ SYMPTOM
- You freeze a multi-part physics object into scenery (a wreck, a settled structure, a "done" ragdoll) by destroying its
Rigidbodyand marking collidersStatic— and some feature that was working silently stops. - Debris that had already broken off the object stops aging, settling, or despawning — but only after the parent is frozen, with no error and no log line.
- A component that ran fine while the actor was live never ticks again once the actor is "turned to scenery."
▸ CAUSE
Destroying a Rigidbody is a bigger hammer than it looks. Any dependent system that gated its own OnFixedUpdate work behind a liveness check like "if the rigidbody is gone, do nothing this tick" stops running the moment the body is gone — including work that has nothing to do with the parent's physics.
A common shape: a per-part debris lifecycle (aging, settling, despawn) lives in the same component that owns the parent actor's physics, sitting below an early-return guard:
protected override void OnFixedUpdate()
{
if ( Rigidbody is null || !Rigidbody.IsValid() )
return; // <-- guard added for the physics path...
UpdateActorPhysics();
AgeAndDespawnShedDebris(); // <-- ...silently takes this down with it
}The idiom that's already proven for freezing a single detached part — destroy its Rigidbody, mark its colliders Static, keep the GameObject and renderer — looks like the obvious thing to apply at whole-actor scale. It's exactly the choice that strands every dependent that keyed its liveness off the body's existence.
▸ FIX
Keep the Rigidbody and make it kinematic instead of destroying it:
// Freeze into scenery WITHOUT killing dependents
rb.MotionEnabled = false; // kinematic: no simulation, but the body still existsOne line buys two things for free:
- Every dependent component keyed off the body's existence keeps ticking on its normal schedule — no new code, no re-parenting, no re-wiring of guards.
- A kinematic body's measured delta-v is always zero, so any impact/impulse-measuring code downstream reads zero for free. The frozen actor becomes dent-proof and stops competing for the shared per-frame collision-response budget — usually exactly what you want from something that is now scenery.
Read before you choose the mechanism, not after. Before freezing, look at every OnFixedUpdate guard of a component that will survive the freeze and ask whether it depends on the body being present. If any does, MotionEnabled = false is strictly safer than destruction.
Corollary for final teardown
When you eventually destroy the actor's GameObject for good, a surviving component may have allocated objects that are not children of the frozen actor — e.g. a debris pool parented to the scene root. Those orphans do not die with their parent GameObject; the component's own destroy-hook is the only thing that frees them.
// Destroy the component EXPLICITLY so its destroy-hook runs and frees scene-root orphans,
// then destroy the actor.
debrisComponent.Destroy();
actor.GameObject.Destroy();Invoke the component's teardown deliberately rather than trusting GameObject destruction to reach it in whatever state it happens to be in.
▸ WHY IT WORKS
MotionEnabled = false flips the Rigidbody to kinematic: the engine stops integrating forces and velocity for it, but the PhysicsBody and the component both stay alive and valid. Destroying the component, by contrast, removes it from the GameObject entirely — every reference goes null and every guard that tested for it takes its downstream code down with it. Choosing "stop simulating" over "cease to exist" preserves all the liveness contracts other systems quietly built on top of the body.
- Published