Reaction System

Documentation Unreal Engine AI Reactions

Event-driven AI reactions: parry, dodge, counter. They respond to game events in real time.


Actions poll on a timer. Reactions fire from events you trigger.

When to Use This

  • AI that parries incoming attacks
  • Dodge rolls triggered by projectile detection
  • Counter-attacks after blocking
  • Any respond-to-this-stimulus behavior
The system does not decide when to react: you do. Most setups call ExecuteReaction with a known ReactionId. Use EvaluateBestReaction only when several reactions in the same category should compete.

Actions vs Reactions

ActionsReactions
TriggerPolled on a timer by the brainEvent-driven from your Blueprint or C++
SelectionScore-based (distance, angle, context)Usually by ID; optional priority + weighted random
ComponentActionEvaluationComponentReactionEvaluationComponent
Data assetActionSetReactionSet
When to use"What should I do next?""Something happened: how do I respond?"

ReactionSet (Data Asset)

Create via Right-click → Miscellaneous → Data Asset → ReactionSet.
Each entry in the Reactions array is an FReactionSpec. Defaults from the plugin header:
Identity
Enabled
Reaction ID
Parry
Reaction Category
None
Execution
Ability Class
None
Activation Mode
By Event
Ability Tag
None
Ability End Tag
None
Ability Timeout
0
Tags
Add Tags
0 Gameplay Tags
Requires Tags
0 Gameplay Tags
Block Tags
0 Gameplay Tags
Scoring
Priority
0
Selection Weight
1
Scoring
0 Array elements
Action Interaction
Cancel Current Action
Assign Ability Class to a UGameplayAbilityBase subclass: the editor auto-fills Ability Tag and Ability End Tag. Activation Mode defaults to By Event on reactions (actions default to By Tag).
Reaction Category, Priority, and Selection Weight on the spec matter only when you use EvaluateBestReaction. For direct ExecuteReaction("Parry", …) calls, the ID alone selects the reaction.
Reaction abilities must inherit from UGameplayAbilityBase. It handles the end-event handshake that tells the system when the ability finishes. Without it, the AI waits until timeout.

Preconditions you author

These ReactionSet fields feed PassesReactionGates (the same check runs at selection and again at execute):
Identity
Enabled
Tags
Requires Tags
0 Gameplay Tags
Block Tags
0 Gameplay Tags
Cooldown
Cooldown Duration
5
Initial Cooldown
0
Randomization (%)
0.2
Spawn Cooldown Chance
0
Max Consecutive Uses
-1
Unlike actions, Requires Tags and Block Tags read only the AI's ASC, not target or world tags. Add Tags is not a gate: it is applied while the reaction runs (see Triggering Reactions).

Block actions via tags

Add SEC.Action.BlockActions to Add Tags to freeze offensive action selection while the reaction runs. Actions check that tag in PassesHardGates().


Assigning the ReactionSet

SECReactionSetComponent on the pawn resolves which ReactionSet an enemy uses. EnemyCharacterBase creates it automatically; EnemyControllerBase already has ReactionEvaluationComponent.
  1. Assign the asset: Set DefaultReactionSet on EnemyAIConfig, or add role entries to RoleReactionSets (see Combat Roles). Or set DefaultReactionSet on the pawn component for zero-config testing.
  2. Custom pawns: Add SECReactionSetComponent manually if not using EnemyCharacterBase.
On every combat role assignment or change (including the first assignment after a combat target is registered), SECCombatControllerComponent::SyncStateForCombatRole calls SECReactionSetComponent::SyncForCombatRole, which resolves the chain below and pushes the result to ReactionEvaluationComponent::SetReactionSet. Until that sync runs, the controller has no active reactions even if the config asset is set.

Quick path

Minimum to get one parry working after assign:
// On controller, after pawn is possessed and reaction set is synced:
FSECExecutionContext Context;
Context.Target = Attacker;
ReactionComp->ExecuteReaction("Parry", Context);
In Blueprint: get Reaction Evaluation Component on the AI controller, call Execute Reaction with the ReactionId from your ReactionSet and a filled Context struct.
The resolver walks this chain and returns the first match:
PrioritySourceUse
1 (highest)Runtime OverrideSetRuntimeOverride(ReactionSet). Boss phase transitions.
2Equipped WeaponWeapon on the sibling SECActionSetComponent implementing ISECWeaponReactionSetProvider.
3Config RoleRole-specific ReactionSet from EnemyAIConfig → RoleReactionSets.
4Config DefaultFallback from EnemyAIConfig → DefaultReactionSet.
5Component DefaultSECReactionSetComponent → DefaultReactionSet on the pawn.
6NoneNo ReactionSet. The AI cannot react.
Weapon and override changes take effect on the next sync. Set the equipped weapon on SECActionSetComponent (SetEquippedWeapon); the reaction component reads it from there.
SECActionSetComponent uses the same resolution chain for action sets.

Swap Modes

When a reaction set swap runs while a reaction is already executing:
ModeBehavior
ESECReactionSetSwapMode::ImmediateCancel the current reaction and swap now (default).
ESECReactionSetSwapMode::WaitForCompletionQueue the swap; it applies when the current reaction completes.
Pass the mode to SetRuntimeOverride, ClearRuntimeOverride, or SyncForCombatRole. There is no persistent SwapMode property on the component.
AdvancedWeapon reaction set provider and replicated state
Implement ISECWeaponReactionSetProvider on your weapon actor and override GetWeaponReactionSetForRole(RoleTag).
Pawn->ActionSetComponent->SetEquippedWeapon(WeaponActor);
Pawn->ActionSetComponent->SetEquippedWeapon(nullptr);  // revert to config resolution
Replicated state on SECReactionSetComponent (client UI, VFX, debug): CurrentReactionId, bReactionExecuting, ActiveReactionSet, OnReactionExecutionStarted / OnReactionExecutionCompleted, OnReactionSetChanged, cooldown delegates, and GetRemainingCooldown / IsReactionOnCooldown / GetAllActiveCooldowns.

Triggering Reactions

Your game logic detects the stimulus and calls the controller's ReactionEvaluationComponent. The usual path is a direct ID call: you already know which reaction fits (parry on block input, dodge on projectile warn, etc.).
FSECExecutionContext Context;
Context.Target = DamageInstigator;
Context.Magnitude = DamageAmount;
Context.EventTag = YourGame.Stimulus.MeleeHit;
Context.TargetData = USECTargetDataLibrary::MakeTargetDataFromDirection(HitDirection);
 
if (ReactionComp->CanReact("Parry", DamageInstigator))
{
    ReactionComp->ExecuteReaction("Parry", Context);
}
  • CanReact(ReactionId, TargetOverride) runs PassesReactionGates without executing. Pass the attacker as TargetOverride so Gates in the Scoring array (distance, attributes, etc.) evaluate against it. Scorers do not run here; they only affect optional selection.
  • ExecuteReaction(ReactionId, Context) runs the same gates again, then starts the ability, manages tags and cooldowns, and fires delegates.
Extra blocks at execute time (not ReactionSet fields):
CheckBlocks when
SEC.Reaction.BlockReactionsThe tag is on the AI's ASC (Gameplay Tags). Global stun or cutscene.
Another reaction executingA reaction is already active on this controller.
Only one reaction runs at a time. If a reaction is already executing, ExecuteReaction returns false.

Execution order

Inside ExecuteReaction (same gates as above, re-checked against Context.GetTarget()):
  1. PassesReactionGates.
  2. Cancel the current action if Cancel Current Action is enabled on the spec.
  3. Apply Add Tags to the ASC.
  4. Pack FSECExecutionContext into the gameplay event payload.
  5. Activate the ability (By Event or By Tag).
  6. On success: commit cooldown, listen for Ability End Tag (or timeout), fire OnReactionStarted.
  7. On end tag or timeout: remove Add Tags, fire OnReactionCompleted.
A failed activation tears down immediately: tags removed, no cooldown committed. Cooldown commits only after activation succeeds; a CanActivateAbility refusal leaves the reaction off cooldown.

Reaction-to-reaction blocking

While a reaction runs, its Add Tags sit on the ASC. Put the same tag in another reaction's Block Tags to prevent overlap. Both reactions below use SEC.State.ReactionActive:

Parry reaction

Adds SEC.State.ReactionActive while it runs. No block tags.

Tags
Add Tags
1 Gameplay Tag
SEC.State.ReactionActive
Requires Tags
0 Gameplay Tags
Block Tags
0 Gameplay Tags

Dodge reaction

Same add tag, plus Block Tags so it cannot fire during Parry.

Tags
Add Tags
1 Gameplay Tag
SEC.State.ReactionActive
Requires Tags
0 Gameplay Tags
Block Tags
1 Gameplay Tag
SEC.State.ReactionActive
When Parry is running, the ASC holds SEC.State.ReactionActive, so Dodge fails PassesReactionGates until Parry ends and removes the tag.

Context payload

Both actions and reactions share FSECExecutionContext. Fill what you know before ExecuteReaction; the ability reads it through GetActionContext() on UGameplayAbilityBase.
AdvancedFSECExecutionContext fields and TargetData helpers
FieldYou setSystem fills
TargetStimulus source (attacker, projectile owner)n/a
OptionalObjectWeapon, projectile, itemn/a
MagnitudeDamage, charge level, intensityn/a
EventTagStimulus categoryn/a
ContextTagsSituational tags ("Backstab", "Airborne")n/a
TargetDataSpatial payload via USECTargetDataLibraryn/a
DistanceToTarget, DirectionToTargetn/aHydrated by UGameplayAbilityBase on activation
InstigatorTags, TargetTagsn/aQueried from ASCs at activation
Context.TargetData = USECTargetDataLibrary::MakeTargetDataFromDirection(HitDirection);
See Gate Before Activation for payload gating in CanActivateAbility.

Choosing a Reaction (Optional)

When several reactions could fit the same moment (multiple parry variants, dodge left vs right), call EvaluateBestReaction instead of picking the ID yourself. The system filters by category, runs gates, scales weights with scorers, picks the highest Priority band, then weighted-random among survivors.
Reaction Evaluation Flow
Content>Plugins>SoulslikeEnemyCombat>Components
Waiting for stimulus…
Stimulus (Your Code)
OnDamageReceived, OnSenseDetected…
EvaluateBestReaction(Category)
Gates → Scorers → Priority → Weighted random
Cancel Current Action
StopCurrentAction() if bCancelCurrentAction
ExecuteReaction(Id, Context)
AddTags → Pack context → Activate ability
OnReactionCompleted
Remove AddTags → Fire delegate → Reset
Reactions are event-driven — your code decides when, the system decides what. Every 3rd cycle shows a blocked reaction.
FChosenReaction Chosen = ReactionComp->EvaluateBestReaction(
    YourGame.Reaction.Defensive,
    DamageInstigator);
if (Chosen.IsValid())
{
    ReactionComp->ExecuteReaction(Chosen.ReactionId, Context);
}
Leave the category tag empty to consider every reaction in the set.

Selection pipeline

Uses the same PassesReactionGates as execute, then adds selection-only steps:
  1. Filter by category (optional).
  2. Hard gates per reaction (identical to preconditions you author).
  3. Scorers scale weight: each survivor's Selection Weight is multiplied by its Scorers. Weight zero or below is a hard veto, even at high priority.
  4. Highest priority band only.
  5. Weighted random among survivors in that band.
Returns FChosenReaction. Check IsValid() before executing.

Priority and weight

These fields apply only on this path, not on direct ExecuteReaction by ID:
Scoring
Priority
0
Selection Weight
1
Scoring
0 Array elements
Add Gates under the Scoring array to hard-veto. Add Scorers to scale Selection Weight by distance, attributes, and more.

Scorers on reactions

Built-in Distance, Angle, Health, and Speed Scorers read live values from the AI pawn and TargetOverride (or focus actor). Pass the attacker to EvaluateBestReaction(CategoryTag, Attacker).
Distance Scorer Range at the melee-preset default:
Range
Min Value
0
Optimal Min
100
Optimal Max
250
Max Value
500
Exponent
2
Clamp To Zero
Stamina Gate reads the default stamina snapshot (100) unless you use an Attribute Gate. Attribute Scorers and Gates read live GameplayAttributes. Same classes as actions: see Scorers & Gates. Custom scorers stay stateless; SeededRandom is fixed on the reaction path (Seed stays 0).

Debug Tools

ReactionEvalComp->bDebugLogReactions = true;
Console variable SEC.Debug.LogReactions 1 enables the same logging globally.

Integration Points

SystemHow It Connects
Action SystemReactions cancel actions (bCancelCurrentAction) and can block new ones via SEC.Action.BlockActions in AddTags. Same resolution chain pattern.
Combat RolesRole changes sync ReactionSets via SECReactionSetComponent::SyncForCombatRole.
Melee TraceOnMeleeHitResponse on SECMeleeTraceComponent is a typical hook to fire or evaluate reactions after a hit.
MultiplayerReaction execution state and cooldowns replicate through SECReactionSetComponent.
Custom character classes: ReactionEvaluationComponent resolves the ASC from the possessed pawn via IAbilitySystemInterface, same as actions. Pawns without it cause reaction abilities to fail silently.

Key API

AdvancedComponent and delegate reference
ComponentLocationRole
ReactionEvaluationComponentControllerExecution, optional selection, cooldowns
SECReactionSetComponentPawnResolution, replication, overrides
ReactionEvaluationComponent (Controller)
  • ExecuteReaction(ReactionId, Context) - Fire a reaction (primary path).
  • CanReact(ReactionId, TargetOverride) - Gate check without executing.
  • EvaluateBestReaction(CategoryTag, TargetOverride) - Optional selection when multiple reactions compete.
  • StopCurrentReaction(bSuccess) - Manually end the running reaction.
  • IsReactionExecuting() / GetCurrentReactionId() - Runtime state.
  • OnReactionStarted / OnReactionCompleted - Controller-local delegates (with full context).
SECReactionSetComponent (Pawn)
  • SetRuntimeOverride(ReactionSet, SwapMode) / ClearRuntimeOverride(SwapMode) - Boss phase override.
  • SyncForCombatRole(RoleTag, SwapMode) - Apply resolved set for a combat role.
  • GetReactionSetForRole(RoleTag) - Query the resolution chain without applying.
  • Replicated execution and cooldown delegates; see Assigning the ReactionSet.

Gameplay Tags

TagPurpose
SEC.Reaction.EndCommon default for AbilityEndTag
SEC.Reaction.BlockReactionsBlocks all reactions when on the ASC
SEC.Action.BlockActionsBlocks all actions when on the ASC
SEC.State.ReactionActiveConvenience tag for reaction-to-reaction blocking
ReactionCategory tags are project-defined. Filter with them in EvaluateBestReaction only when using optional selection.