API Reference
Documentation Unreal Engine AI Reference
The C++ and Blueprint surface: the controller components, the combat token subsystem, the melee trace pipeline, territory lookups, and the scorer and gate bases.
The classes and their public members. For building an enemy, start with Getting Started; this page is the reference you reach for when extending the system in C++. Each system's own page covers the same ground for Blueprint.
SECCombatControllerComponent
The hub every setup needs. Add it to any
AAIController and the combat system works. It resolves the AI config, registers combat roles, syncs role-based state, and orchestrates death.To read the config or role from outside the component, call the static helpers instead of casting to
AEnemyControllerBase. They work on any controller carrying the component.Static helpers
| Function | Returns |
|---|---|
ResolveAIConfig(Controller) | The controller's cached config, or the pawn's config via IEnemyAIConfigProvider, or null |
ResolveCurrentCombatRole(Controller) | The current combat role tag, or an empty tag |
Instance accessors
| Function | Returns |
|---|---|
GetCachedAIConfig() | The resolved UEnemyAIConfig in use |
GetCurrentCombatRole() | The current combat role tag |
Overridable hooks (
BlueprintNativeEvent, call the parent to keep default behavior)| Function | Purpose |
|---|---|
SyncStateForCombatRole(RoleTag) | Runs when the role changes. Override to swap animation sets, trigger effects, or switch StateTree assets. Call Super to keep the action set, reaction set, weapon set and movement profile sync. |
HandleCombatTargetLost(LostTarget) | Runs when the target is destroyed or unregistered. Override for fallback behavior (patrol, switch aggro). The default broadcasts OnCombatTargetLost. |
Blueprint-callable
| Function | Purpose |
|---|---|
ApplyLocalCombatRole(RoleTag) | Adopt a role locally without registering with the subsystem. Syncs the action set and movement profile like a subsystem-assigned role, but consumes no slots and joins no coordination. For bosses and solo enemies. |
HandleDeath() | Full SEC shutdown, idempotent. Cancels the in-flight action and reaction, stops the brain, stops melee tracing, disables threat detection, unregisters from the role subsystem, clears focus, then broadcasts OnDeath. Does not unpossess or destroy the pawn. |
HandleRevive() | Brings a dead enemy back under AI control: releases the action block death took, restarts the brain, and turns threat detection, perception and role registration back on, then broadcasts OnRevive. Call it on a revive or when a pooled enemy is reused. A controller that died stays blocked from acting until it runs. The weapon death unequipped is not re-equipped, and perception returns only when the AI Config manages it. |
Properties
| Property | Default | Purpose |
|---|---|---|
DefaultAIConfig | null | Fallback config when the pawn provides none via IEnemyAIConfigProvider |
bAutoRegisterForCombatRoles | true | Register with the combat role subsystem on possession |
RoleRegistrationParams | n/a | Registration parameters used when the pawn has no config |
bEnableThreatDetection | true | Enable threat detection on BeginPlay when a ThreatDetectionComponent is present |
bAutoHandleDeathOnHealthDepleted | true | Call HandleDeath() automatically when the pawn's health vital empties |
bDropWeaponOnDeath | true | Ask the weapon to drop itself on death, with a suggested 10-second lifespan. The weapon's On Unequipped event turns on the physics |
Delegates
| Delegate | Fires |
|---|---|
OnCombatRoleChanged(NewRole, OldRole) | When the role changes, from subsystem assignment or local apply |
OnCombatTargetLost(LostTarget) | When the target is destroyed or unregistered |
OnCombatRoleSystemReady(Controller) | After registration with the role subsystem. Safe to read the initial role here |
OnDeath() | At the end of HandleDeath(), once every system is stopped. Safe to ragdoll, spawn VFX, or destroy |
OnRevive() | At the end of HandleRevive(), once every system is running again |
EnemyControllerBase
The convenience controller. It creates every combat component as a default subobject and adds facing and rotation behavior on top of
SECCombatControllerComponent.Rotation lock (freezes facing during a committed attack)
| Function | Purpose |
|---|---|
LockRotationBy(LockOwner) | Hold a rotation lock keyed to an owner. Releases when the owner is destroyed, or when an InstancedPerActor ability goes inactive. Defaults to the caller. |
UnlockRotationBy(LockOwner) | Release that owner's lock |
LockRotation() / UnlockRotation() | Keyless counter-based lock; balance each call |
ForceUnlockRotation() | Clear both the counter and the owner set |
IsRotationLocked() | True while any lock holds |
UGameplayAbilityBase locks rotation through this API when bLockAIRotation is set, so montage-driven attacks commit without extra wiring.Facing
| Member | Default | Purpose |
|---|---|---|
GetDesiredFacingLocation(OutLocation) | n/a | BlueprintNativeEvent returning the world location to face. Override for custom facing |
SmoothFocusInterpSpeed | 165.0 | Interpolation speed for smooth focus rotation |
bFaceLastKnownWhenNotDetected | true | Track the live target only while Detected; otherwise face its last-known location instead of through walls |
Team
| Member | Purpose |
|---|---|
USECTeamComponent | Holds the team on the possessed pawn: TeamId (0 = player, 1 = enemy, 255 = neutral), Free-For-All, and the exclusion rules |
IGenericTeamAgentInterface | Answers AI Perception and EQS by reading that component |
Blueprint events (forwarded from the combat component)
| Event | Fires |
|---|---|
K2_OnCombatRoleAssigned(NewRole, OldRole) | On role assignment |
K2_OnCombatTargetLost(LostTarget) | When the target is lost |
K2_OnReactionSetApplied(RoleTag, ReactionSet) | When a reaction set is applied via role sync |
K2_OnDeath() | After HandleDeath() completes; safe to ragdoll or play a death montage |
HandleDeath() on the controller is a convenience wrapper around the component's HandleDeath(). Component accessors: GetCombatControllerComponent, GetHelperBTComponent, GetReactionEvaluationComponent, GetAwarenessComponent. Delegates: OnFocusSet, OnFocusCleared (fires only when there was a focus to clear), OnActionStateChanged.SECBrainComponent
Chooses how the AI runs from its config: a StateTree when one is named, otherwise a native C++ combat loop.
| Member | Default | Purpose |
|---|---|---|
RefreshBrain() | n/a | Re-evaluate the config and switch brains if the StateTree choice changed. Call after changing the config at runtime without re-possessing |
IsRunningNativeBrain() | n/a | True while the native loop drives the AI |
EvaluationCooldown | 0.1 | Seconds between action evaluations in the native loop |
In native mode the component holds the controller's brain slot, so
HandleDeath stops it and the per-action behavior-tree swap restores it.BotStateTreeAIComponent
Extends Unreal's
UStateTreeAIComponent. SECBrainComponent creates it at runtime when the config names a StateTree. It resolves the config on possession in this order:- The controller's
SECCombatControllerComponentcached config - A pawn implementing
IEnemyAIConfigProvider - The
FallbackStateTreeon this component
| Member | Default | Purpose |
|---|---|---|
InitializeFromAIConfig(AIConfig) | n/a | Set and start the config's StateTree. Returns true on success |
AddLinkedStateTreeOverride(StateTag, StateTreeReference) | n/a | Swap a sub-tree per gameplay tag, for enemy variants |
bAutoInitializeFromConfig | true | Initialize from the config automatically on possession. Disable for manual control |
FallbackStateTree | null | Used when no config is found or the config names no StateTree |
USECCombatTokenSubsystem
The world subsystem holding the permission slots each target hands out. Server only: every function that changes the ledger returns without effect on a client, and the queries there report no block. See Combat Tokens.
Claiming
bool TryClaimTokens(AActor* Target, AController* Holder, const TArray<FSECTokenRequirement>& Requirements, FSECTokenLeaseHandle& OutLease);
void ReleaseTokens(const FSECTokenLeaseHandle& Lease);
int32 ReleaseAllTokensForHolder(AController* Holder);
bool CanClaimToken(AActor* Target, AController* Holder, FGameplayTag TokenTag, int32 Cost = 1) const;
bool HoldsToken(AActor* Target, AController* Holder, FGameplayTag TokenTag) const;TryClaimTokens takes every listed slot or takes none. ReleaseTokens hands back what one receipt covers and starts the reissue delay; running it twice, or with a receipt for a target that is gone, does nothing. ReleaseAllTokensForHolder reports how many claims it freed. CanClaimToken reserves nothing and reads true while the holder already holds a slot for the tag.Budget and query:
SetTokenBudget, ClearTokenBudget, ClearTarget, GetTokenBudget, GetAvailableTokens, GetTokenHolders, IsTokenTarget, GetTokenPoolStatus. OnTokenAvailabilityChanged(Target, TokenTag, AvailableTokens, Budget) fires when a pool's free count moves.| Struct | Members |
|---|---|
FSECTokenRequirement | TokenTag, Cost (1). Two entries naming one tag keep the larger cost |
FSECTokenLeaseHandle | ClaimId (0 means nothing is held), Target. The target rides on the receipt so a release finds the right pool after the AI has moved on |
FSECTokenPoolStatus | TokenTag, Budget, AvailableTokens, HeldTokens, CoolingTokens, SecondsUntilNextReissue, Holders |
A holder destroyed without releasing frees its slot the next time the pool is read, so an AI that has gone cannot hold a pool shut.
USECTokenGate
static void CollectRequirements(const FSECCustomScoring& Scoring, TArray<FSECTokenRequirement>& OutRequirements);Gathers what a spec's token gates need, merging duplicate tags and skipping inverted gates.
UActionEvaluationComponent calls it as an action starts and claims against the controller's focus actor, the same actor the gate scored against.USECScorer and USECGate
The two bases an action or reaction extends its scoring with, both
Blueprintable and EditInlineNew. Instances live in FSECCustomScoring: Scorers for soft multipliers, Gates for hard vetoes. See Action System.virtual float ScoreMultiplier_Implementation(const FSECScoringContext& Context) const; // USECScorer, 1.0 = no effect
virtual bool PassesGate_Implementation(const FSECScoringContext& Context) const; // USECGate, false vetoes
virtual FString GetDisplayName_Implementation() const; // row title, logs, breakdownSubclasses must be stateless: one instance is shared across every AI using the asset. Derive randomness from
SeededRandom(Context) rather than raw RNG.FSECScoringContext carries Controller, Target, ASC, SpecId, EventTag, Surface, Seed, WindowId, the snapshot fields Distance, AngleDegAbs, Speed, HealthPercentage, Stamina, the Vitals component, and the Stimulus that set a reaction off. It carries no threat figure, so a scorer wanting camera attention reads UThreatDetectionComponent::GetCurrentThreatLevel() off Context.Controller.Surface names the list the scorer is running in (Actions, Reactions, TriggerRules). One class serves all three, and branches on this where the difference matters.USECMeleeTraceComponent
The swept-trace component on the pawn. Five phases of its pipeline are
BlueprintNativeEvent, so a Blueprint subclass overrides the event and a C++ subclass overrides _Implementation. See Melee Trace.FSECDamageInfo BuildDamageInfo(AActor* HitActor, const FHitResult& Hit, const FVector& Direction, const FSECTraceSocket& Socket) const;
void HandleDamageResult(AActor* HitActor, const FSECMeleeHitData& MeleeHitData, const FSECDamageResult& Result);
ESECHitClassification ClassifyHit(AActor* HitActor, const FHitResult& Hit) const;
ESECContactRole ClassifyContact(AActor* HitActor, const FHitResult& Hit) const;
bool ShouldDetectHits() const;ShouldDetectHits answers the authority. HandleDamageResult replaces the recording rule outright, so call RecordHit for the hits an override means to keep. CanHitActor, RecordHit and IsPassThroughTarget are protected, so an override reaches them from the subclass. GetTargetsHitThisWindow is public, for damage falloff across a cleave. BuildSweepQueryParams(FCollisionQueryParams&) is C++ only, for a sweep that asks the world something else, such as complex collision to read the struck face's material.Manual control, for a project not driving traces from a montage:
virtual void StartTracing(const TArray<FName>& SocketIDs, USECDamageConfig* DamageConfig = nullptr, bool bDebugOverride = false);
virtual void StartTracingSimple(const TArray<FName>& SocketIDs, float DamageOverride);
virtual void StopTracing();
bool RegisterTraceSocket_Struct(const FSECTraceSocket& TraceSocket);
void UnregisterTraceSocket(FName ID);
bool UnregisterTraceSocketFrom(FName ID, const USceneComponent* ExpectedSource);
void UnregisterAllSockets();
TArray<FName> GetRegisteredSocketIDs() const;
void AddTraceIgnoredActor(AActor* Actor);
void RemoveTraceIgnoredActor(AActor* Actor);
TArray<AActor*> GetTraceIgnoredActors() const;StartTracingSimple takes a numeric override instead of a config; a value below zero falls back to BaseDamage. RegisterTraceSocket_Struct is the only entry point reading SourceComponentTag and a socket's own filter override, and returns false when the socket named no source, another component holds its ID, or this machine does not detect hits.Payloads
| Struct | Members |
|---|---|
FSECMeleeHitData | HitResult, FromSocketID, DamageConfig, Classification. Carried by OnMeleeHit and OnMeleeSensed |
FSECDamageResult | DamageApplied, AdditionalVitalDamageApplied, bWasFatal, ResultTags. Added by OnMeleeHitResponse |
FSECDamageInfo | Instigator, DamageCauser (the weapon when the socket sits on one, the wielder otherwise), HitResult, HitDirection, DamageDefinition, DamageConfig, DamageTags, CustomPayload |
A target reached through point damage reports what its
TakeDamage returned, with no fatal flag and no result tags. Subclass USECDamageConfig to carry per-attack data of your own and cast it inside the target's HandleIncomingDamage.USECTerritoryComponent
Pawn-side, server only. The route, ground and leash functions are Blueprint exposed and covered on Territory & Patrols. The lookups, and the two members C++ alone reaches:
| Function | Purpose |
|---|---|
static USECTerritoryComponent* FindTerritoryComponent(const AController* Controller) | The component on a controller or its possessed pawn |
static FVector GetPostLocationFor(AActor* Post) | Where a post stands, for any actor, post component or not |
static float GetWanderExtentFor(AActor* Post) | How far an enemy may drift from a post, 0 for an actor with no post component |
static int32 StepPostIndex(ESECPostOrder Order, int32 PostCount, int32 CurrentIndex, int32& InOutPingPongStep) | The next index an order policy names, with no reference to where the pawn is. C++ only; AdvanceRoute is the Blueprint-reachable form |
float GetLeashSlack() const | LeashRadius as a usable number: never negative, and 0 when it is not a real number |
USECTerritoryPostComponent
static USECTerritoryPostComponent* FindPostComponent(const AActor* PostActor) resolves the post component from any actor, and reads null for one carrying none.virtual bool PickPointInArea_Implementation(const AActor* Querier, FVector& OutPoint) const;Override it for a post that works its destination out at runtime.
Querier is the pawn being sent there, which sizes the navmesh query to its agent; returning false stands the enemy on the post itself. The base gathers reachable spots inside the area, up to PointSampleAttempts tries, and picks among them under WanderSelection.