JEV Tutorial: Build Your First Real-Time Decision-Making NPC

A practical JEV tutorial covering Game State, Legal Actions, decision calls, execution, cadence, latency, timeouts, and safe NPC fallbacks.

Seele Editorial TeamUpdated September 20, 2026
A technical game AI pipeline moving from game state through legal actions to validated execution.

This tutorial walks through a small JEV integration: an NPC receives a compact Game State, chooses one Legal Action, and executes that action through ordinary game code. The example is intentionally modest. A reliable three-action NPC teaches more than a large agent whose state, authority, and fallback behavior are unclear.

The API names and SDK details can vary by integration. The architectural contract stays the same: the game owns truth, JEV selects from legal options, and the executor validates the choice before changing the world.

The smallest useful integration

Use five pieces: an authoritative game state, a state projection, a legal-action builder, a JEV client, and an action executor. The state projection removes irrelevant details. The action builder attaches current preconditions. The client sends a decision request. The executor checks that the response is still valid and translates it into movement, ability, or animation commands.

game state -> state projection -> JEV decision -> validation -> executor -> game state

In Unity, these pieces might live across a MonoBehaviour, a gameplay service, and a command component. In Unreal, they might map to an Actor or Mass processor, a subsystem, and gameplay ability or behavior-tree tasks. The names differ, but the ownership boundary should remain explicit.

Step 1: Design a compact Game State

Start with facts that can change the next decision. For a combat NPC, that might be role, health ratio, visible enemies, nearest cover, ally health, objective status, ammunition, cooldowns, current action, and a monotonic state version. Avoid sending raw engine objects or the entire world. A compact schema is easier to inspect, transmit, and replay.

{ agentId: 'guard-07', role: 'defender', healthRatio: 0.42, objectiveUnderThreat: true, visibleThreats: 1, ammo: 3, healReady: true, stateVersion: 1842 }

Include only information the NPC is allowed to know. This is especially important for stealth and competitive games: a decision model should not receive hidden enemy positions merely because the server has them.

An action should have a stable name, explicit parameters, preconditions, and an execution path. For the example NPC, the set could be holdPosition, takeCover, attack, and retreat. If ammunition is zero, attack should not be sent. If no safe cover exists, takeCover should not be sent.

[ { name: 'takeCover', coverId: 'wall-a' }, { name: 'attack', targetId: 'raider-02' }, { name: 'holdPosition', locationId: 'relay' } ]

Keep the action vocabulary small at first. The goal is not to encode every button press. The goal is to expose meaningful tactical choices and leave low-level execution to the engine.

Step 3: Call JEV

A request should carry the state projection, the legal actions, the NPC goal, and a decision deadline or request identifier. The response should identify the chosen action and may include parameters, a confidence or rationale field if supported, and the state version it observed.

const decision = await jev.decide({ agent: npc, gameState: projectState(state), legalActions: buildLegalActions(state), objective: 'Protect the relay', stateVersion: state.version });

Do not let a late response apply blindly. The world may have changed while the request was in flight. Compare the response's state version with the current version, then revalidate the selected action.

Step 4: Execute and validate

The executor is the final authority. It checks that the action exists in the current legal-action set, that its target still exists, that the NPC is allowed to act, and that the state version is not too old for the action. It then invokes normal movement, ability, or behavior-tree code.

const action = legalActions.find(candidate => candidate.name === decision.name);
if (!action || !matchesParameters(action, decision)) return fallback(state);
return executor.run(action);

Validation should happen even if the service is trusted. It protects against stale state, race conditions, schema drift, and accidental model output that does not match the game contract.

Step 5: Set the decision frequency

Choose a cadence based on the decision, not the render loop. A turn-based NPC can decide at the start of its turn. A real-time combatant might decide when its current intent completes, when a threat enters perception, or on a budgeted timer such as every few hundred milliseconds. Avoid overlapping requests for the same agent unless the system explicitly supports cancellation and ordering.

Use a local authored policy for immediate reactions that cannot wait, such as stopping before a collision or respecting a server-side stun. JEV should handle meaningful tactical choices, not every safety check.

Step 6: Handle latency, timeout, and fallback

Every request needs a deadline. If the deadline expires, keep the NPC in a safe current action or use a deterministic fallback. A fallback can be as simple as hold position, move to cover, follow the last valid intent, or run an authored behavior tree. Choose it per role and situation.

  • Timeout: cancel or ignore the request and use the fallback.
  • Invalid action: log the contract failure, rebuild state, and use a safe policy.
  • Stale state: discard the result or revalidate only actions that remain safe.
  • Service failure: degrade to local behavior without blocking the game loop.
  • Repeated failure: apply backoff and surface telemetry rather than retrying every frame.

Step 7: Test the decision layer

Record the state projection, legal actions, response, validation result, execution outcome, latency, and fallback reason. Build replay cases for low health, no ammunition, multiple threats, lost targets, interrupted actions, and unavailable navigation. A decision system becomes much easier to balance when a designer can replay the exact situation that produced an unexpected choice.

For Unity and Unreal integrations, keep the engine adapter thin. The adapter should translate engine state into the JEV contract and translate a validated action back into an existing command or ability system. This makes the core decision tests runnable without loading a full level.

Production checklist

  • Game State contains only relevant and permitted information.
  • Every Legal Action has preconditions and an owner that can execute it.
  • Responses are validated against current authoritative state.
  • Requests have deadlines, cancellation or staleness rules, and backoff.
  • Fallback behavior is designed for each NPC role.
  • Replay logs make poor decisions reproducible.
  • Latency, validity, action quality, and fallback rate are monitored separately.

For the underlying concept, read What Is JEV?. For the game-loop and NPC design context, see JEV Game AI.