# Adapters

<Badge tone="type">namespace · AR51.Unity.SDK</Badge>

```csharp
namespace AR51.Unity.SDK;
```

The **adapter layer** is how the SDK reads tracked hands, VR controllers, and the play-area boundary without binding to any one device. Each capability is a small **interface** (`IHandsAdapter`, `IControllerAdapter`, `IBoundaryAdapter`) that a platform/device integration implements, plus a **static factory** that resolves and caches the active implementation per platform. Consumers never `new` an adapter — they call the matching factory's `GetAdapter()`. A platform integration calls `Register<T>(platform)` once at startup.

All positions, lengths, and matrices on this page are in **Unity meters**.

:::note[Internal — not documented]
The platform fallback adapters returned when nothing is registered — `EmptyHandsAdapter`, `EmptyControllerAdapter`, `DefaultBoundaryAdapter` — are internal plumbing and not part of the public surface.
:::

## IHandsAdapter

<Badge tone="type">interface</Badge>

```csharp
public interface IHandsAdapter
```

Interface a platform/device integration implements to expose tracked hand data to the SDK. Consumers obtain an instance via [`HandsAdapterFactory.GetAdapter()`](#hands--getadapter). Joint snapshots are returned as [`HandJointInfo[]`](/docs/sdk-api/unity-api/classes/hand-data#handjointinfo); the array is indexed by `(int)HandJointType` and an empty array means no data.

### Members

| Member | Type | |
|---|---|---|
| `SourceDevice` | `string` | <Badge tone="accent">get</Badge> |
| [`GetRightJointInfos`](#hands--get-joint-infos) | `HandJointInfo[]` | <Badge tone="accent">method</Badge> |
| [`GetLeftJointInfos`](#hands--get-joint-infos) | `HandJointInfo[]` | <Badge tone="accent">method</Badge> |

`SourceDevice` identifies the tracking source backing this adapter (e.g. a device or model name) — use it for diagnostics or to branch behavior per source.

### Hands · get joint infos

<ApiBody kind="method">

```csharp
HandJointInfo[] GetRightJointInfos();
HandJointInfo[] GetLeftJointInfos();
```

Returns the current snapshot of right- (resp. left-) hand joints. Order follows the `HandJointType` enum, so `result[(int)HandJointType.IndexTip]` is the index fingertip. An empty array means no data; joints with `Confidence == 0` are considered missing.

<Returns type="HandJointInfo[]" typeHref="/docs/sdk-api/unity-api/classes/hand-data#handjointinfo">the current per-joint pose/confidence snapshot, indexed by joint type</Returns>

<Example inferred>

```csharp
var hands = HandsAdapterFactory.GetAdapter();
HandJointInfo[] right = hands.GetRightJointInfos();
if (right.Length > 0 && right[(int)HandJointType.IndexTip].Confidence > 0f)
    Debug.Log(right[(int)HandJointType.IndexTip].Position);   // Unity meters
```

</Example>

</ApiBody>

## HandsAdapterFactory

<Badge tone="type">static class</Badge>

```csharp
public static class HandsAdapterFactory
```

Static registry/factory that resolves and caches the active [`IHandsAdapter`](#ihandsadapter) per platform. Register your adapter type once at startup, then retrieve it anywhere.

### Method summary

| Method | Returns | |
|---|---|---|
| [`GetAdapter`](#hands--getadapter) | `IHandsAdapter` | <Badge tone="accent">static</Badge> |
| [`GetAdapter(PlatformType)`](#hands--getadapter) | `IHandsAdapter` | <Badge tone="accent">static</Badge> |
| [`Register<T>`](#hands--registert) | `void` | <Badge tone="accent">static</Badge> |

### Hands · GetAdapter

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

```csharp
static IHandsAdapter GetAdapter();
static IHandsAdapter GetAdapter(PlatformType platform);
```

Returns the adapter for the current runtime platform (parameterless overload, resolved from the SDK's active platform) or for a given `platform`. The first call creates and caches the instance; if no adapter is registered for the platform, a no-op fallback is returned and a warning is logged.

<Params>
<Param name="platform" type="PlatformType">platform to resolve the adapter for; see [`PlatformType`](/docs/sdk-api/unity-api/classes/servicemanager)</Param>
</Params>

<Returns type="IHandsAdapter" typeHref="#ihandsadapter">the cached adapter, or a no-op fallback</Returns>

:::caution[⚠️ needs confirmation — single cache]
The result is cached on first call **regardless of which overload is used**, so passing a different `platform` after the cache is populated returns the already-cached adapter, not a fresh one for that platform.
:::

</ApiBody>

### Hands · Register&lt;T&gt;

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

```csharp
static void Register<T>(PlatformType platform) where T : IHandsAdapter, new();
```

Associates adapter type `T` with `platform`. `T` must be public with a public parameterless constructor. Call before the first `GetAdapter` for that platform.

<Params>
<Param name="platform" type="PlatformType">platform the adapter type applies to</Param>
</Params>

:::caution[⚠️ needs confirmation — throws on re-register]
Registering the same platform twice **throws** (it uses dictionary `Add`). This differs from [`BoundaryAdapterFactory.Register<T>`](#boundary--registert), which overwrites.
:::

<Example inferred>

```csharp
HandsAdapterFactory.Register<MyQuestHandsAdapter>(PlatformType.Quest);
```

</Example>

</ApiBody>

## IControllerAdapter

<Badge tone="type">interface</Badge>

```csharp
public interface IControllerAdapter
```

Interface a platform/device integration implements to expose VR controller state. Consumers obtain an instance via [`ControllerAdapterFactory.GetAdapter()`](#controller--getadapter). State is reported as [`ControllerInfo`](#controllerinfo) snapshots.

### Members

| Member | Type | |
|---|---|---|
| `SourceDevice` | `string` | <Badge tone="accent">get</Badge> |
| `LeftControllerInfo` | [`ControllerInfo`](#controllerinfo) | <Badge tone="accent">get</Badge> |
| `RightControllerInfo` | [`ControllerInfo`](#controllerinfo) | <Badge tone="accent">get</Badge> |

`SourceDevice` identifies the controller source/device. `LeftControllerInfo` and `RightControllerInfo` are the current state snapshots of each controller.

<Example inferred>

```csharp
var controllers = ControllerAdapterFactory.GetAdapter();
ControllerInfo right = controllers.RightControllerInfo;
if (right.IsDetected && right.IndexTriggerIsPressed)
    Fire();
```

</Example>

## ControllerAdapterFactory

<Badge tone="type">static class</Badge>

```csharp
public static class ControllerAdapterFactory
```

Static registry/factory resolving and caching the active [`IControllerAdapter`](#icontrolleradapter) per platform.

### Method summary

| Method | Returns | |
|---|---|---|
| [`GetAdapter`](#controller--getadapter) | `IControllerAdapter` | <Badge tone="accent">static</Badge> |
| [`GetAdapter(PlatformType)`](#controller--getadapter) | `IControllerAdapter` | <Badge tone="accent">static</Badge> |
| [`Register<T>`](#controller--registert) | `void` | <Badge tone="accent">static</Badge> |

### Controller · GetAdapter

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

```csharp
static IControllerAdapter GetAdapter();
static IControllerAdapter GetAdapter(PlatformType platform);
```

Returns the adapter for the current runtime platform, or for a specified `platform`. Creates and caches on first call; falls back to a no-op adapter (with a warning) if none is registered.

<Params>
<Param name="platform" type="PlatformType">platform to resolve the adapter for; see [`PlatformType`](/docs/sdk-api/unity-api/classes/servicemanager)</Param>
</Params>

<Returns type="IControllerAdapter" typeHref="#icontrolleradapter">the cached adapter, or a no-op fallback</Returns>

:::caution[⚠️ needs confirmation — single cache]
Same single-cache caveat as [`HandsAdapterFactory.GetAdapter`](#hands--getadapter): the first call's result is cached regardless of the `platform` argument on later calls.
:::

</ApiBody>

### Controller · Register&lt;T&gt;

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

```csharp
static void Register<T>(PlatformType platform) where T : IControllerAdapter, new();
```

Registers adapter type `T` for `platform`. Call before first retrieval.

<Params>
<Param name="platform" type="PlatformType">platform the adapter type applies to</Param>
</Params>

:::caution[⚠️ needs confirmation — throws on re-register]
Registering the same platform twice **throws** (dictionary `Add`), the same as [`HandsAdapterFactory`](#hands--registert) and unlike [`BoundaryAdapterFactory`](#boundary--registert).
:::

</ApiBody>

## ControllerInfo

<Badge tone="type">struct</Badge>

```csharp
public struct ControllerInfo
```

State snapshot of a single VR controller. Value type; data model returned by [`IControllerAdapter`](#icontrolleradapter).

### Fields

<div className="apiPropTable">

| Field | Type | Description |
|---|---|---|
| `ControllerType` | [`ControllerType`](#controllertype) | which controller (left/right) |
| `IsDetected` | `bool` | whether the controller is currently tracked/present |
| `LocalToWorld` | `Matrix4x4` | controller pose as a local-to-world transform (position + orientation). Defaults to `Matrix4x4.identity` when not supplied. ⚠️ exact reference frame unconfirmed; **positions are in Unity meters** |
| `ButtonOneIsPressed` | `bool` | primary face button pressed (e.g. A/X). ⚠️ physical button mapping not specified in source |
| `ButtonTwoIsPressed` | `bool` | secondary face button pressed (e.g. B/Y). ⚠️ mapping not specified |
| `IndexTriggerIsPressed` | `bool` | index (front) trigger pressed |
| `HandTriggerIsPressed` | `bool` | hand/grip trigger pressed |

</div>

:::caution[⚠️ needs confirmation]
`LocalToWorld`'s exact reference frame is not stated in source (positions follow Unity meters), and the `ButtonOne`/`ButtonTwo` physical mappings (A/X, B/Y) are inferred, not specified. Treat both as device-dependent.
:::

### Constructor

<ApiBody kind="method">

```csharp
ControllerInfo(
    ControllerType controllerType,
    bool isDetected = false,
    Matrix4x4? localToWorld = null,
    bool buttonOneIsPressed = false,
    bool buttonTwoIsPressed = false,
    bool indexTriggerIsPressed = false,
    bool handTriggerIsPressed = false);
```

Builds a snapshot. Omitted optional args default to not-pressed / not-detected, and `localToWorld` to `Matrix4x4.identity`.

</ApiBody>

## ControllerType

<Badge tone="type">enum</Badge>

```csharp
public enum ControllerType
```

Identifies a controller.

| Value | Underlying | Meaning |
|---|---|---|
| `RightController` | `0` | right controller |
| `LeftController` | `1` | left controller |

## IBoundaryAdapter

<Badge tone="type">interface</Badge>

```csharp
public interface IBoundaryAdapter
```

Interface a platform integration implements to expose a play-area / guardian boundary. Consumers obtain an instance via [`BoundaryAdapterFactory.GetAdapter()`](#boundary--getadapter).

### Members

| Member | Type | |
|---|---|---|
| `BoundaryName` | `string` | <Badge tone="accent">get</Badge> |
| [`GetPoints`](#getpoints) | `Vector3[]` | <Badge tone="accent">method</Badge> |

`BoundaryName` is the identifier/name of the active boundary.

### GetPoints

<ApiBody kind="method">

```csharp
Vector3[] GetPoints();
```

Returns the boundary outline as ordered points.

<Returns type="Vector3[]">the boundary outline, as ordered points in Unity meters</Returns>

:::caution[⚠️ needs confirmation]
The coordinate space (world vs local) and whether the polygon is closed (last point joins the first) are **not stated in source**. Positions are in Unity meters.
:::

</ApiBody>

## BoundaryAdapterFactory

<Badge tone="type">static class</Badge>

```csharp
public static class BoundaryAdapterFactory
```

Static registry/factory resolving and caching the active [`IBoundaryAdapter`](#iboundaryadapter) per platform.

### Method summary

| Method | Returns | |
|---|---|---|
| [`GetAdapter`](#boundary--getadapter) | `IBoundaryAdapter` | <Badge tone="accent">static</Badge> |
| [`GetAdapter(PlatformType)`](#boundary--getadapter) | `IBoundaryAdapter` | <Badge tone="accent">static</Badge> |
| [`Register<T>`](#boundary--registert) | `void` | <Badge tone="accent">static</Badge> |

### Boundary · GetAdapter

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

```csharp
static IBoundaryAdapter GetAdapter();
static IBoundaryAdapter GetAdapter(PlatformType platform);
```

Returns the adapter for the current runtime platform, or for a specified `platform`. Creates and caches on first call; falls back to a default boundary adapter (with a warning) if none is registered.

<Params>
<Param name="platform" type="PlatformType">platform to resolve the adapter for; see [`PlatformType`](/docs/sdk-api/unity-api/classes/servicemanager)</Param>
</Params>

<Returns type="IBoundaryAdapter" typeHref="#iboundaryadapter">the cached adapter, or the default boundary adapter</Returns>

</ApiBody>

### Boundary · Register&lt;T&gt;

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

```csharp
static void Register<T>(PlatformType platform) where T : IBoundaryAdapter, new();
```

Registers adapter type `T` for `platform`.

<Params>
<Param name="platform" type="PlatformType">platform the adapter type applies to</Param>
</Params>

:::note[Differs from the hands/controller factories]
Unlike [`HandsAdapterFactory`](#hands--registert) and [`ControllerAdapterFactory`](#controller--registert), this uses an **indexer assignment**, so re-registering a platform **overwrites** the previous type rather than throwing.
:::

</ApiBody>

## See also

- [Hand data](/docs/sdk-api/unity-api/classes/hand-data) — `HandJointInfo`, `HandJointType`, and the hand-joint helpers returned by `IHandsAdapter`
- [`CustomHandsAdapter`](/docs/sdk-api/unity-api/classes/scene-components) — an in-scene `MonoBehaviour` that implements `IHandsAdapter` from Inspector-assigned transforms
- [`ServiceManager`](/docs/sdk-api/unity-api/classes/servicemanager) — SDK lifecycle and the active [`PlatformType`](/docs/sdk-api/unity-api/classes/servicemanager) the factories resolve against
- [Class index](/docs/sdk-api/unity-api/classes) — all Unity SDK types
