Targeting System

Documentation Unreal Engine AI Targeting Multi-Player

Register combat targets, pick one per AI with TargetSelector, handle orphans and team filtering.


Register player pawns as combat targets. Each AI gets one assigned target. UTargetSelector picks the pool if several exist.

When to Use This

  • Co-op with two or more player pawns as separate target pools.
  • Versus or FFA where AI should spread across teams or players.
  • Spawning AI before the player exists (lazy assignment on RegisterCombatTarget).
  • Bosses pinned to one player (bIgnoreTargetRedistribution).
Single-player: one RegisterCombatTarget call with bAutoAssignUnassigned = true pins unassigned combatants to that pawn. If an AI registers while targets already exist and no selector is configured, RegisterCombatant falls back to the first non-friendly entry in CombatTargets.
Viewport
LVL_SEC_Showcase>Targeting
Selector:
Target 1(2)
Target 2(3)
E1
E2
E3
E4
E5
Target Counts
Target 1: 2
Target 2: 3
Event Log
No events yet
Click targets to eliminate. System periodically re-evaluates assignments based on strategy.

Register Combat Targets

Use UAICombatRoleSubsystem (same subsystem as Combat Roles).
On the server, register any pawn other AIs should fight over. In Blueprint, on the player pawn's BeginPlay: add Get AI Combat Role Subsystem (no inputs), then call Register Combat Target with Target Actor set to Self and Auto Assign Unassigned checked.
Player pawn · Event BeginPlay
AICombatRoleSubsystem
Register Combat Target
Target is AI Combat Role Subsystem
Target
Target ActorSelf
Auto Assign Unassignedtrue
Reevaluate Existingfalse
Search context menu: Register Combat Target, category AI | Combat Role | Targeting.
FlagEffect
bAutoAssignUnassignedAssigns unassigned combatants directly to this target (skips team-friendly pairs), then ForceReassignment() (role evaluation). Default: false.
bReevaluateExistingCalls ReevaluateAllTargets() so assigned combatants re-run TargetSelector (honors bIgnoreTargetRedistribution). Default: false.
SetCombatTarget(Player) is deprecated: it calls RegisterCombatTarget(Player, true) only.
Each registered target owns a separate role pool. Two players can each cap at three Attackers independently.

Target Selectors

On registration, target loss, or redistribution, the subsystem resolves UTargetSelector and calls SelectTarget.
SelectorBehaviorTypical use
UFirstTargetSelectorFirst entry in the candidate listSingle-player default when you add the class in settings
UClosestTargetSelectorLowest squared distance from FTargetSelectionContextMelee chase
UBalancedTargetSelectorFewest assigned combatants; sticky while tied for least-loadedCo-op fairness
URandomTargetSelectorUniform random pickChaos encounters
UAwarenessFilteredTargetSelectorFilters by USECAwarenessComponent state, then runs InnerSelector (closest if null)Stealth / perception-gated aggro
Selectors are Abstract, Blueprintable, EditInlineNew. Set one on EnemyAIConfig or project settings.

Awareness Filtered Target Selector

Set Target Selector to Awareness Filtered Target Selector, then fill the instanced fields below. Add Awareness Config on the same EnemyAIConfig (Awareness System).
Details
Awareness Filtered Target Selector (AwarenessFilteredTargetSelector)
Awareness Filter
Min Required State
Lost
Inner Selector
None
Instanced selector on EnemyAIConfig. Requires USECAwarenessComponent on the AI controller.
FieldNotes
Min Required StateTarget awareness on this AI must be >= this state (static_cast on enum order). Default Lost passes Lost and Detected; Unknown, Forgotten, and Suspicious fail.
Inner SelectorRuns on the filtered list. Null picks closest by squared distance.
Missing awareness componentWarns once per controller, then runs the inner selector (or closest fallback) on the unfiltered list.

Resolution order

  1. EnemyAIConfig → Target Selector
  2. Project Settings → Default Target Selector
  3. Registration-only fallback: if both are null during RegisterCombatant, the subsystem assigns the first registered non-friendly target in CombatTargets.
  4. SelectTargetForCombatant / SelectTargetFor: if no selector is configured, returns nullptr (no pick).
Co-op and redistribution need Default Target Selector (for example UBalancedTargetSelector) or per-AI overrides.

Configure per AI

Combat Role
Target Selector
None
Ignore Target Redistribution
FieldNotes
Target SelectorInstanced selector object. Null uses project default.
Ignore Target RedistributionSkips this AI in ReevaluateAllTargets and periodic target refresh. Boss pinned to one player.

Project default

Project Settings → Plugins → Soulslike Enemy Combat → Combat Roles → Target Selection:
Target Selection
Default Target Selector
None

Lazy Assignment

Register combatants before you register targets. They stay unassigned until you call RegisterCombatTarget.
Set FAIRoleRegistrationParams::InitialTarget at registration time to pin one AI to a specific pool without waiting for a selector.
With bAutoAssignUnassigned = true, waiting combatants get this target directly (no selector) and run role evaluation. With false, unassigned combatants call SelectTargetForCombatant once a target appears (requires a configured selector).

Target Loss

UnregisterCombatTarget or OnDestroyed on a registered target triggers this sequence:
  1. Combatants on that target lose AssignedTarget; role resets to SEC.Role.None where applicable.
  2. OnCombatTargetUnregistered broadcasts.
  3. SECCombatControllerComponent::HandleCombatTargetLost fires OnCombatTargetLost on each orphaned controller.
  4. Subsystem calls SelectTargetForCombatant(..., bIsTargetLossReselection = true) and AssignCombatantToTarget if the selector returns an actor. (SelectTargetFor does not set this flag.)
  5. OnCombatantsOrphaned broadcasts with the lost target and orphaned controllers.
Return nullptr from SelectTarget when Context.bIsTargetLossReselection is true to skip auto-reassign on the orphan path. Handle the orphan in OnCombatTargetLost.
EnemyControllerBase exposes Blueprint event K2_OnCombatTargetLost on the same path.
USECCombatControllerComponent* CombatComp =
    Controller->FindComponentByClass<USECCombatControllerComponent>();
CombatComp->OnCombatTargetLost.AddDynamic(this, &AMyAIController::HandleTargetLost);

Integration

SystemLink
Combat RolesPer-target role pools; see RegisterCombatTarget there.
Awareness SystemUAwarenessFilteredTargetSelector + AwarenessConfig on the same EnemyAIConfig.
Action SystemPer-action target via Contextual Execution.
Movement SystemMovementEvaluatorComponent reads GetFocusActor(); the subsystem sets focus when it assigns a target.
MultiplayerRegister each player pawn as a combat target on the server.

AdvancedManual control API
FunctionPurpose
AssignCombatantToTarget(Controller, Target, bTriggerRoleEvaluation)Pin an AI to a pool (nullptr = unassigned).
SelectTargetFor(Controller, CandidateTargets)Run configured selector on a custom list (self + friendlies filtered). Returns nullptr if no selector is configured.
ReassignCombatantTarget(Controller)Re-run selector for one AI. On success, assigns the new target. On failure, clears assignment, resets role to None, and fires HandleCombatTargetLost.
ReevaluateAllTargets()Re-run selector for all combatants (skips bIgnoreTargetRedistribution). Keeps the current target if the selector returns nullptr or the same actor.
BalanceCombatantsAcrossTargets(bForceReassignment)Round-robin across targets; ignores selectors.
TransferCombatantsToTarget(From, To, bForceReassignment)Move a pool (for example player death handoff).
SetPrimaryTargetAndAssignAll(Target, bForceReassignment)Assign all combatants to one target.
AdvancedPeriodic target refresh
The reassignment timer updates roles only unless you enable Re-evaluate Targets On Reassignment under Combat Roles → Timing:
Timing
Role Reassignment Interval
8
Min Time In Role
8
Re-evaluate Targets On Reassignment
The flag runs only with CombatTargets.Num() > 1. Each cycle: ReevaluateAllTargets() (respects bIgnoreTargetRedistribution), then role evaluation. You can set the same flag on FAICombatRoleConfig via UpdateConfig.
Toggle at runtime:
Subsystem->SetReevaluateTargetsOnReassignment(true);
AdvancedCustom UTargetSelector
Subclass UTargetSelector in C++ or Blueprint. Override SelectTarget.
SelectTargetForCombatant and SelectTargetFor build FTargetSelectionContext before they call your selector:
Target Selection Context
Current Target
None
Combatant Count Per Target
0 Map elements
Distance Squared Per Target
0 Map elements
Is Target Loss Reselection
The subsystem strips the AI's own pawn and friendly actors (USECTeamHelpers::AreFriendly) from the candidate list. Return nullptr when Context.bIsTargetLossReselection is true to defer auto-reassign on the orphan path (SelectTargetForCombatant only).
AdvancedTeam filtering
Actors with ISECTeamInterface expose a team ID on the actor or its controller (0-254, or 255 = neutral via SECTeamID::NoTeam). USECTeamHelpers::AreFriendly removes same-team targets from:
  • Selector candidate lists
  • RegisterCombatTarget auto-assign
  • Registration fallback (first non-friendly target)
  • SetPrimaryTargetAndAssignAll and BalanceCombatantsAcrossTargets
Exception: TransferCombatantsToTarget moves combatants between pools as-is; it does not team-filter.
Actors without the interface count as neutral and stay valid targets for everyone.
Blueprint: Class Settings → Interfaces → SEC Team Interface → implement Get SEC Team ID.
Constants in SECTeamID: Player = 0, Enemy = 1, NoTeam = 255.
Damage (separate from targeting): USECTeamHelpers::CanDamageWithFilter with ESECTeamFilter (NoFilter, EnemiesOnly, FriendliesOnly, NeutralOnly). Melee trace exposes Team Filter on the pawn's SECMeleeTraceComponent:
Details
SEC Melee Trace Component (SECMeleeTraceComponent)
Damage
Team Filter
No Filter
Damage category on the wielder trace component. Filters who can be hit, separate from combat target selection.
AdvancedQuery and legacy API
Query
FunctionReturns
GetAllCombatTargets / HasCombatTargetRegistered target pool
GetCombatantTarget(Controller)AI's assigned target
GetCombatantsForTarget(Target)Combatants on that target
GetRoleCountForTarget(Role, Target)Per-pool role usage
Deprecated (compiler warnings)
LegacyReplacement
SetCombatTargetRegisterCombatTarget(..., true)
GetCombatTargetGetCombatantTarget or GetAllCombatTargets (returns first registered target)
AdvancedSubsystem delegates
DelegateFires when
OnCombatTargetRegistered / OnCombatTargetUnregisteredTarget added or removed
OnCombatantsOrphanedTarget lost; controllers need a new assignment
OnCombatRoleChangedForTargetRole change with target context