Action System

Documentation Unreal Engine AI Actions

Action selection that scores available moves and picks the highest for the current context.


Scores every action against live battlefield data: context tags, plus distance, angle, and health when you add scorers for those dimensions.
Action Evaluation Flow
Content>Plugins>SoulslikeEnemyCombat>Components
ActionEvaluationComponent
EvaluateBestAction()
Gameplay Ability
(Simple Action)
Behavior Tree Sequence
(Complex Logic)
The system dynamically chooses the execution method. Behavior Trees can be chained for complex sequences.

When to Use This

  • Any AI that needs to choose between multiple attacks or behaviors
  • Enemies with distance-dependent movesets (melee vs ranged)
  • Boss fights with phase-based action sets
  • Group AI where different roles use different attack patterns
This system runs on its own. You can use it without the other plugin systems.

How Scoring Works

On every evaluation, all valid actions compete. The highest score above zero wins.
Final Score
ActionEvaluationComponent>Scoring
Final Score
Selection Weight
Risk Penalty
Tag Multipliers
Novelty
Penalizes recent use
Chain Bonus
Scorers
Distance · Angle · Health · Speed · custom
Runtime Modifiers
GlobalMultiplier × SetActionOverride
Jitter
±5%, deterministic
Highest score above zero wins. Leave a scorer off and that dimension drops out of the product. Jitter is deterministic from the decision context seed.
Runtime modifiers scale every action's score: SetGlobalMultiplier buffs or nerfs the whole moveset (0 blocks all selection), and SetActionOverride(ActionId, Multiplier) targets one action. They multiply together in the formula.
A small jitter factor (deterministic ±5% from the decision context seed) breaks ties so two enemies with the same data do not pick identically every tick.
Two base numbers sit on every action:
  • Selection Weight biases the action up or down. Weight 2.0 doubles its score against a weight 1.0 action.
  • Risk Penalty divides the final score to hold risky moves back. 1.0 is no penalty, 2.0 halves the score (a high-risk heavy attack), 0.5 doubles it (a safe move).
Scorers (distance, angle, health, speed, and custom ones) fold in as multipliers. Leave them off and that dimension does not affect the score. An action with no Distance Scorer scores the same at any range. A scorer that returns 0 zeroes the whole product for that action.

Distance, Angle, Health & Speed Scorers

Add a Distance Scorer to score against AI-to-target distance. Leave it off and the action is distance-agnostic. The same pattern covers angle, health, and speed: one scorer per dimension, each with a Range curve (FRangeEval) for its optimal zone. An attack can be valid from 0-500cm while its score peaks at 150-250cm.
Each scorer exposes a single Range property. Below Min Value the score is 0; it ramps up to 1.0 across Optimal Min, holds at 1.0 through the Optimal Min → Optimal Max sweet spot, then ramps back down to 0 at Max Value. Exponent shapes the falloff. 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
The four built-ins and their Range defaults:
ScorerReadsRange default
Distance ScorerAI-to-target distance in cm (0 with no target)MakeMeleeRange()
Angle ScorerAngle to target in degrees, 0 facing it, 180 away (0 with no target)MakeFrontalAngle()
Health ScorerAI health, 0-1 fractionMakeAlwaysOne()
Speed ScorerAI horizontal speed in cm/sMakeAlwaysOne()
FRangeEval presets to assign to a scorer's Range:
  • MakeMeleeRange() - Close-range distance band (0-100-250-500)
  • MakeRangedRange() - Long-range distance band (300-500-1000-1800)
  • MakeFrontalAngle() - Forward-facing cone (0-0-30-90, Angle Scorer)
  • MakeLowHealthRange() - Peaks at low health (0-0-0.25-0.5, Health Scorer, for desperate moves)
  • MakeAlwaysOne() - Always returns 1.0 (no preference)
A Health Scorer or Speed Scorer left at its MakeAlwaysOne() default has no effect. Shape the Health Scorer's Range to favor low or high health. Use the Speed Scorer for pace-dependent moves (a running attack that should score only while sprinting). For health or stamina backed by a GameplayAttribute, use an Attribute Scorer or Attribute Gate.

Cooldowns & Variance

An action's Cooldown group controls reuse. Cooldown Duration is the base delay. Initial Cooldown blocks the move at spawn so enemies cannot open with a heavy hit. Randomization (%) adds ±variance (0.2 = ±20%). Spawn Cooldown Chance rolls a starting cooldown so a pack does not attack in lockstep. Max Consecutive Uses (-1 = unlimited, 2 = must switch after two uses) stops spam. Defaults:
Cooldown
Cooldown Duration
5
Initial Cooldown
0
Randomization (%)
0.2
Spawn Cooldown Chance
0
Max Consecutive Uses
-1
An action commits its cooldown only after BeginExecute succeeds and PreExecute hooks do not veto. If activation fails or a hook sets bVetoExecution, no cooldown is stamped and the action can retry next tick. A CanActivateAbility refusal during ability activation follows the same rule: off cooldown, free to retry.

Recovery Time

Recovery time suspends offensive action selection for a set number of seconds after an enemy commits a move. The enemy still strafes, repositions, and reacts (parry, dodge, counter). It does not start a new attack. Use it when a relentless attacker needs gaps between swings.
Cooldown gates re-use of one move. Recovery gates all offensive actions after one ends. Tune the global windows on UEnemyAIConfig under Recovery. Every field defaults to 0, so recovery stays off until you set a value.
FieldSets
ActionRecoveryTimeSeconds suspended after an action completes or times out.
InterruptRecoveryTimeSeconds suspended after an action is interrupted or cancelled. Keep it shorter than ActionRecoveryTime so a parry or stagger does not double-stun.
ActionRecoveryTimeRandomizationPlus or minus jitter on the completion recovery, so a pack does not act in lockstep.
Per-action override. On the action's Recovery group, enable bOverrideRecoveryTime and set RecoveryTime. The value replaces the global for that action; it does not add to it. With config ActionRecoveryTime = 3, a jab that sets RecoveryTime = 1 recovers in one second while other moves recover in three.
To watch the gate at runtime, set SEC.Debug.LogActionDecisions 1. While an enemy is held off, the decision log prints Recovering (X.Xs remaining). For a HUD, BlueprintPure IsActionRecovering(float& OutRemaining) returns whether the enemy is recovering and the seconds left.
AdvancedRecovery window by exit path, and combo follow-ups
By exit path:
  • Completed uses the action's effective recovery (the override if set, otherwise the global ActionRecoveryTime).
  • Interrupted or cancelled uses InterruptRecoveryTime.
  • Timed out uses the global ActionRecoveryTime, never the per-action override.
  • Failed stamps no recovery window.
Combos flow through recovery. An action's preferred follow-up (PreferredNextActionId) can still be selected while the enemy recovers, for ChainFollowupWindowSeconds after the action ends. Set that window on the evaluation component (default 0.6). 0 makes the follow-up wait out recovery like any other action. Only the named follow-up bypasses the gate. A non-completion end (interrupt or cancel) clears it, so a parried enemy cannot combo through its own recovery.
Stacked or conditional recovery. For recovery that depends on state (extra recovery at low health), call RequestRecoveryTime from a Lifecycle Hook PostExecute. The contribution folds into the window.

Novelty & Chains

  • Novelty: Recently used actions take a score penalty, so the AI mixes moves without hard randomization.
  • Chains: Set PreferredNextActionId on an action. After a Light Attack, Heavy Finisher receives a ChainBonusMultiplier boost (default 1.5).

Organic Combos

Raise Smash's score after Hit. If the player rolls out of range, the AI drops the combo. It does not swing at air.

Preconditions (Hard Gates)

Before scoring, every action runs hard gates. Fail one and the action drops out for that tick.
GateBlocks the action when
Enabled (bEnabled)The action is toggled off in the ActionSet.
SEC.Action.BlockActionsThe tag sits on the AI's ability system component (add it via a reaction's AddTags to freeze offense).
Valid execution methodThe action has no configured method, or HasValidData returns false.
Cooldown / repeat limitThe action is still cooling down, or Max Consecutive Uses is exhausted.
RecoveryThe AI is in a post-action recovery window (chain follow-ups excepted).
RequiresTagsThe combined Self/Target/World tags don't hold all of them.
BlockTagsAny of them sits on Self, Target, or World.
Require Line Of SightThe box is ticked and the AI has no clear view of its target.
GatesAny USECGate in the action's CustomScoring → Gates array returns false. See Scorers & Gates.
Stamina only blocks when you add a Stamina Gate (MinStamina) to the Scoring list, or an Attribute Gate for a GAS stamina pool. Decision-context Stamina defaults to 100, so an action with no Stamina Gate ignores stamina.
Require Line Of Sight reads FDecisionContext::bHasLOS. The Build Decision Context task traces from the AI's eyes to the target each tick. Tick the box for ranged shots and gap-closers. Leave it off for moves that land blind (radial slam, taunt).
While an action runs it applies its AddTags to the AI's ability system component. SEC.State.IsMovementBlocked ships in that list by default and holds the enemy still mid-swing. Remove it from AddTags to let the AI move during that action.

Execution Methods

Each action runs through an Execution Method: an instanced object under Execution that owns how the action runs. Two built-ins ship. You can write your own.
MethodRuns
Gameplay AbilityA single Gameplay Ability, activated by tag or event.
Behavior Tree SequenceOne or more Behavior Trees, one after another.
Pick a method once per action. Its fields appear inline under it: ability actions show ability fields, behavior-tree actions show the tree list.

Gameplay Ability

Activates a Gameplay Ability and listens for its end tag. Set these on the method:
FieldSets
AbilityClassThe ability to activate. Auto-granted to the character's ASC.
ActivationModeByTag or ByEvent (see Activation Modes).
AbilityTagActivation tag in ByTag mode, event tag in ByEvent mode. Auto-filled from the ability's class defaults.
AbilityEndTagTag the system waits for to end the action. Auto-filled from the ability.
AbilityTimeoutSeconds to wait for the end tag before giving up. 0 waits indefinitely.
Your ability class must inherit from UGameplayAbilityBase. It handles the end-event handshake that tells the Action System when the ability finishes. Without it, the AI gets stuck waiting.
UGameplayAbilityBase provides rotation lock (bLockAIRotation: the AI commits to the attack direction with no mid-swing tracking), motion warping toward the target, and the action context (target, distance, direction, magnitude, tags) on activation.

Motion Warping

Steer an attack toward its target. UGameplayAbilityBase registers a warp target from the resolved action context. The montage's Motion Warp notify performs the warp.
AdvancedMotion warping setup and tuning
UGameplayAbilityBase steers an attack toward its target with Unreal's Motion Warping. On activation the ability registers a warp target from the resolved action context: the event payload's target for event-driven activations, or AIController::GetFocusActor() when tag or direct activations carry no payload target. The plugin registers the target. The montage's Motion Warp anim notify performs the warp.
The pawn must carry a UMotionWarpingComponent. AEnemyCharacterBase does not add one, so warping no-ops on pawns without it.
PropertyDefaultEffect
MotionWarpingTargetNameTargetWarp target key. Must match the Warp Target Name on the montage's Motion Warp notify. Set to None to disable warp setup for the ability.
MotionWarpingOffset100.0Warp point offset in cm, placed in front of the target toward the AI. Doubles as a switch-off distance: once the AI is closer than this, root-motion warping pauses so the attack does not overshoot. The pause condition is created only while the offset is above 0.
MaxWarpDistance0.0Skip gate. If the AI is closer than this when the ability activates, no warp target is set up. The default of 0 leaves the gate off (any distance warps).
bLockAIRotationtrueStops the controller from yawing the pawn toward focus during the ability. Warping runs on a separate path and still rotates the pawn during the notify window.
Setup:
  1. Add a UMotionWarpingComponent to your character, in the Blueprint through Add Component or in a C++ subclass through CreateDefaultSubobject.
  2. Place a Motion Warp notify window over the attack's windup in the montage.
  3. Set the ability's MotionWarpingTargetName to the notify's Warp Target Name.
Where the AI tracks versus commits depends on the notify window: rotation warps toward the target across the window, then the swing commits past it. bLockAIRotation and warping run on separate paths, so a committed attack can still track during the warp window while controller facing stays locked.
AM_SEC_TwoHanded_TripleAttack_Montage in the showcase content has a working Motion Warp setup. Copy its notify and warp values as a starting point.

Activation Modes

Most abilities activate by tag (the default). Switch to event activation when the ability needs its payload on the first frame.
AdvancedByTag vs ByEvent activation
The ability method's ActivationMode chooses how the ability is triggered:
ModeHow It Works
ByTag (default)Calls TryActivateAbilitiesByTag using AbilityTag. The ability must have matching AbilityTags in its class defaults.
ByEventSends a Gameplay Event using AbilityTag as the event tag. The ability must have a matching Trigger entry (Gameplay Event) in its class defaults, or use WaitGameplayEvent.
ByEvent mode sends a payload with the instigator and current target actor. Your ability can read them on the first frame.
// Standard tag-based activation (default)
ActivationMode = EAbilityActivationMode::ByTag;
AbilityTag = "SEC.Action.Attack.Light";
 
// Event-based activation
ActivationMode = EAbilityActivationMode::ByEvent;
AbilityTag = "SEC.Action.Attack.SweepEvent";  // Used as the event tag
ByEvent abilities do not need AbilityTags in their class defaults. Instead, add a Trigger entry with the matching tag and set its source to Gameplay Event. The AbilityTag field serves double duty: tag activation in ByTag mode, event tag in ByEvent mode.

Behavior Tree Sequence

Runs one or more Behavior Trees in order. Use it for multi-stage behaviors: circling, investigating, boss sequences. Set these on the method:
FieldSets
BehaviorTreeSequenceThe trees to run, in order.
BehaviorTreeTimeoutPer-tree timeout in seconds. 0 waits for each tree to finish.
AbilityToGrantOptional ability granted to the ASC so a BT task in the sequence can trigger it by tag. Setting it auto-fills the tags below.
AbilityTag, AbilityEndTag, ActivationModePassed to the blackboard for a BT task that activates the tagged ability.
AdvancedBlackboard keys and the ability-activation BT tasks
Before the first tree starts, the method writes these blackboard keys:
Blackboard KeyTypeValue
SEC_ActionIdNameThe action's ActionId
SEC_AbilityTagNameThe AbilityTag, written as the tag's full name
SEC_AbilityEndTagNameThe AbilityEndTag, written as the tag's full name
SEC_ActivationModeName"ByTag" or "ByEvent"
SEC_TargetActorObjectThe current target (focus) actor
SEC_SelfActorObjectThe AI pawn
SEC_DistanceFloatDistance to target
These keys must exist in your Blackboard Data Asset, with the exact types above, for the writes to succeed. A missing key or a type mismatch (for example a String key where the plugin writes a Name) is silently ignored. Add all SEC_ keys to your Blackboard asset.
The plugin provides two Behavior Tree tasks for activating abilities inside a BT:
Activate Blackboard Ability reads the ability tag and activation mode from the blackboard keys above, then activates the ability the Behavior Tree Sequence granted. Its ActivationMode property has three options:
  • FromBlackboard (default) - reads SEC_ActivationMode, falling back to ByTag if the key is empty.
  • ByTag - always uses tag-based activation.
  • ByEvent - always uses event-based activation.
Activate Ability is a standalone task where you set the ability class directly. It has ActivationMode (ByTag / ByEvent) and an EventTag field that appears only in ByEvent mode. Use it to activate a specific ability from a BT without going through the Action System.

Custom Execution Methods

The two built-ins cover abilities and behavior trees. For a latent task, spawned projectile, timeline, or third-party system, write your own method. USECExecutionMethod is Blueprintable (Blueprint or C++).
AdvancedWriting a custom execution method
Subclass USECExecutionMethod in Blueprint or C++. Your method carries its own payload fields and slots into the action's Execution Method picker next to the built-ins.
The action holds your method as a definition and never mutates it. On execution the component duplicates the definition. The running instance can hold per-execution state (spawned actor, montage handle, elapsed counter) as ordinary properties.
Override the phases you need:
FunctionWhen it runsWhat to do
BeginExecuteThe action startsStart your work. Return false to fail the start; the action charges no cooldown and can retry.
TickExecuteEach frame while runningPoll or advance. Runs only when bWantsTick is set.
AbortExecuteThe action is interrupted or cancelledTear down: cancel timers, destroy spawned actors, unbind callbacks, restore state.
Call FinishExecution(bSuccess, Reason) when your work ends. The component completes the action on the next tick, so completion never re-enters your BeginExecute. The ability method calls it from the ability's end-tag event; the behavior-tree method calls it when the last tree finishes.
The component owns everything around the method: cooldowns, recovery, lifecycle hooks, the timeout, and the started and completed delegates. Your method owns only the mechanism.
Four definition-side queries shape how the component treats the method. Keep them as pure reads:
QueryReturns
HasValidDataWhether the method is configured. An action whose method returns false is skipped, the same as an action with no execution data.
GetAbilityToGrantAn ability class to auto-grant to the character's ASC, or none for a method that grants nothing.
GetExecutionTimeoutSeconds before the component force-ends the method. Return 0 to wait on FinishExecution alone.
GetDisplayNameThe label shown on the action's Execution Method slot and in the logs.
Report completion by calling FinishExecution. A method that never calls it and returns 0 from GetExecutionTimeout ends only when the 30-second hard safety fires.
A method instance is stateful and private to one execution, unlike a Scorer or Gate, which stays stateless and shared across every AI using the asset. Hold per-execution state on the method; the component discards the instance when the action ends.

Choosing the Action Set

The plugin resolves which ActionSet an enemy uses through a priority chain. Combat role changes trigger SECCombatControllerComponent to sync.
The resolver walks this chain and returns the first match:
PrioritySourceUse
1 (highest)Runtime OverrideSetRuntimeOverride(ActionSet). For boss phase transitions.
2Equipped WeaponA weapon actor implementing ISECWeaponActionSetProvider.
3Config RoleRole-specific ActionSet from EnemyAIConfig (Attacker vs Flanker).
4Config DefaultFallback ActionSet from EnemyAIConfig.
5Component DefaultSECActionSetComponent → DefaultActionSet on the pawn.
6NoneNo ActionSet found. The AI moves but never attacks.
Weapon and override changes take effect immediately. No role change required.
SECReactionSetComponent uses the same resolution chain for reaction sets.

Weapon-Driven AI

Equipped Weapon outranks Config. Give a skeleton a Bow and it becomes a sniper. Disarm it and it falls back to its Config set as a brawler.

Weapon Action Set Provider

AdvancedMaking a weapon supply its own action set
Implement the ISECWeaponActionSetProvider interface on your weapon actor (Blueprint or C++). Override GetWeaponActionSetForRole(RoleTag) to return the appropriate UActionSet*.
// Tell the pawn about the weapon:
Pawn->ActionSetComponent->SetEquippedWeapon(WeaponActor);
 
// Clear to revert to Config-based resolution:
Pawn->ActionSetComponent->SetEquippedWeapon(nullptr);

Dynamic Actions

AdvancedGranting and revoking actions at runtime
Grant and revoke individual actions without swapping the whole set. These are BlueprintCallable (Blueprint or C++).
Pawn->ActionSetComponent->GrantAction(ThrowGrenadeSpec);
Pawn->ActionSetComponent->RevokeAction("ThrowGrenade");
Granted actions persist across role changes and action set swaps. They participate in evaluation alongside the base ActionSet: same scoring, same cooldowns.
For bulk operations:
Pawn->ActionSetComponent->GrantActionsFromSet(BonusActionSet);  // Add all from set
Pawn->ActionSetComponent->RevokeActionsFromSet(BonusActionSet); // Remove all from set
Pawn->ActionSetComponent->ClearGrantedActions();                // Remove all granted

Scorers & Gates

Every scoring dimension past Selection Weight, Risk Penalty, tags, novelty, and chains is a Scorer or Gate on the action. Distance, angle, health, and speed each have a built-in scorer. Tags stay built into the pipeline. For mana, ally count, terrain, or faction state, attach your own. Each FActionSpec has a Scoring group with two arrays:
Content Browser
Content>Plugins>SoulslikeEnemyCombat>ActionSets
Scoring list
Action Scorers
SelectionWeight
Always present · × base
No scorers or gates — action scores on weight alone.
score = Weight × Distance · (Stamina pass) × Yours
Add from library
Distance ScorerData Asset (Scorer)
Stamina GateData Asset (Gate)
Create customBlueprint (USECScorer)
Each scorer multiplies in; each gate can veto. Mix built-ins with custom subclasses.
TypeBase ClassReturnsEffect
ScorerUSECScorerA multiplier (1.0 = no effect)Folds into the action score. Above 1 favors, below 1 disfavors.
GateUSECGatetrue / falseA false drops the action from selection, like a built-in precondition.
Add an entry, pick a built-in class or your own Blueprint/C++ subclass, and set its parameters inline. Same authoring pattern as Role Evaluators and Positioning Rules.
An action with an empty Scoring list scores on SelectionWeight alone. No scorer or gate can veto it. Built-in hard gates still apply (cooldown, block tags, ability activation). Adding a scorer opts the action into that dimension. Omitting it leaves the action indifferent to it.

Built-in Classes

ClassKindPropertyUse
Distance ScorerScorerRange (default MakeMeleeRange())Score by AI-to-target distance in cm (0 with no target). The Range OptimalMin/OptimalMax also feed the positioning query.
Angle ScorerScorerRange (default MakeFrontalAngle())Score by angle to the target in degrees, 0 facing it, 180 away (0 with no target).
Health ScorerScorerRange (default MakeAlwaysOne())Score by AI health, a 0-1 fraction from the decision context. For a GAS health attribute, use Attribute Scorer.
Speed ScorerScorerRange (default MakeAlwaysOne())Score by AI horizontal speed in cm/s.
Stamina GateGateMinStamina (default 0)Veto unless decision-context Stamina is at least MinStamina. Stamina is 100 by default; for a GAS stamina attribute, use Attribute Gate.
Attribute ScorerScorerAttribute, NormalizeBy, ValueEvalScale by a GameplayAttribute through an FRangeEval curve. Optional normalize-by attribute (e.g. Mana / MaxMana).
Attribute GateGateAttribute, MinValue, MaxValueAllow only while an attribute sits between a min and a max.
Health or stamina backed by a GameplayAttribute? The decision context does not read GAS attributes. Health Scorer and Stamina Gate read the decision-context snapshot (BasicHealthComponent health, default-100 stamina). Point an Attribute Scorer or Attribute Gate at your attribute.

Examples

Cast a spell only above a mana threshold. Add an Attribute Gate, set Attribute = Mana, MinValue = 30. The spell drops out whenever mana is below 30.
Favor a heavy attack as rage builds. Add an Attribute Scorer, set Attribute = Rage, NormalizeBy = MaxRage, and shape the curve to peak near full. The attack scores higher as rage fills.
The same Scorers and Gates work on reactions. On the reaction path the component builds a live spatial snapshot (distance, angle, speed, health) from the pawn and TargetOverride instead of the action decision context. See Reaction System.

Custom Scorers & Gates

For anything the built-ins miss, write your own. USECScorer and USECGate are both Blueprintable (Blueprint or C++).
AdvancedWriting a custom scorer or gate
Subclass USECScorer or USECGate and override its one function (ScoreMultiplier or PassesGate). FSECScoringContext carries the controller, target, owning ASC, the seed that SeededRandom uses, and a decision-context snapshot (below).
Keep scorer and gate subclasses stateless. One instance is shared across every AI using the asset, so mutable fields alias across enemies. For randomness, use the SeededRandom helper.
SeededRandom draws from the per-action seed in FSECScoringContext. On the reaction path that seed is 0, so SeededRandom returns a fixed value. Vary reaction randomness through a built-in factor or your own context read.
The scoring context snapshot. FSECScoringContext copies a decision-context snapshot once per evaluation. Built-in Distance/Angle/Health/Speed Scorers and the Stamina Gate read from it:
FieldMeaning
DistanceDistance to the focus target in cm (0 with no target).
AngleDegAbsAbsolute angle to the target in degrees, 0 facing it (0 with no target).
SpeedAI horizontal movement speed in cm/s.
HealthPercentageAI health as a 0-1 fraction.
StaminaAI stamina (default scale 0-100).
These fields fill from the action decision context on the action path. On the reaction path, ReactionEvaluationComponent builds a separate live snapshot (distance, angle, speed, health from the pawn and TargetOverride; stamina stays at the default 100 unless you use an Attribute Gate). Pass TargetOverride when evaluating reactions so spatial scorers measure against the attacker.
Labeling. Each scorer and gate reports a display name through overridable GetDisplayName() (BlueprintNativeEvent). It defaults to the class display name and drives the decision log, the score breakdown, and the editor array row title on UE 5.7+. Override it for a custom or dynamic label (fold the configured range into the name).

Project-Wide Hooks

Built-in scorers and gates cover per-action rules. For logic that applies to every action, use the two override hooks or a lifecycle hook.

Custom Scoring Hooks

AdvancedCanExecuteAction and ModifyActionScore
ActionEvaluationComponent exposes two BlueprintNativeEvent hooks (Blueprint or C++). Prefer Scorers & Gates for per-action rules.
CanExecuteAction - Veto an action after all built-in gates pass.
bool CanExecuteAction(FName ActionId, const FDecisionContext& Context);
// Return false to block the action.
ModifyActionScore - Adjust the score after the pipeline computes it.
float ModifyActionScore(FName ActionId, float BaseScore, const FDecisionContext& Context);
// Return a modified score. Return BaseScore for no change.
Override these in a Blueprint or C++ subclass of UActionEvaluationComponent.

Lifecycle Hooks

A lifecycle hook runs your logic around an action as it executes, on every exit path. Use it for a telegraph before a heavy swing, analytics, or a veto scorers cannot express. USECActionHook is Blueprintable (Blueprint or C++).
AdvancedAuthoring and attaching a lifecycle hook
Subclass USECActionHook (Instanced, abstract) and override the phases you need.
USECActionHook override functions in the Blueprint editor: PreExecute, TickExecute, and PostExecute under the SEC category, plus GetDisplayName
A hook has three phases:
PhaseWhen it runsGives you
PreExecuteAfter BeginExecute succeeds, before cooldown commit, AddTags, and started delegatesA chance to abort: set bVetoExecution to tear down the start without stamping cooldown.
TickExecuteEach tick while the action executesPer-frame work. Off by default; set bWantsTick to opt in.
PostExecuteWhen the action ends, on every exit pathThe end reason (Completed, Interrupted, Cancelled, TimedOut, or Failed).
Attach a hook in two places. Per action: FActionSpec.Hook. For the whole enemy: UEnemyAIConfig.GlobalHook (under Recovery), which runs for every action it commits.
When both are set they compose, global first then per-action, in each phase:
  • Veto is OR. If either hook sets bVetoExecution in PreExecute, the action aborts.
  • Recovery accumulates. RequestRecoveryTime contributions from both hooks add up.
For stacked or conditional recovery time, call RequestRecoveryTime on the context in PostExecute. Example: add extra recovery when the enemy ends the action below a health threshold.

World State Tags

WorldTags carry global game state (boss phases, weather, arena state) into AI scoring. Push them through USECWorldTagSubsystem (or the one-node USECWorldTagLibrary helper). The build task copies them into FDecisionContext::WorldTags each tick, where they feed RequiresTags, BlockTags, and TagScoreMultipliers.
The plugin ships three example world tags: SEC.World.Combat.Active, SEC.World.Boss.Active, and SEC.World.Boss.Casting. Use them as starters or define your own.
AdvancedWorld tag API: mutators and client reads
// Server BP, anywhere
USECWorldTagLibrary::AddWorldTag(this, SEC.World.Combat.Active);
USECWorldTagLibrary::AddWorldTagForDuration(this, SEC.World.Boss.Casting, 3.0f);
USECWorldTagLibrary::AddWorldTagUntil(this, SEC.World.Boss.Active, {YourGame.Boss.Defeated});
VariantBehavior
AddWorldTagPermanent until RemoveWorldTag.
AddWorldTagForDurationRemoves after N seconds. Re-adding refreshes the timer.
AddWorldTagUntilRemoves when any sentinel tag is added.
Mutators run on the server only (BlueprintAuthorityOnly); client calls no-op silently.
Client-side reads (UI, audio): drop USECWorldTagComponent on GameState. The component replicates the subsystem's tags and broadcasts OnTagsChanged on clients. Without the component, USECWorldTagLibrary::GetWorldTags returns empty on clients and warns once.

Contextual Execution

Sometimes an action needs a specific target, item, or magnitude. Creating a new ActionId per variation does not scale. Pass Context.
AdvancedExecuting with context and gating on the payload

1. Execute with Context

Call this from Blueprint or C++ to pass dynamic data:
FSECExecutionContext Context;
Context.Target = CustomTargetActor;
Context.OptionalObject = SomeItem;
Context.Magnitude = 0.5f;
Context.ContextTags.AddTag(Tag_QuickVariant);
 
ActionEvaluationComponent->ExecuteActionWithContext("SpecialAttack", Context);

2. Receive in Ability

Your ability (inheriting from UGameplayAbilityBase) captures this data.
  • Event: On Action Context Received (Blueprint)
  • Accessor: GetActionContext() (Blueprint Pure)
Context mapping:
Context FieldMaps To
TargetActionContext.Target
OptionalObjectActionContext.OptionalObject
MagnitudeActionContext.Magnitude
ContextTagsActionContext.ContextTags
If Target is not provided in context, the system falls back to the AI's current Focus Actor.

3. Gate Before Activation

SEC hydrates the context before the ability activates. Override CanActivateAbility (Blueprint or C++) on your UGameplayAbilityBase, read GetActionContext(), and return false to block.
  • Event-triggered activations (ByEvent, Execute With Context, reactions) fill the context from the payload.
  • Tag and direct activations fill it from the AI's focus target.
// Inside your ability's CanActivateAbility override:
const FSECExecutionContext& Ctx = GetActionContext();
if (!Ctx.GetTarget() || Ctx.Magnitude < RequiredCharge)
{
    return false; // refuse before the ability runs
}
A refusal costs nothing: SEC commits no cooldown and interrupts no running action. Use it when your rule needs the payload. Use CanExecuteAction for self or world gates that do not.

Quick Setup

  1. Create Asset: Right-click → MiscellaneousData AssetActionSet.
  2. Define Actions: Each entry needs an ActionId, SelectionWeight, an Execution Method, and (for range behavior) a Distance Scorer in its Scoring list. A new action with no Distance Scorer is distance-agnostic. Toggle bEnabled off to disable an action without deleting it.
  3. Assign: Drop the ActionSet into your EnemyAIConfig, or set it for testing:
ActionEvaluationComponent->ActiveActionSet = MyActionSet;
See Configuration Reference for the full EnemyAIConfig structure.

Debug Tools

// On ActionEvaluationComponent:
ActionEvalComp->bDebugLogDecisions = true;  // Log scoring breakdown
ActionEvalComp->bDebugLogExecution = true;  // Log execution flow
The SEC.Debug.LogActionDecisions 1 console variable does the same globally without touching the component.

Integration Points

SystemHow It Connects
Movement SystemProvides distance/angle for scoring context
Combat RolesRole changes trigger automatic ActionSet swaps
Threat DetectionThreat level feeds into FDecisionContext
MultiplayerAction state replicates to clients via SECActionSetComponent
Custom character classes: ActionEvaluationComponent resolves the AbilitySystemComponent from the possessed pawn via IAbilitySystemInterface on possession. Pawns that do not implement this interface cause ability-based actions to silently fail. See Getting Started for setup details.

Key API

AdvancedComponent and delegate reference
ComponentLocationRole
ActionEvaluationComponentControllerScoring, evaluation, execution
SECActionSetComponentPawnResolution, replication, weapon/override management
SECCombatControllerComponentControllerOrchestrates sync on role changes
ActionEvaluationComponent (Controller)
  • EvaluateBestAction(Context, Time, OutChosen) - Run the scoring pipeline.
  • ExecuteAction(ActionId) - Force-execute a specific action.
  • ExecuteActionWithContext(Id, Context) - Execute with custom data (Target, etc.).
  • SetGlobalMultiplier(Multiplier) - Runtime buff/nerf for every action (0 blocks all selection).
  • SetActionOverride(ActionId, Multiplier) - Per-action multiplier, folded with the global value.
  • CanExecuteAction() / ModifyActionScore() - Override hooks (see above).
SECActionSetComponent (Pawn)
  • SetEquippedWeapon(Actor) - Weapon-driven action set override.
  • SetRuntimeOverride(ActionSet) / ClearRuntimeOverride() - Boss phase override.
  • GrantAction() / RevokeAction() - Runtime action management.
  • OnActionExecutionStarted / OnActionExecutionCompleted - Replicated delegates for client UI/FX.
  • OnActionSetChanged - Fires when the active ActionSet changes (replicated).
  • OnActionCooldownStarted(ActionId, Duration) / OnActionCooldownExpired(ActionId) - Cooldown lifecycle delegates (replicated).
  • GetRemainingCooldown(ActionId) / IsActionOnCooldown(ActionId) / GetAllActiveCooldowns() - Query current cooldown state.
AdvancedUpgrading from an older version
ActionSet assets migrate once on load; behavior is unchanged, and re-saving the asset persists the migration.
  • Execution methods. Each action's old execution mode becomes an Execution Method: a Gameplay Ability action becomes a Gameplay Ability method carrying the same ability, tags, and timeout; a Behavior Tree action becomes a Behavior Tree Sequence method carrying the same trees and timeout.
  • Scorers and gates (from v1.6). Each action's old distance and angle evaluations become a Distance Scorer and Angle Scorer, a non-default health or speed range becomes a Health or Speed Scorer, and a stamina cost above 0 becomes a Stamina Gate. New actions you author ship with no scorers, so they are distance-agnostic until you add a Distance Scorer.
  • Display names. GetDisplayName() replaces the old ScorerName / GateName text fields, which are removed. Any label typed into them on a pre-existing asset is lost on load; re-label through a GetDisplayName() override.