tutorial · 2026-05-03

Find Jittering NPCs and Off-Navmesh Actors with One SQL Query

Use Mythic Dev Assist's per-session entity_tracking table to detect NPC jitter, off-navmesh, and stuck actors in Unreal without scrubbing the timeline by eye.

Mythic Dev Assist
Featured on Fab Mythic Dev Assist A queryable causal world-model of UE5 for AI coding agents, over MCP.
$24.99 Get on Fab →
18
Always-on observability channels
40+
SQLite tables of session history
10000
Jitter threshold (jerk)
50000
Teleport-spike threshold (acceleration)

The bug you can see but cannot catch

You know the symptom. An NPC vibrates in place for a frame or two. An actor pops a few metres sideways. A patrolling guard slips off the navmesh and walks into a wall. You can see it happen in Play-In-Editor, but by the time your eyes register it the moment is gone, and you are left scrubbing the timeline trying to reproduce something that lasted three frames. Detecting NPC jitter, off-navmesh, and stuck actors in Unreal by eye is slow, unreliable, and impossible to do across a whole level at once.

Mythic Dev Assist (MDA) takes a different approach. It is an agent-native editor bridge that runs inside the UE5 editor process and streams the live world state into a per-session SQLite database. One of its always-on observability channels is entity_tracking, and that table turns the bug you can only see into a bug you can query. Instead of watching for jitter, you write a single SQL statement and ask the database to find every frame where it happened.

This tutorial walks through the entity_tracking schema, the kinematic derivatives MDA computes for you, and the exact query patterns that surface jitter, teleport spikes, steep slopes, and navmesh flicker. The result is a repeatable, level-wide audit instead of a guessing game.

Where the data lives: the session database

MDA writes everything it observes to a SQLite file under your project at Saved/Observations/sessions/, named after the session id. There are over 40 tables in that database, one cluster per observability channel, and they all share the same session timeline so you can correlate movement against physics, AI, animation, and more. Snapshots are flushed to disk roughly every five seconds by default, so the data is available while the session is still running.

The entity_tracking table is populated by MDA's per-tick actor tracer, which samples actors over time. Each row is one snapshot of one entity at one moment, and that is exactly the grain you need to catch a transient glitch: the misbehaving frame becomes a single row that violates a threshold, no matter how briefly it appeared on screen.

Because the channel is always on, you do not have to instrument anything in your gameplay code or add logging Blueprints. You play your level as normal, the tracer records, and the history is waiting in the database for you to interrogate afterwards.

What entity_tracking records per snapshot

Each entity_tracking snapshot carries the spatial context MDA enriches movement with on the fly: whether the actor is on the navmesh, whether it is grounded, the ground slope under it, plus the kinematic derivatives acceleration and jerk. That last pair is the key to jitter detection, and it is worth understanding where the numbers come from.

Acceleration is the rate of change of velocity, and jerk is the rate of change of acceleration. MDA computes both from an in-memory rolling history kept per entity, so by the time a snapshot lands in the database it already has these derivatives attached. You do not compute finite differences yourself in SQL; you just filter on the columns. A smoothly moving actor shows low, stable jerk. An actor that snaps back and forth across a couple of frames produces a huge jerk spike, because its acceleration reverses violently in a very short interval.

The navmesh and slope fields answer a different class of question. on_navmesh tells you whether the entity is standing on valid navigation at that snapshot; grounded tells you whether it is on the ground at all; and ground_slope describes how steep the surface beneath it is. Together they let you ask whether an actor is where the navigation system thinks it can be.

Learn the schema first with peek

Before you write SQL against an unfamiliar table, look at real rows. The mda_observe tooling can peek at a channel and show you sample data, so you see the actual column names and representative values rather than guessing at them. This matters because a query written against the wrong column name silently returns nothing, which looks deceptively like 'no bugs found'.

Peeking first means you write correct SQL on the first attempt. You confirm that entity_tracking really does expose jerk, acceleration, on_navmesh, grounded and ground_slope, you see the rough scale of the values in your project, and only then do you commit to a threshold. This is the glass-box philosophy MDA is built on: every response is designed to keep a stateless agent, or a forgetful human, grounded in what the data actually contains.

If you drive MDA through an AI coding agent over its Model Context Protocol surface, the same peek step is what lets the agent write a working query without a round of trial and error. If you are reading the database by hand with any SQLite client, peek is still the fastest way to confirm the schema before you start.

The four query patterns

Start with jitter. The canonical jitter query is WHERE jerk > 10000: any snapshot whose jerk exceeds that threshold is an actor whose motion changed direction far too abruptly to be natural. Select the entity name and the snapshot time alongside it and you get a list of exactly which actors jittered and when, which you can then go back and reproduce deliberately.

Teleport spikes are the next pattern. A teleport, a botched root-motion warp, or a physics correction shows up as an enormous one-frame acceleration, so WHERE acceleration > 50000 surfaces actors that effectively jumped position between snapshots. This is the query to run when a designer reports that something 'popped' but cannot say exactly where.

Off-navmesh and stuck actors come from the navigation fields. Filter WHERE on_navmesh = 0 (or whatever falsey value peek shows) to list every snapshot where a navigating actor was off the navmesh, which is the usual fingerprint of a guard that wandered into geometry or got pushed off its path. Combine it with grounded to separate 'off the navmesh but on the floor' from 'off the navmesh and falling'.

Steep-slope problems use ground_slope. Actors standing on surfaces too steep to legitimately occupy, for example WHERE ground_slope is below your walkable threshold, often correspond to characters clinging to ramps or wedged against collision they should be sliding off. Tune the threshold to your project's slope limits.

Navmesh flicker is the most subtle of the four. A single off-navmesh snapshot can be noise, but an actor whose on_navmesh value oscillates between snapshots is flickering on and off the navigation surface, which usually points at a navmesh seam or a thin collision gap. Group by entity and look for actors with both on-navmesh and off-navmesh rows in a short window rather than treating each row in isolation.

From a query result to a fix

A row that violates a threshold gives you three things at once: which entity, when, and by how much it broke the rule. That is enough to stop guessing. Sort your jitter results by descending jerk to triage the worst offenders first, note the snapshot times, and reproduce those moments deliberately instead of replaying the whole session hoping the glitch recurs.

Because the session database shares one timeline across all of MDA's channels, you can widen the investigation without changing tools. The same session that recorded the jitter also recorded physics, AI, animation and gameplay snapshots, so once a query tells you an actor jittered at a given moment you can look at what the other channels were doing at that moment to find the cause rather than just the symptom.

If you work with an AI agent through MDA, this is the loop that makes it useful: it can spawn the actor it just wrote, watch it tick, query entity_tracking for jerk and navmesh violations, and read back concrete evidence of whether its change helped or hurt, instead of declaring success blind. Whether you run the queries yourself or hand them to an agent, the principle is the same: detect npc jitter, off navmesh and stuck actors in unreal by asking the data, not by watching the screen.

entity_tracking fields for bug detection

FieldMeaningBug it surfacesExample filter
jerkRate of change of acceleration (from a rolling per-entity history)NPC jitter / vibrationWHERE jerk > 10000
accelerationRate of change of velocity per snapshotTeleport / pop spikesWHERE acceleration > 50000
on_navmeshWhether the entity is on valid navigationOff-navmesh and navmesh flickerWHERE on_navmesh = 0
groundedWhether the entity is on the groundFalling / airborne actorsWHERE grounded = 0
ground_slopeSteepness of the surface beneath the entityActors on too-steep slopesWHERE ground_slope below walkable limit

Per-snapshot fields MDA records in the entity_tracking table, and the kind of bug each one surfaces. Confirm exact column names and value scales with a peek before querying.

FAQ

How do I detect NPC jitter, off-navmesh and stuck actors in Unreal with MDA?

Play your level so the entity_tracking channel records snapshots into the session SQLite database, then run SQL against that table. Use WHERE jerk > 10000 for jitter, WHERE acceleration > 50000 for teleport spikes, and filter on on_navmesh and ground_slope for off-navmesh and steep-slope actors. Each violating row tells you which entity, when, and by how much.

Where is the session database stored?

Under your project at Saved/Observations/sessions/, in a SQLite file named after the session id. It contains over 40 tables, one cluster per observability channel, all sharing the same session timeline, and it is flushed to disk roughly every five seconds by default.

Do I have to compute acceleration and jerk myself?

No. MDA computes acceleration and jerk from an in-memory rolling history kept per entity, and attaches them to each entity_tracking snapshot. You filter on the columns directly rather than computing finite differences in SQL.

Why use peek before writing the SQL?

The mda_observe peek shows you sample rows and the real column names and value scales, so you write correct SQL on the first try. A query against a misspelled column silently returns nothing, which can be mistaken for 'no bugs found', so confirming the schema first avoids a false all-clear.

Does the tracking add runtime cost to my shipped game?

MDA's runtime module is UncookedOnly, so it is excluded from cooked shipping builds. The observability and entity_tracking work happens in the editor host, not in your packaged game.

Get it on Fab

Mythic Dev Assist

Give AI coding agents (Claude Code, Cursor, any MCP client) eyes inside Unreal — a queryable causal world model exposing perception, memory, causality, verification and action through an in-editor HTTP bridge and an external MCP server. Observe, set, create, destroy and watch the editor programmatically.

$24.99USD · one-time · free updates
Report a bug