17 — Modular Equipment
Unsupported prefab components/renderers (particles, lines, trails, sprites and unmarked rigid meshes) are skipped with warnings. Supported skins and bones remain collected across the prefab, including ignored Animator subtrees. This does not provide independent child animation or automatic assembly.
Do not use this sample as a multi-Animator or arbitrary Prefab converter. It supports one animated body and independent rigid equipment roots. The converter permits extra Animators: a valid Animator Path wins; otherwise the first in prefab hierarchy order is used and the rest are ignored with non-blocking warnings. Animator Path does not split/filter animated child subtrees. To animate them independently, separately author/convert each Animator and manage composition in game code.
Open Scenes/ModularEquipment.unity and press Play. The body, sword and shield are separate assets. Equip/unequip either item, trigger an attack, and move between the near/mid/far/cull camera buttons. Auto attack repeats every three seconds. Toggle Manual callback controls equipment to switch between the Burst follower and this character's C# callback example.
What this example demonstrates
Resources/ECSAnimator/17_ModularEquipment/Body/Body.prefabhas one Animator and one body SkinnedMeshRenderer per LOD (three levels). The emptyattachment.right.swordandattachment.left.shieldmarkers remain on the original rig.Body_EntityDesc.assetis generated beside it; its Convert Preset remains in Authoring.- The body is converted offline into its own EntityDesc and runtime resources under
Resources/ECSAnimator/17_ModularEquipment/Body. Sword.prefab,Shield.prefaband the additional reusableCrossbow.prefabcontain an LODGroup with three identity-transform children, each with one MeshFilter and MeshRenderer. They have no skinning or Animator and use one standard URP/Lit material with GPU instancing enabled. The scene equips the sword and shield.- The demo loads the body EntityDesc through
Resources.Load, then calls the public Prepare/Spawn APIs. It loads each equipment Prefab as a mesh/material resource when needed and creates an Entities Graphics entity; it does not instantiate an equipment GameObject. CharacterRigidEquipmentAttachment.TryCreateresolves a semantic socket once. The existing BurstCharacterRigidEquipmentAttachmentSystemupdates the equipment's LocalToWorld after the character animation pose is committed.CharacterRigidEquipmentLodopts that equipment into automatic model LOD following. The system changes only its MaterialMeshInfo mesh index, keeps the same Entity/socket, clamps missing levels to the last available mesh, hides with the body, and restores on return. All mesh levels are loaded before attachment.
Model LOD and callbacks
The body's LODGroup is the model-level authority. Its thresholds are 0.4 / 0.2 / 0.01 of screen height; at this demo's 45-degree camera and LOD bias 1, the buttons at 3 / 6 / 12 metres demonstrate LOD0 / LOD1 / LOD2. At 250 metres the body and equipment are hidden. Different model bounds, FOV, scale or quality LOD bias change the switching distances. The equipment Prefab's LODGroup supplies ordered mesh levels; its own distance thresholds are not evaluated by the follower.
EquipmentRenderData loads the shared mesh/material array once per equipment type and creates render entities with bounds covering every level. Create immediately applies the body's current selection, including first equip while distant or hidden. It does not synchronously load a mesh during an LOD switch. The asset owner remains responsible for validating mesh indices and keeping dependencies loaded.
To handle LOD in C#, subscribe to ModularEquipmentDemo.OnLodChanged(int level, bool hidden). The sample observes only its one selected body in LateUpdate, calls once on the initial observation and subsequently only when Level/Hidden changes. It does not invoke delegates from Burst or attach a managed listener to every crowd member. In manual mode the sample removes CharacterRigidEquipmentLod before the callback writes mesh selection; returning to automatic mode adds it back. Never let automatic following and business code both write MaterialMeshInfo for the same equipment.
For a different game, read the public CharacterLodSelection from your chosen body; use CharacterRigidEquipmentLod.SelectMesh to reuse the same clamp/hide rules, or apply your own visual policy. On initial equip apply the current state even if no callback occurred. Remove subscriptions on disable. An equipment entity is owned by your code; destroy it before its body. A missing owner is hidden by the automatic follower, not automatically destroyed.
Animation LOD is independent: this sample uses distance bands 8 / 16 / 30 metres, hysteresis 0.5, and evaluation intervals 1 / 2 / 4 / 8 frames. Model callbacks do not signal animation-frequency changes. Attachments follow the committed animation pose; at reduced animation rates they update at that pose's rate.
The current four-second Idle uses full-body breathing, including pelvis/knee motion and the corrected swordsman stance. Inspect a nearby character or trigger Attack before assuming animation is frozen. A changing evaluation counter alone is insufficient: compare committed poses and fixed-camera rendered frames. Earlier every-frame and every-eight-frame Idle were checked this way on the Windows D3D11 development Player; that historical result is not a new Player qualification for the October 4 animation update.
| Rigid mesh | LOD0 triangles | LOD1 | LOD2 |
|---|---|---|---|
| Sword | 66 | 36 | 18 |
| Shield | 60 | 33 | 16 |
| Crossbow | 108 | 59 | 30 |
The reduced meshes originate from Blender Decimate at 0.55 / 0.28, with source UVs, normals and weights retained during authoring. The rigid export bakes the verified single-bone bind-space correction into each level. Triangle reduction does not by itself prove a frame-time improvement; use a representative Player workload on your hardware.
Adapting it
- Keep
CharacterAttachmentAuthoringmarkers on your body Prefab even after removing the weapon renderer. Assign each marker a unique semantic key and an Owner Bone in that body. - Export a rigid equipment mesh into the marker's local coordinates. Preserve the pivot, orientation and scale; copying a skinned mesh without checking its bind pose can produce an offset. This demo verifies single-bone weights and bakes the bind-space correction into a new mesh.
- Rebuild the body with Tools → ECSAnimator → Convert Character so the marker tracks enter the offline data. Geometry changes and marker changes require a rebuild.
- Load the body EntityDesc and equipment mesh/material dependencies. Spawn the body first; wait for a valid attachment observation before showing the equipment.
- Create an Entities Graphics render entity, add the attachment binding, and leave it without
Parent. The attachment system owns itsLocalToWorld; another transform writer must not overwrite it. Pass an explicitLocalOffsetfor an intentional adjustment. - Unequipping destroys only that equipment entity. Re-equipping creates a fresh entity on the same socket while the body keeps playing. Destroy the equipment before destroying its owning body; dispose animation clients, destroy the body scope and Release the prepared body.
- If a resource manager unloads assets, wait until the body prepared handle reports
Releasedand until no equipment/rendering still uses the corresponding mesh/material. This demo keeps its Resources assets loaded and does not force an unsafe unload during toggles.
Scope
This is a focused sample for one animated body and two rigid attachments. The game/sample owns the attachment entities and their lifetime; a EntityDesc does not automatically create or destroy them. This loader expects one identity-transform child MeshRenderer per LOD, one shared material and one submesh. It does not implement multi-Animator automatic splitting, recursive composition, physics drops, runtime mesh simplification or a generic GameObject converter. Equipment need not have all the body's mesh levels. Existing other Demos and original character Prefabs are unchanged.
The development-only builder and validation helpers are not required to run this imported sample. The distributed Prefabs, generated body data, runtime script and scene are complete.