"Disabled components vanish from GetAllComponents — hold spawn-time references"
▸ SYMPTOM
You write a distance cull that turns renderers off past some range by flipping ModelRenderer.Enabled = false, and rebuild the working set each frame with GetAllComponents<ModelRenderer>() (or a Scene/GameObject query). Two things go wrong and neither errors:
- A per-frame census under-reports by exactly the number of objects the cull switched off in a prior pass.
- A re-enable pass (bring objects back as the camera approaches) can never find the objects it disabled earlier — they've dropped out of the query, so they stay off forever.
▸ CAUSE
GetAllComponents<T> and any component query over Scene/GameObject do not return disabled components. The moment the cull sets Enabled = false, that renderer disappears from every subsequent query. So a design that re-queries the scene each frame to (a) count what's live or (b) find candidates to re-enable is querying a set that no longer contains the very objects it manipulated. The disabled objects are still in the scene graph — they're just invisible to the query API.
A second trap sits in the same code. ClothingContainer.Apply creates bone-merged child SkinnedModelRenderers — one per clothing piece merged onto the base skeleton. A spawn-time capture that only grabs the object's own renderer misses all of them, so a cull driven off that capture leaves the clothing renderers toggling out of sync with the base body.
▸ FIX
Capture the renderer references once at spawn time and keep the list. Drive Enabled off distance against that held list — never off a fresh per-frame query:
// At spawn, AFTER clothing is applied:
_renderers = GameObject
.GetComponents<SkinnedModelRenderer>(FindMode.Enabled | FindMode.InSelf | FindMode.InDescendants)
.ToList();
// Per frame:
bool visible = Vector3.DistanceBetween(cam, WorldPosition) < CullRange;
foreach (var r in _renderers)
r.Enabled = visible;Use FindMode.Enabled | InSelf | InDescendants at capture time (after clothing is applied) so the held list already includes every clothing-piece child renderer the cull needs to toggle alongside the base body. A self-only query captures the base and misses the merged children.
▸ WHY IT WORKS
The held list is a stable set of direct references that doesn't depend on the current Enabled state of anything. You can flip renderers on and off all day and still iterate the same complete list next frame, because you're holding the components directly rather than re-deriving them from a query that filters out disabled objects.
- Published