Architecture
How the runtime turns a button press into an authoritative hit
Gameplay Moveset System ships as two C++ modules: MovesetRuntime (everything the cooked game needs) and MovesetEditor (UncookedOnly — graph editor, factories, asset definitions). Together they implement a state machine that resolves "which attack should play right now" from input + combo state, executes the attack as a montage with hitbox sweeps, and replicates the relevant state across all network roles.
This page walks the data flow start to finish, then pulls each subsystem apart.
Layered view
┌─────────────────────────────────────────────────┐
│ YOUR GAME │
│ Damage System, VFX, Sound, Movement, Camera │
│ ↑ OnHit, OnAttackStarted, etc. │
│ ↑ Action Modules (per-node BPs) │
├─────────────────────────────────────────────────┤
│ MOVESET SYSTEM │
│ │
│ MovesetComponent (state machine + replication) │
│ ├── ComboGraphInstance (cursor + advance) │
│ ├── InputBufferComponent (motion patterns) │
│ ├── ActiveHitboxes (registered by ANS; │
│ │ sweep + dispatch in TickComponent) │
│ └── DispatchMovesetEvent → Action Modules │
│ │
│ MovesetDefinition (DefaultMode + AlternateModes)│
│ ComboGraphAsset (compiled node/transition data)│
│ AnimNotifyStates (Combo Window + Hitbox) │
├─────────────────────────────────────────────────┤
│ UNREAL ENGINE │
│ AnimMontage, Enhanced Input, GameplayTags, │
│ Chooser Tables, Subobject Replication │
└─────────────────────────────────────────────────┘
The plugin owns the middle layer. Your game owns the top layer (cosmetic + numerical decisions). The bottom layer is unmodified UE — the plugin doesn't fork or extend any engine class, only registers AnimNotifyStates, components, asset types, and a chooser variant.
Request lifecycle: press → hit
Trace one frame from "the player presses LMB" to "the enemy takes damage":
- Enhanced Input fires
IA_PrimaryAttack(Triggered) on the locally-controlled pawn. - Auto-binding callback on
MovesetComponentbuilds anFMovesetInputCommand { RequiredAction = IA_PrimaryAttack }and callsTryComboAdvance. - ComboGraphInstance evaluates the current cursor's outgoing transitions in priority order. The first transition whose
RequiredInputmatches and whoseConditions(FGameplayTagQuery) pass wins. - If the previous attack's Combo Window is still open, the input is buffered. When the window closes (montage notifies
NotifyEndon the Combo Window ANS), the buffered input is replayed against the cursor and steps 3-7 happen for that input. - The chosen transition's
TargetAttackTagis resolved to aUAnimMontagethrough the activeFMovesetModeData::AnimationChooser(Chooser Table). The chooser receivesOwner+ the attack tag in its context container. Internal_ActivateAttackplays the montage on the character'sUSkeletalMeshComponent, setsCurrentAttackTag(replicated to simulated proxies), and dispatchesMoveset.Event.AttackStartedto any Action Module attached to the new node.Moveset: HitboxAnimNotifyState firesNotifyBeginat its placed timeline position. The component registers the hitbox (geometry fromFMovesetModeData::Hitbox, attached to the resolved socket) and starts sweeping it inTickComponent.- Each frame the sweep finds candidates, runs the filter pipeline (per-target cooldowns, max-hits, ignore-tags, owner exclusion), and for each survivor calls
ReportHit→OnHitdelegate fires +Moveset.Event.Hitdispatches to Action Modules. - Your damage / VFX code reacts through one of those hooks. The plugin doesn't touch numbers.
Every step has a fallback: hitbox notifies that fire on a coalesced server tick, swap requests during an open combo window, missing chooser entries, missing component on the actor — all are handled with logged warnings, not crashes.
The agent loop, in one paragraph
MovesetComponent is a finite state machine. Its state at any moment is (ActiveSelection, CurrentAttackTag, ComboCursor, BufferedInput, ActiveHitboxes). Inputs and tick events transition between states. Replication mirrors (ActiveSelection, CurrentAttackTag) — that's the minimum the simulated proxy needs to play the cosmetic montage. The autonomous proxy keeps its own predicted CurrentAttackTag and an authoritative copy of everything else from OnRep_ActiveSelection.
Networking model
MovesetComponentis a CDO/BP-added stably-named replicated sub-object. It does not get spawned at runtime.ActiveSelection(FMovesetActiveSelection { Definition, Mode }) replicates atomically with anOnRepthat auto-initialises the local component on remote sides. This means a server-onlyApply Movesetcauses autoproxies / simproxies to set themselves up via the OnRep — no extra wiring.CurrentAttackTagreplicates withCOND_SkipOwner. Simulated proxies receive it throughOnRep_CurrentAttackand play the matching montage locally. The autonomous proxy keeps its own predicted value.Server_DispatchInputRPC mirrors autoproxy presses to the authority — necessary because the server's view of a client-controlled pawn has noUEnhancedInputComponent.Server_InitializeFromClientRPC ensures the authority initialises when Apply Moveset only runs on the client.- Hitbox sweeps run on Authority and AutonomousProxy. Simulated proxies don't simulate sweeps — they're cosmetic only.
- Authority-side hitbox coalescing. On Auth the server's pose tick can be coarser than animation-frame resolution, so a hitbox ANS that lasts 8 animation frames can collapse into 8 atomic
NotifyBegin → NotifyEndpairs. The component coalesces same-key registrations into one long-lived hitbox and defers unregister by a couple of TickComponent passes, so the sweep pipeline accumulates real Prev → Curr deltas. Automatic, no project-side wiring.
The result: all three call patterns (server-only, client-only, both-parallel) work without disconnects or special-casing.
See Replication for the full failure-mode catalogue and how each is contained.
Key classes
| Class | Purpose |
|---|---|
UMovesetComponent |
Core state machine — attacks, combos, hitbox sweep + dispatch, event routing, replication. |
UMovesetDefinition |
Data asset: DefaultMode + AlternateModes map of FMovesetModeData. |
UComboGraphAsset |
Compiled combo structure (edited via the visual node graph). |
UComboGraphInstance |
Runtime cursor + transition matching. |
UMovesetInputBufferComponent |
Input recording, buffer querying, motion-input pattern matching. |
UAsyncAction_ApplyMoveset |
Blueprint entry point. Single OnMovesetEvent pin (discriminate by Payload.EventTag). |
UActionModule |
Abstract Blueprint base for tag-routed per-node modules. |
UAnimNotifyState_MovesetComboWindow |
Combo Window on a montage timeline. |
UAnimNotifyState_MovesetHitbox |
Hitbox slot binding on a montage timeline. |
UAnimNotify_MovesetComboPoint |
Combo point marker (UI feedback hook). |
IMovesetAnimProvider |
Optional interface — actor provides its USkeletalMeshComponent to the system. |
IMovesetMeshSourceProvider |
Optional interface — actor resolves a MeshSlot tag to a SceneComponent (e.g. equipped weapon). |
IMovesetHitTarget |
Optional interface on hit candidates — provides per-target ignore tags for filter gating. |
UMovesetSettings |
Project-wide settings (UDeveloperSettings). |
Key structs
| Struct | Purpose |
|---|---|
FMovesetActiveSelection |
{ Definition, Mode } — replicated atomically, the source of truth for "what's running on the component". |
FMovesetModeData |
{ ComboGraph, AnimationChooser, Hitbox, Config } — one per Mode of a Definition. |
FMovesetHitboxData |
Hitbox geometry + filter parameters (shape, extent, socket, channel, max hits, cooldowns, ignore-query). |
FMovesetHitResult |
Hit payload: HitActor, HitLocation, HitNormal, NativeHit, AttackTag. |
FMovesetEventPayload |
Unified event payload: EventTag, AttackTag, PreviousAttackTag, HitResult, FailureReason. |
FMovesetInputCommand |
Combo input descriptor: RequiredAction, RequiredTriggerEvent, RouteTag, optional MotionInput. |
FMovesetMotionInputPattern |
{ PatternTag, DirectionalSequence, Tolerance, MaxDuration } — registered on InputBufferComponent. |
FMovesetConfig |
Per-mode override knobs (input buffer size / window, combo reset time, …). Negative = inherit project setting. |
Project Settings
Edit → Project Settings → Plugins → Moveset System:
| Setting | Default | Description |
|---|---|---|
| Default Buffer Size | 30 | Input buffer capacity (frames). |
| Default Buffer Window | 0.5s | How long inputs stay valid in the buffer. |
| Default Motion Input Window | 0.3s | Time window for motion-input sequences. |
| Default Combo Reset Time | 2.0s | Time after last input before the combo resets and OnComboDropped fires. |
| Default Trace Channel | ECC_Pawn | Channel used by hitbox sweeps when their bUseProjectDefaultTraceChannel flag is set. |
| Max Hitbox Step Distance | 500.0 | Per-tick prev → curr socket distance cap (cm). Sweeps exceeding this skip the current tick (teleport guard). 0 = off. |
| Draw Hitboxes | false | Debug-draw active hitboxes every tick. |
| Hitbox Active Color | red | Wireframe colour when a hitbox is active and didn't hit anything this tick. |
| Hitbox Impact Color | yellow | Wireframe colour flashed for one frame when the hitbox registers a hit. |