Unreal Gameplay (framework, Blueprint vs C++, input, GAS, AI, UI)

SkillDev tools

Use when building or debugging UE5 gameplay: a character with double jump or dash, abilities with cooldowns, enemy AI that patrols, chases and attacks, a health bar or HUD; choosing Blueprint vs C++, GAS or not, StateTree vs Behavior Tree, EQS or Mass; setting up Enhanced Input, Common UI or UMG; replication, RepNotify, save games, World Partition state; "Blueprints are slow", Tick, casts and hard references; "ability won''t activate", "enemy ignores the player"; or writing automated gameplay tests with injected input.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Unreal Gameplay (framework, Blueprint vs C++, input, GAS, AI, UI) skill

What this skill tells your AI

The instructions your AI receives, as published by scenario-labs/skills in skills/game-engines/unreal/scenario-unreal-gameplay/SKILL.md and read by ahel’s review.

Expert level means every piece of data lives in the class whose lifetime and network presence match it, logic is text (C++ or small functions) under data-only Blueprints and data assets that Python configures, and every mechanic is proven in isolation (injected input, debug commands, traces) before it is judged in play. The agent does not pretend to author graphs it cannot reach: it states which surface each piece lands on and escalates only the residue. If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.

REQUIRED BACKGROUND: scenario-unreal-expert (channels, review loop, 5.8 traps).

Status (2026-09-24): Unreal is not installed. The offline layer passed (python3 tests/code/unreal-gameplay/run_all.py --offline). Every in-editor snippet and generated C++ file is not yet run in Unreal (C++ not yet compiled); job_00_gameplay_probe.py answers the [verify] names.

Stance (the expert delta)

  • Place data by lifetime and network presence. GameMode is server-only rules; GameState and PlayerState replicate to all; PlayerController is owner-only; the Pawn is a respawnable body; GameInstance subsystems outlive maps (Forsythe IaU2Hue-ApI [00:13:26]). State goes in replicated properties with RepNotify, never multicast; C++ RepNotify does not fire on the server (JOJP0CvpB8w [00:10:50], [00:14:48]). Under World Partition BeginPlay can run several times: keep it idempotent (Shao KBn62trwkLw [00:07:29]).
  • C++ base, data-only Blueprint child; Blueprint may depend on C++, never the reverse (Forsythe VMZftEVDuCE [00:24:10]). Tuning is EditDefaultsOnly, asset choices TSubclassOf slots, cosmetics BlueprintImplementableEvent hooks: logic as diffable text, Blueprints as defaults Python sets. Broadcasters fire one-way delegates (OnDied) that controllers, spawners and HUD bind ([00:19:25]).
  • Blueprint cost is per node; the real cost is hard references. Tick overhead is noise next to a CharacterMovement update; casting to a Blueprint class keeps it loaded with the caster (Arnbjörnsson S2olUc9zcB8 [00:06:07], [00:28:01]). Move to C++ only what an Insights trace puts in the hot set (Forsythe [00:16:33]).
  • Effects are what, abilities are when. New features are new reactive assets listening through tags; reactions query a GE's asset tags; one damage GE with SetByCaller; Wait Delay, not Delay; always End Ability; PlayMontageAndWait; Gameplay Cues cosmetic only (Shao 8bi0rnXnRj4 [00:19:25], [00:47:35], [00:21:43], [00:49:12], [00:13:02]; tranek 9.3). A SetByCaller value exists only when code sets it, so console isolation tests use a fixed-magnitude twin [added].
  • Wire the ASC on both sides. On the pawn unless attributes survive respawn (then PlayerState); InitAbilityActorInfo on the server in PossessedBy and on the owning client in the PlayerController's AcknowledgePossession (or OnRep_PlayerState); Mixed for players, Minimal for AI, set early; never predict damage (tranek 4.1, 4.1.2, 7.3, 4.10).
  • Input is data with indirection. Input Action to input tag to ability, so the pawn holds no ability logic (Noland, Lyra Fj1zCsYydD8 [00:34:33]); mapping contexts mutually exclusive per gameplay state (framework doc).
  • StateTree selects a leaf before any transition and re-checks enter conditions only when a transition asks (Mononen YEmq4kcblj4 [00:11:22]). Every condition that should end a state needs an event or a transition; every state gets explicit success and failure ways out, or it falls back to the root ([00:14:13]); data by binding, tuning as tree parameters ([00:16:40]); actions delegate to a Gameplay Ability with a timeout (Bruno XKQfMZOXFv0 [00:17:37], [00:19:15]).
  • UI updates by events, never property bindings; turn Global Invalidation on, then hunt frozen widgets (Albert VxX1aah6TZM [00:24:09]). Widget Tick runs from Paint, a text change is a layout invalidation, Hidden only repaints where Collapsed relayouts ([00:10:09], [00:13:56], [00:12:53]).

Establish first

InputWhy it changes the planDefault when silent
Single player or multiplayer, listen or dedicatedASC placement, replication mode, prediction, test matrixsingle player, authority checks from day one (networking doc)
C++ toolchainXcode 26.6 is installed; Epic lists 26.0 minimum, 26.1.1 recommended, 26.4 incompatible, 26.6 unlistedprobe build (P3) before promising C++; else Blueprint-plus-data fallback
Scaletens of actors vs hundreds of stationary interactables vs thousands of moversactors; Instanced Actors, then Mass (KBn62trwkLw [00:01:06])
Feel targetsjump height, dash distance, cooldown, walk and chase speedsask; untouched CharacterMovement defaults "feel like an Unreal tutorial project" (IaU2Hue-ApI [00:23:39])
Frame budget, platformtick and AI caps60 fps; numbers to scenario-unreal-performance
Character and level inputsskeleton, montages, notifies; nav bounds, player starts, streamingfrom scenario-unreal-animation and scenario-unreal-world-building

Authoring surfaces

Scriptable: C++ classes, Blueprint children and their CDO defaults, data assets and tables, Enhanced Input assets, Gameplay Effect shells, StateTree assets and parameters, widget trees (EditorUtilityLibrary.add_source_widget). Graph content (event graphs, GE modifiers Python cannot instance, StateTree states and transitions, Behavior Tree nodes) goes through a template duplicated per variant, Epic's MCP toolsets, 5.8 BlueprintGraphEditor after help(), or one precise GUI pass. Full table: procedures P15.

Workflow

  1. Probe once per engine. job_00_gameplay_probe.py (headless) lists the classes, properties and cvars the skill marks [verify], including the AbilitySystem.Fix... asset-tag cvar, plus a dumpticks sample. GATE: probe.json written.
  2. Decide. ue_gameplay.decide(spec): data placement, Blueprint vs C++, GAS or not, AI system, ASC placement, init and replication, communication, UI, each with its deciding condition. GATE: every datum has owner, replication and survival (respawn, map change, streaming).
  3. Scaffold project and config. ue_env.enable_plugins; ini_set for bUseDebugTargetFromHud, the asset-tag cvar at 0, tags; a GameMode HUD class for showdebug. GATE: config_verdict clean.
  4. C++ layer. cpp_scaffold() (character base, player controller, dash and melee abilities, AI controller, StateTree tasks, health widget, native tags), patch_build_cs(), build_command(), editor closed for new reflected members. GATE: build exit 0; cpp_lint without errors (it includes ASC_CLIENT_INIT_MISSING).
  5. Data layer from Python. Blueprint children and CDO defaults: movement, JumpMaxCount = 2, player_controller_class on the GameMode, and on every AI pawn ai_controller_class = the controller Blueprint that holds the tree plus auto_possess_ai placed or spawned. Input assets from input_plan(), GE assets from plans checked by ge_plan_verdict, StateTree asset, widget tree. GATE: ge_plan_verdict and audit_gameplay() clean.
  6. Graph residue. Write each tree as a spec, check it with statetree_spec_verdict, wire it from statetree_outline through an MCP toolset or one GUI pass (references/gui-paths.md), set tree parameters per enemy. GATE: spec verdict clean; the tree compiles with no unbound Inputs; ai_setup_verdict clean.
  7. Test in isolation, then the chain. Fixed-magnitude debug GE from the console, showdebug abilitysystem (3 pages), the gameplay debugger on each AI, log LogAbilitySystem VeryVerbose read by gas_log_triage; then pie_scenario or a PythonAutomationTest with Input.+key injection, judged by analyzers whose thresholds derive from the parameters (analyze_jump, dash_expected_cm, analyze_cooldown, state_sequence_verdict, health_bar_verdict). GATE: every scenario passes, no stuck ability, no unplanned root entry.
  8. Evidence and review. Screenshots of the debug pages, showdebug enhancedinput, health bar before and after damage, enemy per state, navmesh coverage; ue_review.image_checks before looking. GATE: no blank frame; UI in sync.
  9. Measure. Development build or new-window PIE: stat game, stat unit, dumpticks (tick_verdict), stat slate with Global Invalidation off and on, and an Insights trace of the scenario before any rewrite (rewrite_gate). GATE: tick census justified; rewrites only for hot-set functions; trace handed to scenario-unreal-performance.

Numbers

ValueRelative toSource
Tick overhead per actor: BP 3.59 us editor, 1.84 us packaged; C++ about 0.64 to 0.70 us100,000 actors, UE 5.4Arnbjörnsson [00:05:00]
CMC 241 us, PlayerController 125 us per framesame Lyra frameArnbjörnsson [00:06:07]
BP node 0.1 us packaged (0.5 editor); C++ call 0.7 us (0.8 editor)per callArnbjörnsson [00:18:48]
Print String 88.8 to 272.5 us per callDevelopment buildArnbjörnsson slides 00:20:10
Fortnite: about 80 ticks per frame client, 700 serverticking functionsArnbjörnsson [00:11:03]
GetAllActorsOfClass fine for a few dozen results per frame; WithTag and WithInterface scan every actorworld sizeArnbjörnsson [00:37:17], [00:38:56]
Empty Tick: free from 5.6compiler disables itmyth article
20 active enemies cap; async path offset 0.2 to 0.5 s; attackers 1 dangerous or up to 3 weakLords of the Fallen, console, per playerBruno [00:16:25], [00:15:20], [00:38:40]
Lyra modal push repainted 207 widgets; 25 after the fixpaint countAlbert [00:35:03]
Jump apex h = v^2 / 2g; a second jump resets Velocity.Z, adding h to the rise at the pressJumpZVelocity, gravity 980 cm/s^2 x GravityScalephysics [added]
Ground dash = speed x duration + exit coast (about 24 cm from 600 cm/s)GroundFriction 8, BrakingFrictionFactor 2, BrakingDecelerationWalking 2048engine model [added, verify]

Quality gates

  • Measurable: cpp_lint (asset paths and dependency direction, multicast state, Reliable RPC in Tick, OnRep on the server, client ASC init, widget Tick, Collapsed toggles, repeated BeginPlay); ge_plan_verdict; statetree_spec_verdict; ai_setup_verdict; audit_gameplay (tick census, hard-reference closure, cooldown tags, widget bindings); scenario verdicts with derived thresholds; gas_log_triage empty; showdebug abilitysystem shows no stuck ability; StateTree trace without unplanned root entry; rewrite_gate behind every rewrite.
  • Visual: jump arc and dash from a fixed camera; enemy facing and spacing; health bar and cooldown UI in sync; no frozen widget with Global Invalidation on; two-client screenshots agree in multiplayer.

Common mistakes

MistakeWhat it looks likeFix
Transient client data on the GameModeclients see nothingGameState or PlayerState
Multicast for a door statelate joiners see it closedreplicated property + RepNotify
Cast from the player to optional Blueprintslong loads, memory residentC++ base, interface, IsA (Soft)
"Don't use Tick", Timeline every framesame cost, harder codefewer ticking actors, aggregate, interval
ASC initialized only in PossessedBy"Can't activate LocalOnly or LocalPredicted ability" on clientsAcknowledgePossession or OnRep_PlayerState
Ability never ends; Delay inside itcannot re-trigger; wait survives cancelEnd Ability on every path; Wait Delay
PlayMontage in an abilitymontage missing on other machinesPlayMontageAndWait
SetByCaller GE applied from the consolezero damage, missing-magnitude logfixed-magnitude twin (GE_Damage_Debug)
Querying GE granted tagsreactions fire on wrong effectsasset tags; asset-tag cvar at 0
Enter conditions trusted to be polledenemy keeps chasing inside attack rangeevent or On Tick transition on the running state
State without a failure transition"why did it go to the root"explicit success and failure transitions
Delay beside MoveTo in one statewaits while walkingseparate Wait state; 5.6 Task Control Flow
C++ controller class on the enemy CDOenemy stands stillcontroller Blueprint holding the tree; placed or spawned
UMG property binding, widget Tickper-frame cost; frozen under invalidationevents from the attribute delegate
Collapsed for an often-toggled barlayout invalidation per toggleHidden
Invalidation tested in the viewport PIEno gain seennew-window PIE
Sync save during playhitch, certification riskAsyncSaveGameToSlot
Rewrite from a stat huncheffort, no gainInsights trace, rewrite_gate
Hand-picked test thresholda correct build failsderive it from the parameters
Agent Blueprint timers as Delay chains150 Delay nodes (Deiter [00:56:45])Timeline, C++ timer, or GAS task

Handoffs

  • Receives from scenario-unreal-animation: the character Blueprint child, AnimBP with its variable contract, montages with root motion and warp targets, the reusable AN_SendGameplayEvent notify on hit frames; from scenario-unreal-world-building: levels with nav bounds, player starts, streaming ranges, Data Layers, gameplay data as Asset User Data.
  • Delivers to scenario-unreal-vfx: an event contract (vfx_event_contract: tag, trigger, payload, radius from gameplay data, window, max rate, spawn path; cues cosmetic only); to scenario-unreal-performance: a Development build, the worst-case scenario as an automation test, its Insights trace, tick census, AI and ASC counts (perf_handoff); to scenario-unreal-animation: the tuned movement model before motion matching data (DeVoe FLDXtAV7qsw [00:07:01]); to scenario-unreal-pipeline-automation: the automation tests for CI.

UE 5.8 notes

  • Replicated properties auto-register (5.6); Iris Production Ready for licensees, generic replication still default; Mover still Experimental.
  • Enhanced Input unified with Common UI (Production Ready); Combo trigger deprecated; UPlayerMappableInputConfig deprecated (5.4).
  • GAS: NonInstanced abilities deprecated (5.5); cooldown GE validation (5.7); GetAssetTags and GetGrantedTags in Blueprint (5.7); debug commands under AbilitySystem.Ability and AbilitySystem.Effect (5.4).
  • StateTree: Task Control Flow and scheduled ticks (5.6); Rewind Debugger track (5.7); start state by tag, reversible bindings, UE::StateTree::EComparisonOperator (5.8).
  • Blueprint: nativization removed (5.4); IsA (Soft) (5.5); empty Tick free (5.6); BlueprintGraphEditor, no local variables shadowing members, bp.MaxAsyncActionCount (5.8).

References

Signals

GitHub stars
681
Forks
82
Last commit
Sep 2026

ahel review

  • K6low
    bundled executables the agent is told to run

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Item type
skill
Key
scenario-unreal-gameplay
Source
github.com/scenario-labs/skills