Reaction System

Documentation Unreal Engine AI Reactions

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


The flip side of actions. Actions are polled - reactions are event-driven.
Waiting for stimulus…
Stimulus (Your Code)
OnDamageReceived, OnSenseDetected…
EvaluateBestReaction(Category)
Priority → Weighted random → Gate checks
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.

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 doesn't decide when to react - you do. It decides what to react with and whether the reaction is allowed.

Core Concepts

Actions vs Reactions

ActionsReactions
TriggerPolled on a timer via StateTreeEvent-driven from your Blueprint/C++
SelectionScore-based (distance, angle, context)Priority + weighted random
ComponentActionEvaluationComponentReactionEvaluationComponent
Data AssetActionSetReactionSet
When to use"What should I do next?""Something happened - how do I respond?"

How It Works

  1. Something happens - your code detects a stimulus (damage, projectile, proximity)
  2. Ask the system - call EvaluateBestReaction() with an optional category filter
  3. System evaluates - checks gates (enabled, cooldown, BlockReactions tag, RequiresTags, BlockTags), groups by priority, picks weighted random winner
  4. You fire it - call ExecuteReaction() with the chosen reaction ID and a context payload
  5. Lifecycle managed - the system handles ability activation, tag management, cooldowns, timeout, and completion

ReactionSet (Data Asset)

Create via Right-click → Miscellaneous → Data Asset → ReactionSet.
Each entry is an FReactionSpec:

Identity

PropertyTypeDescription
bEnabledboolToggle without removing
ReactionIdFNameUnique name: "Parry", "DodgeLeft", "Counter"
ReactionCategoryFGameplayTagGrouping tag for EvaluateBestReaction()

Execution

PropertyTypeDescription
AbilityClassTSubclassOf<UGameplayAbility>Ability to activate (auto-granted to ASC)
ActivationModeEAbilityActivationModeByEvent (default) or ByTag
AbilityTagFGameplayTagTag for activation
AbilityEndTagFGameplayTagTag to listen for on ability completion
AbilityTimeoutfloatMax seconds to wait (0 = infinite)

Tags

PropertyTypeDescription
AddTagsFGameplayTagContainerApplied to ASC while reaction is active
RequiresTagsFGameplayTagContainerAll must be present to allow this reaction
BlockTagsFGameplayTagContainerIf any are present, this reaction is blocked

Scoring

PropertyTypeDescription
Priorityint32Higher value = chosen first
SelectionWeightfloatRandom tiebreak among equal-priority reactions
CooldownFActionCooldownSame cooldown struct as actions

Action Interaction

PropertyTypeDescription
bCancelCurrentActionboolCancel running action when reaction fires (default: true)

Block actions via tags

To prevent new actions during a reaction, add SEC.Action.BlockActions to AddTags. This is checked in PassesHardGates() on the action side, so blocked actions won't even be evaluated.


Selection Logic

EvaluateBestReaction(CategoryTag):
  1. Filter by CategoryTag (skip if empty)
  2. Gate checks per reaction:
     - Is enabled?
     - Off cooldown?
     - Under MaxConsecutiveUses?
     - SEC.Reaction.BlockReactions not on ASC?
     - All RequiresTags present?
     - No BlockTags present?
  3. Group survivors by Priority (highest first)
  4. Weighted random pick among top-priority group
  → FChosenReaction { ReactionId, AbilityTag, Priority, Weight }

ReactionEvaluationComponent

Lives on the AIController alongside ActionEvaluationComponent.

Core API

FunctionDescription
SetReactionSet(UReactionSet*)Deprecated. Use SECReactionSetComponent for reaction set management. This function still works but bypasses the resolution chain.
CanReact(FName ReactionId)Quick gate check without executing.
EvaluateBestReaction(FGameplayTag CategoryTag)Find best available reaction. Returns FChosenReaction.
ExecuteReaction(FName ReactionId, FSECExecutionContext Context)Fire a reaction with full context.
StopCurrentReaction(bool bSuccess)Manually stop the running reaction.
IsReactionExecuting()True while a reaction is active.
GetCurrentReactionId()Name of the currently running reaction.

Delegates

DelegateParametersWhen
OnReactionStartedFName ReactionId, FSECExecutionContext ContextReaction begins
OnReactionCompletedFName ReactionId, bool bSuccess, FSECExecutionContext ContextReaction finishes (success or timeout)
Your reaction ability must inherit from UGameplayAbilityBase. It handles the end-event handshake that tells the system when the ability finishes. Without it, the AI gets stuck waiting until timeout.

Execution Flow

ExecuteReaction("Parry", Context)
  ├── 1. Validate gates (enabled, cooldown, BlockReactions, RequiresTags, BlockTags)
  ├── 2. Cancel current action (if bCancelCurrentAction)
  ├── 3. Apply AddTags to ASC
  ├── 4. Pack FSECExecutionContext into FGameplayEventData
  │      ├── Auto-populate InstigatorTags from instigator ASC
  │      ├── Auto-populate TargetTags from target ASC
  │      └── TargetData passed through directly
  ├── 5. Activate ability (ByEvent or ByTag)
  ├── 6. Start cooldown, record use
  ├── 7. Fire OnReactionStarted
  └── Listen for: AbilityEndTag event OR AbilityTimeout
       └── CompleteExecution
           ├── Remove AddTags from ASC
           └── Fire OnReactionCompleted

Reaction-to-Reaction Blocking

No dedicated flag needed. Use AddTags and BlockTags for full control:
Reaction A (Parry):
  AddTags:   [SEC.State.ReactionActive]
  BlockTags: []

Reaction B (Dodge):
  AddTags:   [SEC.State.ReactionActive]
  BlockTags: [SEC.State.ReactionActive]   ← can't fire while Parry is active
To block all reactions globally (e.g., during stun), apply the SEC.Reaction.BlockReactions tag to the ASC. This is checked inside PassesReactionGates() before any per-reaction tag check.

FSECExecutionContext

Both actions and reactions share the same unified context struct:
FieldTypeFilled By
TargetTWeakObjectPtr<AActor>Caller
OptionalObjectTWeakObjectPtr<UObject>Caller
MagnitudefloatCaller
EventTagFGameplayTagCaller
ContextTagsFGameplayTagContainerCaller
TargetDataFGameplayAbilityTargetDataHandleCaller
DistanceToTargetfloatSystem (auto)
DirectionToTargetFVectorSystem (auto)
InstigatorTagsFGameplayTagContainerSystem (auto from ASC)
TargetTagsFGameplayTagContainerSystem (auto from ASC)
Use USECTargetDataLibrary to pack spatial data (directions, locations, transforms) into TargetData.

SECReactionSetComponent (Pawn)

Mirrors SECActionSetComponent for actions. Lives on the pawn, handles reaction set resolution, replication, and runtime overrides.
EnemyCharacterBase creates this component automatically. If you use a custom pawn, add it manually.

Resolution Priority

When GetReactionSetForRole() is called, it walks this chain and returns the first match:
  1. Runtime OverrideSetRuntimeOverride(ReactionSet). Highest priority. Use for boss phase transitions.
  2. Equipped Weapon — If the weapon actor implements ISECWeaponReactionSetProvider, calls GetWeaponReactionSetForRole(RoleTag).
  3. Config Role — Role-specific ReactionSet from EnemyAIConfig → RoleReactionSets[CurrentRole].
  4. Config Default — Fallback from EnemyAIConfig → DefaultReactionSet.
  5. Component DefaultSECReactionSetComponent → DefaultReactionSet property (set in Blueprint).
  6. None — No ReactionSet found. AI cannot react.
This is the same resolution chain as SECActionSetComponent. Learn one, know both.

Swap Modes

When a reaction set swap is triggered while a reaction is already executing:
ModeBehavior
ESECReactionSetSwapMode::ImmediateCancel the current reaction and swap immediately.
ESECReactionSetSwapMode::WaitForCompletionQueue the swap. It applies automatically when the current reaction completes.
Set via the SwapMode property on the component, or pass it to SetRuntimeOverride().

Replicated State

These properties replicate to all clients for UI, VFX, and animation purposes:
PropertyTypePurpose
CurrentReactionIdFNameName of the currently executing reaction
bReactionExecutingboolTrue while a reaction is active
ActiveReactionSetUReactionSet*The currently resolved reaction set

Delegates

DelegateParametersWhen
OnReactionExecutionStartedFName ReactionIdReaction begins (replicated)
OnReactionExecutionCompletedFName ReactionId, bool bSuccessReaction finishes (replicated)
OnReactionSetChangedUReactionSet* NewSetActive ReactionSet changes (replicated)
OnReactionCooldownStartedFName ReactionId, float DurationCooldown begins (replicated)
OnReactionCooldownExpiredFName ReactionIdCooldown expires (replicated)

Weapon Reaction Set Provider

To make a weapon provide reaction sets, implement ISECWeaponReactionSetProvider on your weapon actor:
// Your weapon actor implements ISECWeaponReactionSetProvider
UReactionSet* GetWeaponReactionSetForRole(FGameplayTag RoleTag) override
{
    // Return a role-specific reaction set, or a single set for all roles
    return MyWeaponReactionSet;
}
// Tell the pawn about the weapon:
Pawn->ReactionSetComponent->SetEquippedWeapon(WeaponActor);
 
// Clear to revert to Config-based resolution:
Pawn->ReactionSetComponent->SetEquippedWeapon(nullptr);

Key API

FunctionDescription
SetEquippedWeapon(AActor*)Set weapon for reaction set resolution via ISECWeaponReactionSetProvider.
SetRuntimeOverride(UReactionSet*)Override resolution chain. Takes effect immediately or queued based on SwapMode.
ClearRuntimeOverride()Remove override, fall back to normal resolution.
SyncForCombatRole(FGameplayTag Role, UEnemyAIConfig* Config)Called by controller on role change. Resolves and applies the correct set.
GetRemainingCooldown(ReactionId)Returns remaining cooldown seconds (0 if not cooling down).
IsReactionOnCooldown(ReactionId)Returns true if the reaction is on cooldown.
GetAllActiveCooldowns()Returns array of all active FSECCooldownEntry structs.

Quick Setup

  1. Create Asset: Right-click → Miscellaneous → Data Asset → ReactionSet
  2. Define Reactions: Fill in ReactionId, AbilityClass, AbilityTag, AbilityEndTag
  3. Assign to Config: Set DefaultReactionSet on your EnemyAIConfig, or add role-specific sets to RoleReactionSets
  4. Pawn Component: If using EnemyCharacterBase, SECReactionSetComponent is already created. Otherwise, add it to your pawn.
  5. Controller Component: Add ReactionEvaluationComponent to your AIController (already present on EnemyControllerBase)
  6. Trigger from your logic:
// In your Blueprint or C++ (e.g., OnDamageReceived):
FSECExecutionContext Context;
Context.Target = DamageInstigator;
Context.Magnitude = DamageAmount;
Context.EventTag = SEC_Stimulus_MeleeHit;
Context.TargetData = USECTargetDataLibrary::MakeTargetDataFromDirection(HitDirection);
 
FChosenReaction Chosen = ReactionComp->EvaluateBestReaction(SEC_Reaction_Defensive);
if (Chosen.IsValid())
{
    ReactionComp->ExecuteReaction(Chosen.ReactionId, Context);
}

Context reuse

FSECExecutionContext is the same struct used by actions. If you're already familiar with ExecuteActionWithContext(), reaction context works identically - same fields, same auto-population, same TargetData library.

Working example included

Open BP_SEC_EnemyCharacter and check its ApplyDamage interface implementation to see exactly how reactions are triggered from damage events. The included DA_ReactionSet data asset is pre-configured with example reactions you can experiment with and use as a starting point.


Gameplay Tags

TagPurpose
SEC.Reaction.EndDefault end tag for reactions
SEC.Reaction.BlockReactionsBlocks ALL reactions when present on ASC
SEC.Action.BlockActionsBlocks ALL actions when present on ASC
SEC.State.ReactionActiveConvenience tag for reaction-to-reaction blocking

Integration Points

SystemHow It Connects
Action SystemReactions can cancel actions and block new ones via AddTags. Resolution chain mirrors SECActionSetComponent.
Combat RolesRole changes trigger automatic ReactionSet swaps via SECReactionSetComponent
Melee TraceOnMeleeHitResponse delegate triggers reactions on attack; ISECDamageable returns parry/block results
Threat DetectionThreatLevel can inform which reaction category to evaluate
MultiplayerReaction state replicates to clients via SECReactionSetComponent. Cosmetics use Gameplay Cues.