# SkeletonConsumer

<Badge tone="type">class</Badge> <Badge>Unity component</Badge> <Badge>singleton</Badge>

```csharp
public class SkeletonConsumer : Singleton<SkeletonConsumer>, ISkeletonListener
```

**Inherits:** [`Singleton<SkeletonConsumer>`](/docs/sdk-api/unity-api/connection) — a `MonoBehaviour` base. Reach the live instance with `SkeletonConsumer.Instance`. **Implements:** `ISkeletonListener` (its `OnSkeleton(...)` callback is how incoming frames are delivered).

The heart of the SDK runtime. Drop one on a GameObject in the scene; it receives [`Skeleton`](/docs/sdk-api/unity-api/classes/skeleton) messages from CVS, keeps a [`Person`](/docs/sdk-api/unity-api/classes/person) per tracked skeleton, instantiates an [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) prefab for each, and applies smoothing / scaling / IK / foot-contact every `LateUpdate`. Configure character prefabs and tuning in the Inspector; subscribe to its events to react to detection and person-lifecycle changes. Acts as a singleton.

:::note[Units]
The AR 51 Unity SDK works in **meters** (positions/lengths) and **degrees** (angles). All joint positions read through `Person.Positions`, `HeadPosition`, etc. are Unity world-space `Vector3` in **meters**.
:::

:::tip[How-to]
This page is **reference** (facts only). For the step-by-step *connect → skeleton → drive a character* walkthrough, see the [How-to guide](/docs/sdk-api/unity-api/skeletons-and-characters).
:::

## Properties

### State & collections

The live set of tracked [`Person`](/docs/sdk-api/unity-api/classes/person)s, plus the singleton accessor. Iterate `Persons` to read joints each frame.

<div className="apiPropTable">

| Property | Type | Access | Description |
|---|---|---|---|
| **Singleton** | | | |
| `Instance` | [`SkeletonConsumer`](#instance) | `static get` | the singleton in the scene |
| **Persons** | | | |
| `ActivePerson` | [`Person`](/docs/sdk-api/unity-api/classes/person) | `get` | the local / nearest-to-camera person (headset/AR) |
| `Persons` | `IEnumerable<Person>` | `get` | all currently-tracked persons |
| `PersonById` | `Dictionary<string, Person>` | `get` | tracked persons keyed by [`Skeleton.Id`](/docs/sdk-api/unity-api/classes/skeleton) |
| `ActiveSkeleton` | `GameObject` | `get` / `set` | the active (local) skeleton GameObject |
| **Active character** | | | |
| `DefaultCharacter` | [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) | `get` | the default prefab applied when none is pinned |
| `ActiveCharacterPrefab` | [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) | `get` | the prefab currently on the active person |
| **Timing** | | | |
| `CVSTimeDifference` | `double` | field | `Time.time` − CVS `PyTime`, seconds |

</div>

### Character prefab configuration

Assign these in the Inspector. They select which [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) prefab is spawned for each tracked person — globally, or pinned per entity id / display name.

<div className="apiPropTable">

| Field | Type | Default | Description |
|---|---|---|---|
| **Prefabs** | | | |
| `CharacterPrefabs` | [`AR51Character[]`](/docs/sdk-api/unity-api/classes/ar51character) | — | the drivable character prefabs to spawn |
| `CharacterPrefabByEntityId` | [`CharacterMapping[]`](/docs/sdk-api/unity-api/skeletons-and-characters#charactermapping) | — | pin a prefab by external `EntityId` |
| `CharacterPrefabByEntityDisplayName` | [`CharacterMapping[]`](/docs/sdk-api/unity-api/skeletons-and-characters#charactermapping) | — | pin a prefab by external display name |
| `CharacterPrefabOverrideBySkeletonId` | `Dictionary<string,string>` | — | runtime override of prefab name by skeleton `Id` |
| `UseTitleAsPrefab` | `bool` | `false` | use the server-requested prefab name (`Skeleton.CharacterPrefab`) |

</div>

### Inspector tuning knobs

Many `[Header]`-grouped public fields tune visibility, smoothing, scaling, solvers, and bounds. Names/units are as shown in the Inspector; lengths are **meters**, angles **degrees**.

<div className="apiPropTable">

| Field | Type | Default | Description |
|---|---|---|---|
| **Visibility** | | | |
| `MoveLimbs` | `bool` | `true` | drive limb IK |
| `ShowModel` | `bool` | `true` | render the character mesh |
| `ShowSkeleton` | `bool` | `true` | draw the skeleton |
| `ShowControllers` | `bool` | `true` | draw controller meshes |
| `ShowSkeletonGizmo` | `bool` | `false` | draw the skeleton gizmo |
| **Gating** | | | |
| `IgnoreUnidentified` | `bool` | `false` | drop skeletons with no entity id |
| `IgnoreOutOfBounds` | `bool` | `false` | drop skeletons outside `ActiveBounds` |
| `ActiveBounds` | `Bounds` | — | bounds (meters) for accepting skeletons |
| `IgnoreLowConfidence` | `bool` | `true` | drop low-confidence joints |
| `UpdateSkeleton` | `bool` | `true` | apply incoming skeleton data each frame |
| `UpdateIk` | `bool` | `true` | run IK each frame |
| `UpdateFingerRotations` | `bool` | `true` | apply finger rotations |
| **Rotation** | | | |
| `EnableClavicleRotation` | `bool` | `true` | rotate clavicles |
| `EnableSpineRotation` | `bool` | `true` | rotate spine |
| `CopyHeadRotationFromCamera` | `bool` | `false` | drive head rotation from the main camera |
| **Scaling** | | | |
| `AutoScaleMode` | [`AutoScaleMode`](/docs/sdk-api/unity-api/skeletons-and-characters#autoscalemode) | `NonUniform` | how bone lengths rescale to the person |
| `HandAutoScaleMode` | [`HandAutoScaleMode`](/docs/sdk-api/unity-api/skeletons-and-characters#autoscalemode) | `Disabled` | hand-scaling mode |
| `UniformScale` | `float` | `1` | `0..2` world scale applied to all positions |
| `MinTorsoSpanInMeters` / `MaxTorsoSpanInMeters` | `float` | `0.2` / `1` | torso-span clamp (meters) |
| `MinArmSpanInMeters` / `MaxArmSpanInMeters` | `float` | `0.22` / `0.9` | arm-span clamp (meters) |
| `MinLegSpanInMeters` / `MaxLegSpanInMeters` | `float` | `0.35` / `1.1` | leg-span clamp (meters) |
| **Ground** | | | |
| `GroundY` | `float` | `0` | ground plane height (meters) |
| `GroundPlane` | `Transform` | — | transform defining the ground |
| **Lifecycle** | | | |
| `DestroyInactivePersons` | `bool` | `true` | destroy models for stale persons |
| `InactivePersonMaxSeconds` | `float` | `1` | seconds without updates before removal |
| `MinSkeletonUpdateBeforeBirth` | `int` | `5` | updates required before a `Person` is created |
| **Solvers** | | | |
| `FootSolver` | [`FootSolvers`](#nested-enums) | — | `Default` / `Plainer` / `Advanced` |
| `FootAdvancedSolver` | [`FootAdvancedSolvers`](#nested-enums) | — | `MoveAndRotateHips` / `MoveHipsOnly` / `MoveKnee` |
| `HandSolver` | [`HandSolvers`](#nested-enums) | — | `Default` / `Advanced` |
| `FootContactParameters` | `FootContactParameters` | — | foot-contact tuning |
| **Web streaming** | | | |
| `StreamToWeb` | `bool` | `false` | mirror frames to a web endpoint |
| `StreamToWebUri` | `string` | — | the web stream URI |

</div>

⚠️ Many more `[Header]`-grouped public fields exist for smoothing, foot-contact, finger and scale tuning — they are Inspector knobs with the names/units shown above.

## Method summary

**Static**

| Method | Returns | |
|---|---|---|
| [`Instance`](#instance) | `SkeletonConsumer` | <Badge tone="accent">static</Badge> *(property)* |

**Instance — lookup**

| Method | Returns | |
|---|---|---|
| [`TryGetPerson`](#trygetperson) | `bool` | |
| [`GetPerson`](#getperson) | [`Person`](/docs/sdk-api/unity-api/classes/person) | |
| [`GetPersonId`](#getperson) | `string` | |
| [`GetDevicePerson` / `GetDeviceCharacter`](#getdeviceperson) | [`Person`](/docs/sdk-api/unity-api/classes/person) / [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) | |
| [`TryGetPrefabByName`](#trygetprefabbyname) | `bool` | |

**Instance — characters**

| Method | Returns | |
|---|---|---|
| [`SetActiveCharacter`](#setactivecharacter) | `bool` | |
| [`OverrideCharacter`](#overridecharacter) | `void` | overloads |
| [`NextCharacter`](#nextcharacter) | `void` | |
| [`SetCharacter`](#setcharacter) | `bool` | <Badge tone="warn">deprecated</Badge> |
| [`ResetCharacter` / `ResetCharacters`](#resetcharacters) | `void` | |

→ Full descriptions in [Method details](#method-details) below.

## Events

`EventHandler` / `EventHandler<T>`. Subscribe with `+=` in C# (e.g. on `Start`), unsubscribe with `-=` on teardown.

| Event | Args | Fires when |
|---|---|---|
| `OnActivePersonChanged` | `EventHandler` | the active `Person` changed |
| `OnActiveSkeletonChanged` | `EventHandler` | the active (local) skeleton GameObject changed |
| `OnCvsSkeletonReceived` | `SkeletonEventArgs` | a raw skeleton arrived from CVS (before queue/playback gating) |
| `OnSkeletonReceived` | `SkeletonEventArgs` | a skeleton is about to be processed this frame |
| `OnPersonCreated` | [`PersonEventArgs`](/docs/sdk-api/unity-api/skeletons-and-characters#personeventargs) | a new `Person`/character was instantiated |
| `OnPersonDeleting` | [`PersonEventArgs`](/docs/sdk-api/unity-api/skeletons-and-characters#personeventargs) | a `Person` is about to be removed (model still alive) |
| `OnPersonDeleted` | [`PersonEventArgs`](/docs/sdk-api/unity-api/skeletons-and-characters#personeventargs) | a `Person` has been removed (model destroyed) |

A `Person` is created after `MinSkeletonUpdateBeforeBirth` updates (default 5) and removed after `InactivePersonMaxSeconds` (default 1) without updates (when `DestroyInactivePersons`).

<Example>

```csharp
// Spawn a log line per new character:
SkeletonConsumer.Instance.OnPersonCreated += (s, e) =>
    Debug.Log($"Spawned character for {e.Person.Id}");
```

</Example>

## Method details

### Instance

<ApiBody kind="property" tags="static">

```csharp
public static SkeletonConsumer Instance { get; }
```

The singleton consumer in the scene — the usual way to reach it from anywhere.

<Returns type="SkeletonConsumer" typeHref="#skeletonconsumer">the singleton, or `null` if none is in the scene</Returns>

<Example>

```csharp
var consumer = SkeletonConsumer.Instance;
foreach (var person in consumer.Persons)
    Debug.Log(person.Positions[Joints.RWrist]);   // right wrist in meters, world space
```

</Example>

</ApiBody>

### TryGetPerson

<ApiBody kind="method">

```csharp
public bool TryGetPerson(string personId, out Person person);
```

Look up a tracked person by id.

<Params>
<Param name="personId" type="string">a [`Skeleton.Id`](/docs/sdk-api/unity-api/classes/skeleton) / `Person.Id`</Param>
<Param name="person" type="Person" typeHref="/docs/sdk-api/unity-api/classes/person">receives the person if tracked</Param>
</Params>

<Returns type="bool">`true` if a person with that id is tracked</Returns>

<Example>

```csharp
if (SkeletonConsumer.Instance.TryGetPerson(personId, out var person))
    Debug.Log(person.HeadPosition);   // meters, world space
```

</Example>

</ApiBody>

### GetPerson

<ApiBody kind="method">

```csharp
public Person GetPerson(Transform t);
public string GetPersonId(Transform t);
```

The person (or its id) owning the character under a scene `Transform` — handy from collision/raycast hits on a character mesh.

<Params>
<Param name="t" type="Transform">any transform within a spawned character</Param>
</Params>

<Returns type="Person" typeHref="/docs/sdk-api/unity-api/classes/person">the owning person, or null</Returns>

<Example>

```csharp
void OnTriggerEnter(Collider other)
{
    var person = SkeletonConsumer.Instance.GetPerson(other.transform);
    if (person != null)
        Debug.Log($"Hit character of person {person.Id}");
}
```

</Example>

</ApiBody>

### GetDevicePerson

<ApiBody kind="method">

```csharp
public Person GetDevicePerson(string deviceId);
public AR51Character GetDeviceCharacter(string deviceId);
```

Resolve the person / character associated with a given originating device id (see [`Skeleton.DeviceId`](/docs/sdk-api/unity-api/classes/skeleton)).

<Params>
<Param name="deviceId" type="string">the originating device id</Param>
</Params>

<Returns type="Person" typeHref="/docs/sdk-api/unity-api/classes/person">the matching person, or null</Returns>

</ApiBody>

### TryGetPrefabByName

<ApiBody kind="method">

```csharp
public bool TryGetPrefabByName(string prefabName, out AR51Character prefab);
```

Look up an [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) prefab in `CharacterPrefabs` by name.

<Params>
<Param name="prefabName" type="string">name of a prefab in `CharacterPrefabs`</Param>
<Param name="prefab" type="AR51Character" typeHref="/docs/sdk-api/unity-api/classes/ar51character">receives the prefab if found</Param>
</Params>

<Returns type="bool">`true` if a prefab with that name exists</Returns>

</ApiBody>

### SetActiveCharacter

<ApiBody kind="method">

```csharp
public bool SetActiveCharacter(string characterPrefabName);
```

Change the active person's prefab to a named entry in `CharacterPrefabs`.

<Params>
<Param name="characterPrefabName" type="string">name of an [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) prefab in `CharacterPrefabs`</Param>
</Params>

<Returns type="bool">`true` if the swap succeeded (prefab found)</Returns>

<Example>

```csharp
// Swap the active person's mesh to a named character prefab at runtime
if (!SkeletonConsumer.Instance.SetActiveCharacter("RobotCharacter"))
    Debug.LogWarning("Prefab not found in CharacterPrefabs");
```

</Example>

</ApiBody>

### OverrideCharacter

<ApiBody kind="method" tags="overloads">

```csharp
public void OverrideCharacter(string personId, string overridePrefabName);
public void OverrideCharacter(Person person, string overridePrefabName);
```

Pin a specific prefab to one person (overrides auto-assignment). Use for deterministic casting; otherwise rely on `AutoScaleMode` / `CharacterPrefabByEntityId` mapping.

<Params>
<Param name="personId" type="string">the person to override (or pass the [`Person`](/docs/sdk-api/unity-api/classes/person))</Param>
<Param name="overridePrefabName" type="string">prefab name to use for that person</Param>
</Params>

<Example>

```csharp
// Always cast a specific tracked person as a named prefab
SkeletonConsumer.Instance.OverrideCharacter(personId, "GuestAvatar");
```

</Example>

</ApiBody>

### NextCharacter

<ApiBody kind="method">

```csharp
public void NextCharacter(int indexIncrease = 1);
```

Cycle the default prefab forward through `CharacterPrefabs` by `indexIncrease` entries.

<Params>
<Param name="indexIncrease" type="int" optional>how many prefabs to advance (default `1`)</Param>
</Params>

</ApiBody>

### SetCharacter

<ApiBody kind="method" tags="deprecated">

```csharp
public bool SetCharacter(string characterPrefabName);
```

⚠️ **Deprecated.** Sets the default prefab for **all** persons. Prefer [`SetActiveCharacter`](#setactivecharacter) (active person) or [`OverrideCharacter`](#overridecharacter) (a specific person).

<Returns type="bool">`true` if the prefab was found</Returns>

</ApiBody>

### ResetCharacters

<ApiBody kind="method">

```csharp
public void ResetCharacter(string personId);   // destroy that person's model
public void ResetCharacters();                  // destroy all spawned models, clear assignment
```

Destroy spawned character models and clear assignment — one person, or all of them.

<Params>
<Param name="personId" type="string">the person whose model to destroy (single-person overload)</Param>
</Params>

<Example>

```csharp
// Drop everything and let characters respawn from the next frames
SkeletonConsumer.Instance.ResetCharacters();
```

</Example>

</ApiBody>

### Nested enums

<ApiBody kind="property">

```csharp
public enum FootSolvers { Default, Plainer, Advanced }
public enum FootAdvancedSolvers { MoveAndRotateHips, MoveHipsOnly, MoveKnee }
public enum HandSolvers { Default, Advanced }
```

Solver selectors for the `FootSolver` / `FootAdvancedSolver` / `HandSolver` Inspector fields above.

</ApiBody>

:::note[Internal — not documented]
`Solve(...)` / `SolveAll()` / `OnSkeleton(...)` (the `ISkeletonListener` callback) / `NormlizeSkeletonOrigin()` [sic] / `GetNestedProperties()` / `GetPersonCollider(...)` / `GetGroundY()` / `SetHandsAdapterOverride(...)` are public but are driven internally by `LateUpdate` or by playback — you don't normally call them. The IK/solver math behind the solve is internal and not part of the documented surface.
:::

## See also

- [`Person`](/docs/sdk-api/unity-api/classes/person) — the tracked-person wrapper returned by the lookups
- [`AR51Character`](/docs/sdk-api/unity-api/classes/ar51character) — the mocap-driven mesh this consumer spawns and drives
- [`Skeleton`](/docs/sdk-api/unity-api/classes/skeleton) — the per-frame payload the consumer receives
- [`Joints`](/docs/sdk-api/unity-api/classes/joints) — the joint-index map for `Person.Positions`
- [`ServiceManager`](/docs/sdk-api/unity-api/classes/servicemanager) — the connection entry-point that must be connected first
- [`CharacterMapping`](/docs/sdk-api/unity-api/skeletons-and-characters#charactermapping) — per-entity prefab mapping
- [Class index](/docs/sdk-api/unity-api/classes) — all Unity SDK types
- How-to: [connect → drive a character](/docs/sdk-api/unity-api/skeletons-and-characters)
