"FirstOrDefault() on SkinnedModelRenderer can grab clothing, not the body"
▸ SYMPTOM
- You grab a citizen's renderer with
GetAllComponents<SkinnedModelRenderer>().FirstOrDefault()and a sequence/parameter probe reports every locomotion and hold clip absent — reads like "this model has no sequences." - The renderer reference is non-null and valid, so nothing looks wrong — but animation code driving it does nothing.
▸ CAUSE
Citizen clothing attaches as its own bone-merged SkinnedModelRenderer (a hat, a jacket) — a sibling or child of the body renderer. So a scene with a dressed citizen carries multiple valid SkinnedModelRenderer instances, and FirstOrDefault() returns whichever one happens to enumerate first — not necessarily the body.
When that first renderer is a hat, a sequence/param probe run against it reports every locomotion/hold clip missing, because a hat mesh genuinely has no locomotion sequences. The real story ("this is a hat, not the body") reads as "this model has no sequences."
This is a distinct failure mode from a child-renderer lookup returning null: here the query returns a wrong, non-null renderer because of enumeration order, not an empty result.
▸ FIX
Never grab the first SkinnedModelRenderer blind. Select the body renderer explicitly:
// WRONG — may return a bone-merged clothing renderer (a hat):
var body = go.GetAllComponents<SkinnedModelRenderer>().FirstOrDefault();
// RIGHT — pick the renderer whose Model is the citizen body:
var body = go.GetAllComponents<SkinnedModelRenderer>()
.FirstOrDefault( r => r.Model?.Name?.Contains( "citizen.vmdl" ) == true );
// Or, best: hold the body renderer reference the root assigns, and use that
// directly instead of re-querying the component graph.If you own the character setup, keep an explicit reference to the body SkinnedModelRenderer at spawn time and pass it around — don't re-discover it by enumeration, which is order-dependent and breaks the moment clothing is added.
▸ WHY IT WORKS
GetAllComponents<T>() walks the object's component graph in enumeration order, which is not guaranteed to put the body first — clothing renderers are first-class SkinnedModelRenderer components too. Filtering on the body model (or holding an explicit reference) removes the dependency on enumeration order, so the code always drives the renderer that actually carries the locomotion sequences.
- Published.