# Performance

> What SEC costs, the three knobs that cut it, and how to read stat SEC on your own hardware.

Markdown version of https://rigbak.com/docs/soulslike-combat/performance, a page of the Soulslike Enemy Combat documentation. Link that page when citing it.

> Movement direction sampling is where the plugin's time goes. Three properties cut it, and `stat SEC` tells you whether you need to.

## Three knobs, in order

All three sit on the **Movement Evaluator** component, on the AI controller. **Num Samples** heads the section itself; **Enable Avoidance** and **Enable Nav Aware Sampling** head the **Avoidance** and **Nav Sampling** groups nested under it.

| Knob | Default | What changing it buys |
| --- | --- | --- |
| **Num Samples** | 16 | Directions scored each pass. Drop it to 8 and the scoring loop, the nav probes and the hazard scoring all halve together. Range 4 to 32. |
| **Enable Avoidance** | on | Untick it and the sphere overlap for neighboring pawns stops. Worth it for solo bosses and sparse fields where enemies do not pack together. |
| **Enable Nav Aware Sampling** | on | Untick it and the navmesh probes stop. The ledge guard and hazard-area avoidance go with them, so enemies will strafe off edges. |

**Details panel: Movement Evaluator Component** (component, in AEnemyControllerBase)

**Category: Movement Evaluator**

| Property | Value |
| --- | --- |
| Num Samples | 16 |

**Category: Movement Evaluator > Avoidance**

| Property | Value |
| --- | --- |
| Enable Avoidance | true |

**Category: Movement Evaluator > Nav Sampling**

| Property | Value |
| --- | --- |
| Enable Nav Aware Sampling | true |
| Nav Check Interval | 0.1 (advanced) |
| Nav Stagger Stride | 4 (advanced) |

*Num Samples scales the direction-scoring loop. Avoidance and nav sampling both refresh at 10 Hz by default.*

Start with **Num Samples**. It sets the loop count for both direction scoring and the nav probes, so halving it moves the two largest numbers on the profile together. Setting **Avoidance Weight** to 0 skips the overlap query as well, without unticking anything.

> **Warning**
>
> Turn the debug overlays off before you profile. `SEC.Debug.All 1` draws a text block above every pawn within `SEC.Debug.MaxDistance` (4000 cm by default), and that draw cost lands in your frame while showing up nowhere in `stat SEC`. Run `SEC.Debug.All 0` first, then measure.

---

## What runs when

Cadence is how often the work happens. The cost column is what one pass does.

| System | Runs | Cost per pass |
| --- | --- | --- |
| **Movement** | Every tick that a brain or an **AI Movement** task asks for it | Scores **Num Samples** directions and picks a winner |
| **Avoidance** | Inside the movement pass, pawn overlap refreshed 10 times a second | One sphere overlap, then a penalty per neighbor found |
| **Nav sampling** | Inside the movement pass, cache refreshed every **Nav Check Interval** (0.1 s), spread across **Nav Stagger Stride** (4) frames | Projects the sampled directions onto the navmesh |
| **Detour check** | Every **Detour Recheck Interval** (0.5 s) while the enemy sits inside **Hybrid Switch Distance** | One path query weighed against the straight-line distance |
| **Decision context** | Every **Evaluation Cooldown** (0.1 s) while fighting under the native brain; every tick under the **Build Decision Context** task | Reads distance, angle, health and tags; traces line of sight at most ten times a second, and at once when the focus changes |
| **Action scoring** | Every **Evaluation Cooldown** (0.1 s), and only while the enemy is fighting | Hard gates, then a score for every action still standing |
| **Reaction scoring** | Once per trigger rule that an incoming stimulus matches | Gates and scores the reactions that rule allows |
| **Awareness** | On a timer at **Evaluation Interval** (0.1 s) | Advances a meter per remembered actor and ages the rest |
| **Brain** | Every tick, with action scoring gated inside it | Picks a phase, then drives movement and action scoring |
| **Role assignment** | Every **Role Reassignment Interval** (8 s), and at most once a frame when an alert reaches a whole group | One fitness score per combatant per candidate role |
| **Threat detection** | 10 times a second while evaluation is on, and not at all while it is off | One angle check against the focus actor |
| **Melee tracing** | Every tick between the start and the end of a trace window | One swept trace per socket, sub-stepped by how fast the weapon moved |
| **Approach window** | Every tick while an approach window is open | Steers one pawn toward its approach target |
| **Territory** | Only when something asks | Route arithmetic, or one navmesh query for a wander spot |
| **Combat tokens** | On claim and release | Slot bookkeeping on the target |

Idle enemies are cheap on purpose. Territory, tokens and reactions do nothing until something calls them. The brain skips its movement pass while an enemy walks a patrol leg, waits out a post's dwell, or looks around a spot it reached, and it skips action scoring outright unless the enemy is in combat. Melee tracing, action execution and the approach window each keep their tick switched off until they have work.

---

## Match the tuning to the crowd

What dominates changes with the crowd, and that decides where tuning pays off.

| Scenario | Active enemies | What dominates | What to do |
| --- | --- | --- | --- |
| Soulslike duel | 1 to 5 | Nothing worth tuning | Leave the defaults alone. |
| Action RPG pack | 10 to 20 | Direction sampling, with avoidance climbing as they close on you | Lower **Avoidance Radius** (100 cm) before cutting samples, so fewer neighbors reach the penalty loop. |
| Horde | 50 to 100 | Direction sampling across the pack | Cut **Num Samples** to 8. This is the largest single lever. |
| Stress test | 150+ | Direction sampling and nav probes | Cut samples, raise **Nav Stagger Stride** (under **Nav Sampling**, behind the **Advanced** expander) so fewer enemies sweep on the same frame, and raise **Evaluation Cooldown** on enemies out of the fight. |

*Interactive diagram on the page.*

The slider models linear scaling from an illustrative per-enemy figure. It shows the shape; your machine gives you the numbers, through `stat SEC`.

Cost scales roughly with the number of enemies running a movement pass every frame. An enemy standing at a post, waiting out a dwell, or dead costs a fraction of one in a fight.

---

## Debug it

Open the console (**~**) and run:

```
stat SEC
```

| Stat | Covers |
| --- | --- |
| `MovementEvaluator - Total` | The full movement pass, containing the four below it |
| `MovementEvaluator - Direction Scoring` | The sample loop |
| `MovementEvaluator - Avoidance` | Throttled overlap scoring |
| `MovementEvaluator - Nav Sampling` | The nav validity cache refresh |
| `MovementEvaluator - Detour Query` | The path-length detour check |
| `Decision Context - Calculate` | Builds the distance, angle, sight, health and tags an action pick reads |
| `ActionEvaluation - Scoring` | Gates and scores every candidate action |
| `ReactionEvaluation - Scoring` | Gates and scores reactions for one stimulus |
| `Awareness - Evaluate` | One pass over remembered actors |
| `Brain - Tick` | The native brain tick, containing action scoring and the movement pass |
| `RoleSubsystem - Role Distribution` | The global role assignment pass |
| `ThreatDetection - Tick` | The per-tick threat level |

> **Warning**
>
> Two of these rows are totals, not separate costs. `MovementEvaluator - Total` contains the four movement rows under it, and `Brain - Tick` contains `ActionEvaluation - Scoring` and the whole movement pass whenever the native brain drives the enemy. Adding the column up counts that work two or three times. Under a StateTree the same work runs with no enclosing brain tick, so `Brain - Tick` reads zero and the rest stand on their own.

Melee tracing and the approach window carry no stat line. Measure those in Unreal Insights.

For log-based diagnosis, see [Debugging & Troubleshooting](https://rigbak.com/docs/soulslike-combat/troubleshooting).

<details>
<summary>Filtering an Unreal Insights capture (Advanced)</summary>

The same scopes publish CPU trace events, so an Insights capture can be filtered down to the plugin:

`SEC_MovementEvaluator`, `SEC_DirectionScoring`, `SEC_Avoidance`, `SEC_NavSampling`, `SEC_DetourQuery`, `SEC_DecisionContext`, `SEC_ActionScoring`, `SEC_ReactionScoring`, `SEC_Awareness`, `SEC_BrainTick`, `SEC_RoleDistribution`, `SEC_ThreatDetection`.

They nest the same way the stats do, and Insights draws that nesting for you, which is the easier read when the totals confuse you.

</details>

---

## Slow down the thinking

Two cadences decide how often an enemy makes a decision, and both are safe to widen for enemies that are background dressing rather than the fight.

**Evaluation Cooldown** (0.1 s) spaces action picks. It sits on the **SEC Brain** component for a native brain, and on the **Poll Action** task for a StateTree. Raise it to 0.25 for grunts that do not need ten decisions a second.

**Role Reassignment Interval** (8 s) is how often global role assignment re-runs. It lives in **Project Settings > Plugins > Soulslike Enemy Combat > Combat Roles > Timing**, so it is one value for the whole project. Set it to 0 and the periodic pass does not start, leaving role assignment to whatever asks for it.

<details>
<summary>The rest of the role timing (Advanced)</summary>

**Category: Timing**

| Property | Value |
| --- | --- |
| Role Reassignment Interval | 8 |
| Min Time in Role | 8 |
| Reevaluate Targets on Reassignment | false |

**Min Time in Role** favors an enemy's current role for a stretch, which stops role thrashing without touching the pass cost.

**Reevaluate Targets on Reassignment** adds a target-selection pass for every registered AI to each cycle. It costs nothing in a single-target setup and is worth watching in a multi-target one.

Threat detection updates ten times a second, does one angle check, and stops ticking while evaluation is off, so it rarely shows up. **Set Tick Interval** on the **Threat Detection** component sets the seconds between updates (0.1 by default); 0 updates every frame.

</details>

---

## What perception costs

Awareness runs its own bookkeeping on a timer, but the sensing underneath it is Unreal's perception system, and that is the part that scales with your level.

**Sight Radius** (1500 cm) is the biggest lever, because it decides how many stimulus sources the sight sense considers each update. Cut it to what the encounter needs. **Peripheral Vision Half Angle** (90 degrees, a 180 degree cone) narrows what qualifies before a trace happens.

Untick **Enable Sight**, **Enable Hearing** or **Enable Damage Sense** on the **Awareness Config** to drop a sense from the perception component the config creates. An enemy that reacts only to being hit needs neither of the first two. The three toggles gray out when **Auto Create Perception Component** is off, because a perception component you place yourself keeps its own sense configuration.

<details>
<summary>Trimming awareness memory (Advanced)</summary>

**Evaluation Interval** (0.1 s) is how often the component advances its meters and ages entries. Raise it and detection ramps in coarser steps; the meter maths compensates, so a slower interval changes responsiveness rather than the outcome.

**Track Last Known Location** records a position and velocity per remembered actor. Untick it in memory-tight scenarios, at the cost of the investigate behavior that reads those values.

**Sight Max Age** (5 s) and **Hearing Max Age** (4 s) set how long Unreal's perception system holds a stimulus after the target leaves. Shorter ages mean fewer remembered actors for the awareness pass to walk each interval.

</details>

---

## What melee tracing costs

Tracing enables its own tick when a swing starts and disables it when the swing ends, so an enemy that is not attacking costs nothing here.

While a swing runs, each registered socket sweeps every frame, split into sub-steps so a fast weapon cannot skip past a thin target between frames. **Sub Step Distance** (10 cm) is the longest gap one sweep step may cover, and **Max Sub Steps** (20) caps the count however many the speed asks for.

Raise **Sub Step Distance** to trade precision for fewer sweeps. The cost multiplier that matters more is socket count: three registered sockets on a weapon is three sweeps per sub-step. See [Melee Trace](https://rigbak.com/docs/soulslike-combat/melee-trace).

---

<details>
<summary>What the plugin already throttles for you (Advanced)</summary>

None of these need setting up. They are the reason the default numbers hold at a pack size.

- **Sample cache.** The trigonometry for the direction fan is computed once and rebuilt only when **Num Samples** changes.
- **Throttled avoidance.** The pawn overlap query runs 10 times a second, not every frame. The scoring in between reads the cache.
- **Staggered nav sweeps.** **Nav Check Interval** and **Nav Stagger Stride** spread full nav sweeps across frames, so a whole pack does not sweep on the same one. Both sit in the **Nav Sampling** group behind its **Advanced** expander.
- **Timer-based roles.** Role assignment runs off a timer on its interval, outside the frame loop.
- **Hybrid movement.** Beyond **Hybrid Switch Distance** (800 cm) the enemy pathfinds instead of sampling directions, so distant enemies skip the expensive loop.
- **Conditional ticks.** Melee tracing, action execution and the approach window each start with their tick disabled and enable it only for the window they need.
- **Phase gating.** The brain skips its movement pass while it owns the pawn for a patrol leg, a dwell or a look-around, and skips action scoring entirely outside combat.
- **Line of sight on a clock.** The decision context traces line of sight ten times a second, and at once when the focus changes, since actions are picked at that rate.
- **Weapon replication.** **SEC Weapon Base** sends updates ten times a second, and at once when it is equipped or released. A weapon Blueprint whose own replicated variables change often can raise **Net Update Frequency** in its Class Defaults.

</details>

<details>
<summary>Running the movement pass on your own schedule (Alternative)</summary>

The **Movement Evaluator** component does not tick. Something has to ask it for a pass, which is what the **SEC Brain** component and the StateTree **AI Movement** task do each tick.

Drive it yourself and the cadence is yours: call **Evaluate and Move** from a Blueprint timer at whatever rate the enemy deserves. A background enemy on a 0.2 s timer costs a fifth of one evaluating every frame, and looks it, so reserve this for enemies the player is not looking at.

**Suspend Movement** takes a duration and pauses the pass outright, which is cheaper still for an enemy that should hold position.

</details>

---

Previous page: [Troubleshooting](https://rigbak.com/docs/soulslike-combat/troubleshooting). Next page: [API Reference](https://rigbak.com/docs/soulslike-combat/api-reference)
