# Playables

This page covers the playable system's architecture and how to create custom playable types.

## Playable Architecture

The playable system is built around three concepts:

1. **Playable Entries**: Database entries that define what should happen (play an animation, spawn a VFX, etc.).
2. **PlayableExecutor**: The runtime engine that processes playable entries and executes them.
3. **PlayableEntity**: Scene components that link GameObjects to the playable system.

### Execution Flow

When a playable is triggered (by an ability, interaction, trigger, etc.):

1. The `PlayableExecutor` receives the playable entry and an execution context.
2. It reads the entry's configuration.
3. It resolves targets (who/what is affected).
4. It executes the action (play animation, spawn object, play sound, etc.).
5. It handles timing (duration, speed curves, delays).

### PlayableExecutionContext

The execution context carries information about who triggered the playable and who the targets are:

```csharp
public class PlayableExecutionContext
{
    public SparkEntity Source { get; }     // Who triggered it
    public SparkEntity Target { get; }     // Who is affected
    public Vector3 Position { get; }       // World position
    public Quaternion Rotation { get; }    // World rotation
}
```

## Built-in Playable Types

Each playable type handles a specific action:

| Type            | Entry Class                   | Purpose                                                   |
| --------------- | ----------------------------- | --------------------------------------------------------- |
| Animation       | `PlayableAnimationEntry`      | Play character animations with blend, speed, and locking  |
| Sound           | `PlayableSoundEntry`          | Play audio clips with volume, pitch, and spatial settings |
| Transform       | `PlayableTransformEntry`      | Move, rotate, or scale objects over time                  |
| Coordinates     | `PlayableCoordinatesEntry`    | Move entities to specific world positions                 |
| GameObject      | `PlayableGameObjectEntry`     | Show, hide, spawn, or destroy GameObjects                 |
| Camera Override | `PlayableCameraOverrideEntry` | Temporarily switch to a different camera                  |

## Creating Custom Playable Types

To add a new playable type:

### Step 1: Create the Entry

Define a database entry for your playable type:

```csharp
[CreateAssetMenu(
    fileName = "New Screen Shake Playable",
    menuName = "Spark/Playables/Screen Shake")]
public class PlayableScreenShakeEntry : SparkDatabaseEntry
{
    [Section("Shake Settings")]
    [SerializeField] private float intensity = 0.5f;
    [SerializeField] private float duration = 0.3f;
    [SerializeField] private AnimationCurve falloff = AnimationCurve.EaseInOut(0, 1, 1, 0);

    public float Intensity => intensity;
    public float Duration => duration;
    public AnimationCurve Falloff => falloff;
}
```

### Step 2: Create the Executor Logic

Implement the logic that runs when this playable type is executed. This typically involves a coroutine or tween:

```csharp
public static class ScreenShakePlayableHandler
{
    public static IEnumerator Execute(
        PlayableScreenShakeEntry entry,
        PlayableExecutionContext context)
    {
        Camera cam = Camera.main;
        if (cam == null) yield break;

        Vector3 originalPos = cam.transform.localPosition;
        float elapsed = 0f;

        while (elapsed < entry.Duration)
        {
            float t = elapsed / entry.Duration;
            float currentIntensity = entry.Intensity * entry.Falloff.Evaluate(t);

            cam.transform.localPosition = originalPos + Random.insideUnitSphere * currentIntensity;
            elapsed += Time.deltaTime;
            yield return null;
        }

        cam.transform.localPosition = originalPos;
    }
}
```

### Step 3: Register with the Executor

Register your playable type handler so the `PlayableExecutor` knows how to run it.

## Playable Entities and Overrides

**PlayableEntity** is a MonoBehaviour that marks a GameObject as participating in the playable system. It's the bridge between scene objects and playable entries.

**PlayableTransformEntity** extends this for transform playables, providing per-entity transform override support.

**PlayableTransformEntryOverride** lets you override transform playable settings per entity. For example, the same "weapon swing" animation might need different offsets for different character models.

**PlayableAudioSource** is a component that acts as a spatial audio source for sound playables.

**CameraOverrideEntity** is attached to camera objects that can be activated by camera override playables.

## Animation Integration

### PlayableAnimationTracker

Tracks currently playing animations on an entity. Used to prevent conflicts (two animations trying to play on the same layer simultaneously).

### PlayableAnimationTargeting

Resolves which animator and which layers an animation playable should affect, based on the entity's configuration.

### AnimationSpeedCurveController

Applies speed curves to animations. This lets you speed up or slow down parts of an animation (e.g., a fast swing with a slow followthrough).

### AnimationLockEvents

Events that fire when animation locks start and end. An animation lock prevents other animations from playing and can prevent player input.

* **AnimationLockStartedEvent**: An animation lock began.
* **AnimationLockEndedEvent**: An animation lock ended.

## PlayableCoroutineHelper

A utility for running playable coroutines outside of MonoBehaviours. Since playables may be triggered from non-MonoBehaviour code (plugins, command handlers), this helper manages coroutine execution.

## Best Practices

* Keep playable entries focused on a single action. Combine multiple playables in a PlayableConfiguration for sequences.
* Use speed curves for polish. They make animations feel much more dynamic.
* Always handle null cases (missing cameras, missing animators, missing audio sources).
* Test playables in isolation before combining them in configurations.
* Use PlayableTransformEntryOverride when the same playable needs to work on characters with different proportions.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sparkframework.dev/documentation/developer-guide/core-systems/playables-system.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
