AI Assistant Setup

Give your AI coding assistant context about the Board SDK. Copy the content below and save it to your project as CLAUDE.md, .cursorrules, or whatever format your tool uses.

# Board Game Development

Build games for the Board platform using the Unity SDK.

## Platform Requirements

- Unity 2022.3 LTS or later (Unity 6 supported)
- Android 13 (API 33), ARM64, IL2CPP
- Unity Input System 1.7.0+
- Board OS 1.3.8+

**Unity 6 only**: Set Application Entry Point to "Activity" (not Game Activity) in Player Settings > Android > Other Settings.

## Quick Reference

### Touch Input

```csharp
// Get all active contacts (pieces and fingers)
BoardContact[] contacts = BoardInput.GetActiveContacts();

// Filter by type
BoardContact[] pieces = BoardInput.GetActiveContacts(BoardContactType.Glyph);
BoardContact[] fingers = BoardInput.GetActiveContacts(BoardContactType.Finger);
```

**BoardContact properties:**
- `contactId` - Unique identifier for this contact
- `glyphId` - Which piece in the set (0 to N-1), or -1 for fingers
- `screenPosition` - Position in screen coordinates
- `orientation` - Rotation in radians
- `phase` - Began, Moved, Stationary, Ended, Canceled
- `isTouched` - Whether a finger is touching this piece
- `type` - Glyph (piece) or Finger

```csharp
// Check if a contact ID is still active
bool isActive = BoardInput.GetActiveContacts().Any(c => c.contactId == contactId);
```

### Players & Sessions

```csharp
// Get current players
BoardPlayer[] players = BoardSession.players;

// Get the active profile (device owner)
BoardPlayer activeProfile = BoardSession.activeProfile;

// Show player selector to add a player
await BoardSession.ShowAddPlayerSelector(backgroundColor, fontStyle);

// Show selector to replace a specific player
await BoardSession.ShowReplacePlayerSelector(playerToReplace, backgroundColor, fontStyle);

// Listen for player changes
BoardSession.playersChanged += OnPlayersChanged;
BoardSession.activeProfileChanged += OnActiveProfileChanged;
```

### Save Games

```csharp
// Create a save
var metadata = new BoardSaveGameMetadataChange(
    description: "Level 5 Complete",
    playedTime: TimeSpan.FromMinutes(45),
    gameVersion: Application.version,
    coverImage: screenshotTexture  // optional
);
string saveId = await BoardSaveGameManager.CreateSaveGame(saveData, metadata);

// Load a save
byte[] data = await BoardSaveGameManager.LoadSaveGame(saveId);

// List saves for current players
BoardSaveGameMetadata[] saves = await BoardSaveGameManager.GetSaveGamesMetadata();
```

### Pause Menu

```csharp
// Configure pause screen (call once at startup)
BoardApplication.SetPauseScreenContext(
    applicationName: "My Game",
    showSaveOptionUponExit: true,  // shows "Exit & Save" button
    customButtons: null,
    audioTracks: null
);

// Listen for pause actions
BoardApplication.pauseScreenActionReceived += (action, audioTracks) => {
    switch (action) {
        case BoardPauseAction.Resume:
            // User resumed
            break;
        case BoardPauseAction.ExitAndSave:
            // Save game, then call BoardApplication.Exit()
            break;
        case BoardPauseAction.Exit:
            BoardApplication.Exit();
            break;
    }
};

// Exit the game (returns to Library)
BoardApplication.Exit();
```

## Common Patterns

### Tracking Pieces Across Frames

```csharp
private Dictionary<int, GameObject> trackedPieces = new();

void Update() {
    var contacts = BoardInput.GetActiveContacts(BoardContactType.Glyph);
    var activeIds = new HashSet<int>();

    foreach (var contact in contacts) {
        activeIds.Add(contact.contactId);

        if (contact.phase == BoardContactPhase.Began) {
            // New piece placed
            var piece = Instantiate(piecePrefabs[contact.glyphId]);
            trackedPieces[contact.contactId] = piece;
        }

        if (trackedPieces.TryGetValue(contact.contactId, out var obj)) {
            // Update position/rotation
            obj.transform.position = ScreenToWorld(contact.screenPosition);
            obj.transform.rotation = Quaternion.Euler(0, 0, -contact.orientation * Mathf.Rad2Deg);
        }
    }

    // Clean up lifted pieces
    foreach (var id in trackedPieces.Keys.ToList()) {
        if (!activeIds.Contains(id)) {
            Destroy(trackedPieces[id]);
            trackedPieces.Remove(id);
        }
    }
}
```

### Detecting Piece Touch

```csharp
foreach (var contact in BoardInput.GetActiveContacts(BoardContactType.Glyph)) {
    if (contact.isTouched) {
        // Finger is touching this piece
    }
}
```

## Important Notes

- **Glyph IDs** are indices (0 to N-1) within your Piece Set
- **Contact IDs** are unique per contact lifetime, persist across frames
- Changing `BoardInput.settings` at runtime cancels all contacts
- Switching Piece Set models causes a brief delay (no input during load)
- Always call `BoardApplication.Exit()` when exiting—don't just quit
- Session always requires at least one Profile player

## Full Documentation

https://harris-hill.github.io/board-docs/