# Connecting (ServiceManager & SkeletonConsumer)

A Unity client connects to AR 51 with two singleton components:

- **`ServiceManager`** — the connection entry point. Drop it in your scene; it stands up the SDK's local service host, discovers the AR 51 components on the network (CVS, calibration, …), and exposes the live connection.
- **`SkeletonConsumer`** — receives the skeleton stream, creates a `Person` (and an `AR51Character`) for each tracked skeleton, and drives it with IK every frame.

All positions are Unity world-space **meters**; rotations are `Quaternion`; angles are **degrees**. Namespace: `AR51.Unity.SDK`.

![AR 51 Unity SDK data flow: the AR 51 system streams skeletons, ServiceManager discovers and connects, SkeletonConsumer turns each tracked person into a rigged character, and your Unity scene drives it](./img/unity-data-flow-dark.webp)

## 1. Add the ServiceManager

Place a `ServiceManager` on a GameObject in your scene (or get the singleton in code). It starts the connection itself on `Start` — there is no explicit `Connect()` call; placing the component and successful discovery is what establishes the connection. Configure it in the Inspector (or before `Start`):

```csharp
public bool UseRandomServicePort = true;   // pick a free TCP port for the local service
public int  DiscoveryPort;                  // UDP discovery port
public bool RunOMS = false;                 // host an embedded OMS in-process
public bool IsDedicatedGameServer = false;  // run this instance as a dedicated game server
public string DedicatedOmsAddress = "";     // explicit OMS address for DGS
public HandProviderType HandProvider;       // hand-tracking source (None/OVR/UltraLeap/HTC/HTC_Wave)
```

Check connection state through the static members:

```csharp
ServiceManager.IsConnected         // underlying server is up
ServiceManager.IsCvsConnected      // at least one CVS (vision) component is connected
ServiceManager.IsCalibrationConnected
ServiceManager.Server              // the live connection container (null until startup completes)
```

Subscribe to know when it's ready or when components join/leave:

```csharp
manager.OnStarted += (s, e) => Debug.Log("SDK started; Server ready");
manager.OnRegistrationChanged += (s, e) => { /* a component joined or left */ };
```

To wait for a specific service before issuing calls:

```csharp
// awaits until a component advertising the service appears (or the token cancels / times out)
await ServiceManager.WaitForService(ServiceNames.SkeletonService, token);
```

`ServiceNames` holds the canonical names: `SkeletonService`, `ObjectDetectionService` (`"6DofService"`), `CalibrationService`, `CameraFeedService`.

`Reconnect()` / `Disconnect()` resume / pause the connection afterwards.

## 2. Add the SkeletonConsumer

Add a `SkeletonConsumer` (also a singleton component) and assign one or more **character prefabs** in the Inspector. Each prefab must carry an `AR51Character` on a valid humanoid rig — validate it:

```csharp
foreach (var prefab in SkeletonConsumer.Instance.CharacterPrefabs)
    if (!prefab.ValidateCharacter())
        Debug.LogError("Invalid rig: " + string.Join(", ", prefab.GetMissingRequiredMappings()));
```

Once connected, each tracked person automatically gets its `AR51Character` instantiated and driven — you don't call IK yourself (`SkeletonConsumer` does it in `LateUpdate`). React via events:

```csharp
consumer.OnPersonCreated  += (s, e) => Debug.Log($"Character spawned for skeleton {e.Person.Id}");
consumer.OnPersonDeleted  += (s, e) => { /* a tracked person was removed */ };
consumer.OnSkeletonReceived += (s, e) => { var joints = e.Skeleton.GetPositions<Vector3>(); /* meters */ };
```

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

## 3. Minimal end-to-end example

Connect → receive skeletons → drive characters, using only the public API:

```csharp
using AR51.Unity.SDK;
using UnityEngine;

public class Bootstrap : MonoBehaviour
{
    void Start()
    {
        var manager  = ServiceManager.Instance;   // connection entry point (starts on its own)
        var consumer = SkeletonConsumer.Instance;  // drives characters from the skeleton stream

        // Prefabs are normally assigned in the Inspector; validate the rigs:
        foreach (var prefab in consumer.CharacterPrefabs)
            if (!prefab.ValidateCharacter())
                Debug.LogError("Invalid rig: " + string.Join(", ", prefab.GetMissingRequiredMappings()));

        manager.OnStarted        += (s, e) => Debug.Log("Connected: " + ServiceManager.IsConnected);
        consumer.OnPersonCreated += (s, e) => Debug.Log($"Spawned character for {e.Person.Id}");
    }

    void Update()
    {
        if (!ServiceManager.IsCvsConnected) return;

        foreach (var person in SkeletonConsumer.Instance.Persons)
        {
            GameObject model   = person.Model;          // live character GameObject
            Vector3 headWorld  = person.HeadPosition;   // meters
            Vector3 rightWrist = person.Positions[Joints.RWrist];
            // person.Confidence[...] gives per-joint confidence
        }
    }
}
```

:::note[Unverified]
A few details here are inferred from the SDK source and pending confirmation: `ServiceManager` connects in its private `Start()` (no public `Connect()`), and assigning `CharacterPrefabs` at runtime after startup may require re-validation — the normal path is to set prefabs in the Inspector before Play.
:::

## Next

- [Adding a new character](/docs/unity-sdk/add-character)
- [Unity API reference](/docs/sdk-api/unity-api) — every public type, event, and data model.
