# Spray

![Spray patterns overview](_media/spray-patterns.mp4 ':include :type=video autoplay loop muted playsinline')

Spray is the FocalRig feature set for burst recoil.

In most setups, you set up the [**Spray Settings**](#spray-settings) once and leave them there. While the character is firing, the only part you update shot by shot is `Shots Fired In Burst`: set it to `1` after the first shot, `2` after the second, and reset it to `0` when firing stops or the weapon reloads. If more than one spray node or gameplay helper should match, give them the same spray settings and the same `Shots Fired In Burst` value.

Start here if you are setting spray up for the first time. The two rig nodes you will usually use are [Apply Spray To Target](apply-spray-to-target.md) for weapon or muzzle aim and [Apply Spray To Camera](apply-spray-to-camera.md) for visible camera kick. The same settings can also be reused directly from Blueprint or C++.

## Start Here

1. Set up the **Spray Settings** once, either directly on the node or in a variable you feed into it.
2. Increment `Shots Fired In Burst` every time a shot fires. `0` means idle, `1` means the first shot has already fired, `2` means the second shot has already fired, and so on.
3. Reset `Shots Fired In Burst` back to `0` when the burst ends, such as when the player stops firing or reloads.
4. Use [Apply Spray To Target](apply-spray-to-target.md) when the weapon or muzzle should visibly follow the spray.
5. Add [Apply Spray To Camera](apply-spray-to-camera.md) only if you want visible camera kick.
6. If gameplay also needs the same burst math, make sure it uses the same spray settings and the same `Shots Fired In Burst` value.

Recommended first-person split:

```
Aiming branch:
First Person Camera Control → First Person Aim Target → Apply Spray To Target → Aim Weapon

Camera branch:
First Person Camera Control → Apply Spray To Camera → First Person View
```

If you only need the weapon to follow the spray, you can skip the camera branch entirely.

## Spray Settings

| Property | Type | Description |
|----------|------|-------------|
| Shots Fired In Burst | int | Current burst progress. `0` means idle. `1` means the first shot has already fired |
| Mode | [Spray Mode](#spray-mode) | How the per-shot offset is determined |
| Cumulative | [Cumulative Spray Settings](#cumulative-spray-settings) | Used when **Mode** is **Cumulative** |
| Spray Pattern | [Spray Pattern Asset](#spray-pattern-asset) | Used when **Mode** is **Spray Pattern** |
| Weight | float | Global blend for the final spray rotation. `0` = no spray, `1` = full spray |

In most setups, the spray settings themselves stay the same. While firing, the part you update shot by shot is **Shots Fired In Burst**.

Reset **Shots Fired In Burst** back to `0` when the burst ends, such as when the player stops firing or reloads.

If **Weight** is nearly zero, or if **Shots Fired In Burst** is `0` or less, the shared spray code returns no offset.

In **Spray Pattern** mode, `Shots Fired In Burst = 1` samples the first authored spray point. The shared spray settings convert burst progress to a raw pattern index by using `Shots Fired In Burst - 1` internally.

If **Mode** is **Spray Pattern** but no asset is assigned, the result is also no offset.

### Spray Mode

| Value | Description |
|-------|-------------|
| **Cumulative** | Smoothly builds toward a maximum rotation as more shots are fired |
| **Spray Pattern** | Uses an authored spray pattern asset for per-shot offsets |

### Cumulative Spray Settings

| Property | Type | Description |
|----------|------|-------------|
| Max Rotation | Rotation | Maximum rotation offset once the burst reaches **Max Shot Count** |
| Max Shot Count | int | Number of shots needed to reach that maximum rotation |
| Easing | Easing | Easing function used to ramp from no offset to **Max Rotation** |

In cumulative mode, FocalRig clamps **Max Shot Count** to at least `1`, computes `Shots Fired In Burst / Max Shot Count`, applies the selected easing function, then slerps from identity to **Max Rotation**.

That means extra shots past **Max Shot Count** stay at the maximum rotation instead of continuing to climb forever.

## Spray Pattern Asset

The **Spray Pattern** asset stores a sequence of per-shot yaw and pitch offsets. It does not track burst progress on its own. The surrounding [Spray Settings](#spray-settings) or your own gameplay code decide which shot index to use.

Use one asset anywhere you want the same authored pattern to be shared across Control Rig, Blueprint, or C++.

## Creating a Spray Pattern

1. In the Content Browser, right-click and choose **Animation > Spray Pattern**.
2. The default new asset name is `SP_NewSprayPattern`.
3. The new asset opens in the **Spray Pattern Editor**.

![Spray Pattern Editor walkthrough](_media/spray-pattern-editor.mp4 ':include :type=video preload=metadata autoplay playsinline')

## Editor

The spray pattern editor shows a 2D grid where each point represents one shot's angular offset from the base aim direction.

- Horizontal movement edits **Yaw**. Positive yaw rotates right.
- Vertical movement edits **Pitch**. Positive pitch rotates up.
- The toolbar includes a **Presets** menu.
- The viewport toolbar shows a **Preview Rate** control in shots per second.
- The viewport instructions are `LMB: Add | RMB: Remove | Drag: Move | MMB: Pan | Wheel: Zoom`.

### Presets

The current built-in presets are:

- **No Spray**
- **Tight Shotgun**
- **Wide Shotgun**
- **SMG**
- **Assault Rifle**
- **LMG**

## Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| Loop Pattern | bool | false | Whether the pattern wraps around after all entries are exhausted. When false, extra shots clamp to the last entry |
| Impact Offsets | list | one centered point | Angular offsets from the aim direction for each shot |

### Spray Point

Each entry in **Impact Offsets** has:

| Property | Type | Editor range | Description |
|----------|------|--------------|-------------|
| Yaw | float | -180 to 180 | Horizontal angular offset in degrees. Positive rotates right |
| Pitch | float | -90 to 90 | Vertical angular offset in degrees. Positive rotates up |

The dedicated spray pattern editor and viewport currently clamp pitch to `-90` through `90` degrees.

## Blueprint API

The spray pattern asset exposes two low-level Blueprint-callable functions for working directly with raw pattern points.

For most setups, the shared spray workflow described on this page is easier to keep in sync because it works with both **Cumulative** and **Spray Pattern** modes and uses the same **Shots Fired In Burst** value as the Control Rig nodes.

### Get Offset

```
GetOffset(ShotIndex: int) → Quaternion
```

Returns the raw pattern rotation for the given `ShotIndex`.

- `0` means the first point in the asset.
- Negative indices are treated as `0`.
- If **Loop Pattern** is enabled, the index wraps around.
- If **Loop Pattern** is disabled, the index clamps to the last point.
- If the asset has no impact offsets, the function returns identity.

Use this when you specifically want to sample the asset itself rather than a full [Spray Settings](#spray-settings) struct.

### Get Offset Target

```
GetOffsetTarget(Target: Vector, Space: Transform, ShotIndex: int, Weight: float = 1.0) → Vector
```

Rotates a target around the supplied **Space** transform using the raw pattern point at `ShotIndex`.

| Parameter | Description |
|-----------|-------------|
| Target | Original target position in <span class="fr-term" tabindex="0" data-tooltip-key="world-space">world space</span> |
| Space | Reference transform in <span class="fr-term" tabindex="0" data-tooltip-key="world-space">world space</span>. X = forward, Y = right, Z = up |
| Shot Index | Which raw point in the asset to sample, using 0-based indexing |
| Weight | Blend for the raw pattern point. `0.0` = no effect, `1.0` = full effect |

If the asset has no impact offsets, or if **Weight** is zero, this function returns the original **Target** unchanged.

## Blueprint Helpers

The **FocalRig Spray Function Library** is the shared spray math for full spray settings rather than raw pattern points.

| Function | What it does |
|----------|--------------|
| `GetSprayOffset` | Returns the weighted spray rotation for the current spray state |
| `GetSprayDirection` | Returns a sprayed direction in <span class="fr-term" tabindex="0" data-tooltip-key="world-space">world space</span> from an aim transform |
| `ApplySprayToTarget` | Rotates a target in <span class="fr-term" tabindex="0" data-tooltip-key="world-space">world space</span> around an aim transform |
| `ApplySprayToCamera` | Rotates a camera transform for visible camera recoil |

All transform-based helpers expect **X = forward**, **Y = right**, and **Z = up**.

`GetSprayDirection` rotates the aim transform's forward axis by the spray offset, then normalizes the result. `ApplySprayToTarget` rotates the target around the supplied transform without changing its distance from that transform's origin. `ApplySprayToCamera` only changes rotation and leaves camera translation alone.

## Space Conventions

- The Control Rig spray nodes work in <span class="fr-term" tabindex="0" data-tooltip-key="global-space">global space</span>.
- The Blueprint spray helpers and raw spray pattern Blueprint API work in <span class="fr-term" tabindex="0" data-tooltip-key="world-space">world space</span>.
- In every spray function or node, the reference transform uses **X = forward**, **Y = right**, and **Z = up**.

## Common Setups

### ControlRig-Driven Setup

Use this when the weapon itself should point toward the sprayed shot.

1. Feed the same **Spray** settings into gameplay and Control Rig.
2. Use [Apply Spray To Target](apply-spray-to-target.md) and downstream aim nodes to rotate the weapon toward the sprayed shot.
3. Read the evaluated muzzle bone or socket transform after the rig updates.
4. Fire or trace from that muzzle transform.

This is the setup used in the demo. It keeps the spawned trace or projectile perfectly aligned with the procedural animation, but it also means gameplay depends on the animation update being current.

### Gameplay-Driven Setup

Use this when your project already fires from a camera, view, or other gameplay transform.

1. Keep your existing camera or view trace setup.
2. Use `GetSprayDirection` or `ApplySprayToTarget` when you want that trace to follow the same spray.
3. Use [Apply Spray To Target](apply-spray-to-target.md) or [Apply Spray To Camera](apply-spray-to-camera.md) only where you want the rig or camera to match the same burst recoil.
4. Fire or trace from the same place your project already uses.

This setup is more flexible for existing gameplay code, but you have to make sure the rig, camera recoil, and fired shot all read from the same spray settings.
