# Skeleton

<Badge tone="type">class · data model</Badge> <Badge>transport assembly</Badge>

```csharp
public sealed class Skeleton
```

**Namespace:** `AR51.Unity.SDK` (transport assembly, separate from `.Client`).

The per-frame skeleton payload the client receives from CVS. One `Skeleton` is one tracked track's pose for one frame: an identity (`Id` / `AnchorId`), capture/receive timestamps, optional head rotation and wrist positions, and a **packed float buffer** of joint positions you read through the generic getters. [`SkeletonConsumer`](/docs/sdk-api/unity-api/classes/skeletonconsumer) maintains one [`Person`](/docs/sdk-api/unity-api/classes/person) per `Id` and feeds it the latest `Skeleton`.

You don't normally construct it; you receive it from [`SkeletonConsumer`](/docs/sdk-api/unity-api/classes/skeletonconsumer) events (`OnSkeletonReceived` / `OnCvsSkeletonReceived`) as `SkeletonEventArgs.Skeleton`, or read the most recent one from [`Person.LastSkeleton`](/docs/sdk-api/unity-api/classes/person).

:::caution[Units]
Joint and wrist positions are Unity world-space **meters**; `HeadLocalRotation` is a `Quaternion`. Request `Vector3` from the generic getters (`GetPositions<Vector3>()`) to get Unity world-space meters directly.
:::

:::note[Packed positions — use the generic getters]
Positions arrive as a packed `byte[]` buffer. Don't unpack them by hand — call [`GetPositions<Vector3>()`](#getpositions) (and the hand getters) to decode into Unity types. Joint indices into the returned array follow the [`Joints`](/docs/sdk-api/unity-api/classes/joints) constants.
:::

## Fields

<div className="apiPropTable">

| Field | Type | Description |
|---|---|---|
| **Identity** | | |
| `Id` | `string` | skeleton / track id — the per-track identifier (the `"PersonId"`); the key [`SkeletonConsumer`](/docs/sdk-api/unity-api/classes/skeletonconsumer) stores its [`Person`](/docs/sdk-api/unity-api/classes/person) under |
| `AnchorId` | `string` | spatial anchor this skeleton is expressed in (`""` / `null` = world) |
| `DeviceId` | `string` | originating device id |
| `Source` | `Sources` | where the frame came from — `Sources.CVS` or `Sources.Other` |
| `skeletonReply` | `SkeletonReply` | underlying proto reply (entity id/name, confidence) — transport type |
| `CharacterPrefab` | `string` | optional prefab name the server requests for this skeleton |
| **Timing** | | |
| `CaptureTimestamp` | `double` | capture time at CVS — **seconds since Unix epoch (1970 UTC)** |
| `ReceivedTimestamp` | `double` | local receive time — **seconds since Unix epoch (1970 UTC)** |
| **Head & wrists** | | |
| `HasHeadLocalRotation` | `bool` | `true` when `HeadLocalRotation` is present |
| `HeadLocalRotation` | `AR51.Core.Quaternion` | fused device head rotation (Core type — convert with [`Extensions`](/docs/sdk-api/unity-api/classes/extensions)) |
| `HasWristPositions` | `bool` | `true` when the wrist positions are present |
| `LeftWristPosition` | `AR51.Core.Vector3` | left wrist position — **meters** (Core type) |
| `RightWristPosition` | `AR51.Core.Vector3` | right wrist position — **meters** (Core type) |
| **Controllers** | | |
| `MultiControllerInfo` | `MultiControllerInfoRequest` | VR controller info — transport type (see [Hands, controllers & boundary](/docs/sdk-api/unity-api/hands-controllers-boundary)) |
| **Playback override** | | |
| `OverridePositions` | `byte[]` | override the packed positions buffer (used by playback) — `{ get; set; }` |

</div>

`public static readonly Skeleton Empty;` — the empty/no-op skeleton sentinel.

:::caution[ID semantics]
`Id` is the **skeleton / track** id — present from the first frame, stable for the life of the track, and the key under which [`SkeletonConsumer`](/docs/sdk-api/unity-api/classes/skeletonconsumer) stores its [`Person`](/docs/sdk-api/unity-api/classes/person). It is **not** the recognized identity: `skeletonReply.EntityId` (and `EntityDisplayName`) is the *identified* entity, populated only once the skeleton is recognized. Test it with [`Extensions.IsIdentified()`](/docs/sdk-api/unity-api/classes/extensions); [`SkeletonConsumer.IgnoreUnidentified`](/docs/sdk-api/unity-api/classes/skeletonconsumer) uses the same check.
:::

`Core.Vector3` / `Core.Quaternion` are transport (`AR51.Core`) types, not `UnityEngine` types — convert them with the [`Extensions`](/docs/sdk-api/unity-api/classes/extensions) helpers. Prefer the generic getters below, which already return Unity types when you request `Vector3`.

## Constructors

<div className="apiPropTable">

| Constructor | Use |
|---|---|
| `Skeleton(SkeletonReply skeleton)` | wrap a received proto reply (the normal CVS path) |
| `Skeleton(double captureTime, string id, string anchorId, byte[] rawPositions, …)` | build from raw packed buffers — playback / synthetic sources; optional `deviceId`, `characterPrefab`, and raw left/right hand position & rotation buffers |

</div>

## Method summary

**Positions** — generic decoders over the packed buffers; call with `Vector3` for Unity world-space meters.

| Method | Returns | |
|---|---|---|
| [`GetPositions<T>`](#getpositions) | `T[]` | body joints (indices follow [`Joints`](/docs/sdk-api/unity-api/classes/joints)) |
| [`GetLeftHandPositions<T>`](#hand-positions) | `T[]` | left-hand joint positions |
| [`GetRightHandPositions<T>`](#hand-positions) | `T[]` | right-hand joint positions |
| [`GetLeftHandRawPositions<T>`](#hand-positions) | `T[]` | left-hand raw (pre-fuse) positions |
| [`GetRightHandRawPositions<T>`](#hand-positions) | `T[]` | right-hand raw (pre-fuse) positions |

**Rotations**

| Method | Returns | |
|---|---|---|
| [`GetLeftHandRotations<T>`](#hand-rotations) | `T[]` | left-hand joint rotations |
| [`GetRightHandRotations<T>`](#hand-rotations) | `T[]` | right-hand joint rotations |

**Other**

| Method | Returns | |
|---|---|---|
| [`ToString`](#tostring) | `string` | |

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

## Method details

### GetPositions

<ApiBody kind="method">

```csharp
public T[] GetPositions<T>() where T : struct;
```

Decodes the packed body-joint buffer into an array of `T`. Pass `Vector3` to get Unity world-space positions in **meters**. The returned array is indexed by the [`Joints`](/docs/sdk-api/unity-api/classes/joints) constants (OpenPose-25 body layout, plus hands when present) — e.g. `positions[Joints.Neck]`, `positions[Joints.LWrist]`.

<Returns type="T[]" typeHref="/docs/sdk-api/unity-api/classes/joints">joint positions, indexed by `Joints` (meters when `T` is `Vector3`)</Returns>

<Example inferred>

```csharp
consumer.OnSkeletonReceived += (s, e) =>
{
    Vector3[] joints = e.Skeleton.GetPositions<Vector3>();   // world-space meters
    Vector3 neck  = joints[Joints.Neck];
    Vector3 lWrist = joints[Joints.LWrist];
    Debug.Log($"{e.Skeleton.Id}: neck at {neck} (m)");
};
```

</Example>

</ApiBody>

### Hand positions

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

```csharp
public T[] GetLeftHandPositions<T>()  where T : struct;
public T[] GetRightHandPositions<T>() where T : struct;
public T[] GetLeftHandRawPositions<T>()  where T : struct;   // pre-fuse / un-smoothed
public T[] GetRightHandRawPositions<T>() where T : struct;
```

Per-hand joint positions, decoded like [`GetPositions<T>()`](#getpositions); request `Vector3` for Unity world-space **meters**. The `…Raw…` variants return the un-fused source positions (before head/wrist fusion and smoothing). Hand-joint order follows the hand indices in [`Joints`](/docs/sdk-api/unity-api/classes/joints) (starting at `Joints.RightWrist = 25`).

<Returns type="T[]" typeHref="/docs/sdk-api/unity-api/classes/joints">hand-joint positions (meters when `T` is `Vector3`)</Returns>

<Example inferred>

```csharp
Vector3[] left  = skeleton.GetLeftHandPositions<Vector3>();
Vector3[] right = skeleton.GetRightHandPositions<Vector3>();
// or the fused wrist shortcut, when present:
if (skeleton.HasWristPositions)
{
    Vector3 lw = skeleton.LeftWristPosition.ToUnity();   // Core.Vector3 → meters, via Extensions
}
```

</Example>

</ApiBody>

### Hand rotations

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

```csharp
public T[] GetLeftHandRotations<T>()  where T : struct;
public T[] GetRightHandRotations<T>() where T : struct;
```

Per-hand joint rotations. Request `Quaternion` for Unity rotations. Indexed in the same hand-joint order as the hand position getters.

<Returns type="T[]">hand-joint rotations (Unity `Quaternion` when `T` is `Quaternion`)</Returns>

</ApiBody>

### ToString

<ApiBody kind="method">

```csharp
public override string ToString();
```

Human-readable summary of the skeleton (id and key fields) for logging/debugging.

</ApiBody>

## See also

- [`Joints`](/docs/sdk-api/unity-api/classes/joints) — the index map for every array the position getters return
- [`Person`](/docs/sdk-api/unity-api/classes/person) — the tracked-person wrapper that holds the latest `Skeleton` (`LastSkeleton`) and decoded `Positions`
- [`SkeletonConsumer`](/docs/sdk-api/unity-api/classes/skeletonconsumer) — receives `Skeleton` frames and raises the events that hand you one
- [`Extensions`](/docs/sdk-api/unity-api/classes/extensions) — `IsIdentified()` plus `Core`↔Unity `Vector3`/`Quaternion` helpers
- [Skeletons & Characters](/docs/sdk-api/unity-api/skeletons-and-characters) — the area overview and the *Connect → skeleton → character* sequence
- [Class index](/docs/sdk-api/unity-api/classes) — all Unity SDK types
