Quick answer: SkinnedMeshRenderer uses bind-pose bounds for culling. Animations that extend the silhouette beyond bind pose can fall outside the cached bounds. Enable Update When Offscreen for hero characters, or expand Local Bounds manually to cover worst-case poses.
Here is how to fix Unity skinned characters that pop out of view when the camera focuses on a body part. The mesh disappears just as the player swings a sword overhead, then reappears when the animation ends. The cause is a static bounding box that was computed from the bind pose — animations stretching beyond that box trigger frustum culling early.
The Symptom
A character animates correctly when the entire body is in view. Camera moves close, framing only the head or weapon, and the character vanishes from the frame. Pulling the camera back makes them reappear. Static meshes nearby render fine.
What Causes This
Bind-pose bounds. SkinnedMeshRenderer caches bounds from the bind pose at import. Animations stretching outside (raised arms, leaping, large attack animations) put parts of the mesh outside the cached box, causing the entire renderer to be culled when its center is offscreen.
Update When Offscreen disabled. The default is off because every-frame bounds recomputation costs CPU.
Bone influences outside bind extents. Bones that move significantly (cloth, hair, weapon attachments) can also push the actual silhouette beyond the cached bounds.
Wrong root scale. A scale baked into the root bone can make the bone-space bounds smaller than the world-space silhouette.
The Fix
Step 1: Enable Update When Offscreen for hero characters. On the SkinnedMeshRenderer, check Update When Offscreen. Bounds recalc every frame from current bone positions. The mesh stays visible when any part of it is in frustum.
Step 2: Expand Local Bounds manually for crowd characters.
using UnityEngine;
[RequireComponent(typeof(SkinnedMeshRenderer))]
public class ExpandSkinnedBounds : MonoBehaviour
{
[SerializeField] private Vector3 extents = new Vector3(2, 3, 2);
void Awake()
{
var r = GetComponent<SkinnedMeshRenderer>();
r.localBounds = new Bounds(Vector3.zero, extents * 2f);
}
}
Set extents large enough to cover the largest pose. A bigger box culls slightly less aggressively but avoids per-frame recompute.
Step 3: Compute worst-case bounds at edit time. Write an editor script that scrubs through every animation clip and unions the resulting renderer.bounds. Bake the result into the SkinnedMeshRenderer’s Local Bounds:
#if UNITY_EDITOR
[UnityEditor.MenuItem("Tools/Compute Skinned Bounds")]
static void Compute()
{
var smr = Selection.activeGameObject.GetComponent<SkinnedMeshRenderer>();
var animator = smr.GetComponentInParent<Animator>();
var bounds = new Bounds(smr.transform.localPosition, Vector3.zero);
foreach (AnimationClip clip in animator.runtimeAnimatorController.animationClips)
{
for (float t = 0; t <= clip.length; t += 0.1f)
{
clip.SampleAnimation(animator.gameObject, t);
bounds.Encapsulate(smr.localBounds);
}
}
smr.localBounds = bounds;
}
#endif
Step 4: For attached weapons, parent to the mesh. A weapon as a separate Renderer is culled independently. If you want it visible whenever the character is, place it under the same SkinnedMeshRenderer’s GameObject or wide-set bounds on its own renderer.
Step 5: Avoid root scale baking. If your character’s import has scale 100 baked into root, bone-space bounds are tiny relative to world-space. Reset root bone scale to (1,1,1) and re-author the rig if needed.
Cost Tradeoffs
Update When Offscreen costs roughly 0.05–0.2ms per character per frame depending on bone count. For 50 visible characters, this is meaningful. Use it for protagonists; bake bounds for crowds.
“Skinned bounds are static unless you tell them otherwise. Animations beyond bind pose require Update When Offscreen or larger Local Bounds.”
Related Issues
For animation playback issues, see Animation Not Playing. For LOD issues, see LOD Group Not Switching.
Update When Offscreen for heroes. Bake bounds for crowds. The character stops vanishing.