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.UTargetSelectorpicks 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.| Flag | Effect |
|---|---|
bAutoAssignUnassigned | Assigns unassigned combatants directly to this target (skips team-friendly pairs), then ForceReassignment() (role evaluation). Default: false. |
bReevaluateExisting | Calls 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.| Selector | Behavior | Typical use |
|---|---|---|
UFirstTargetSelector | First entry in the candidate list | Single-player default when you add the class in settings |
UClosestTargetSelector | Lowest squared distance from FTargetSelectionContext | Melee chase |
UBalancedTargetSelector | Fewest assigned combatants; sticky while tied for least-loaded | Co-op fairness |
URandomTargetSelector | Uniform random pick | Chaos encounters |
UAwarenessFilteredTargetSelector | Filters 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.
| Field | Notes |
|---|---|
| Min Required State | Target 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 Selector | Runs on the filtered list. Null picks closest by squared distance. |
| Missing awareness component | Warns once per controller, then runs the inner selector (or closest fallback) on the unfiltered list. |
Resolution order
EnemyAIConfig → Target Selector- Project Settings → Default Target Selector
- Registration-only fallback: if both are null during
RegisterCombatant, the subsystem assigns the first registered non-friendly target inCombatTargets. SelectTargetForCombatant/SelectTargetFor: if no selector is configured, returnsnullptr(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
| Field | Notes |
|---|---|
| Target Selector | Instanced selector object. Null uses project default. |
| Ignore Target Redistribution | Skips 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:- Combatants on that target lose
AssignedTarget; role resets toSEC.Role.Nonewhere applicable. OnCombatTargetUnregisteredbroadcasts.SECCombatControllerComponent::HandleCombatTargetLostfiresOnCombatTargetLoston each orphaned controller.- Subsystem calls
SelectTargetForCombatant(..., bIsTargetLossReselection = true)andAssignCombatantToTargetif the selector returns an actor. (SelectTargetFordoes not set this flag.) OnCombatantsOrphanedbroadcasts 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
| System | Link |
|---|---|
| Combat Roles | Per-target role pools; see RegisterCombatTarget there. |
| Awareness System | UAwarenessFilteredTargetSelector + AwarenessConfig on the same EnemyAIConfig. |
| Action System | Per-action target via Contextual Execution. |
| Movement System | MovementEvaluatorComponent reads GetFocusActor(); the subsystem sets focus when it assigns a target. |
| Multiplayer | Register each player pawn as a combat target on the server. |
AdvancedManual control API
| Function | Purpose |
|---|---|
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
RegisterCombatTargetauto-assign- Registration fallback (first non-friendly target)
SetPrimaryTargetAndAssignAllandBalanceCombatantsAcrossTargets
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
| Function | Returns |
|---|---|
GetAllCombatTargets / HasCombatTarget | Registered target pool |
GetCombatantTarget(Controller) | AI's assigned target |
GetCombatantsForTarget(Target) | Combatants on that target |
GetRoleCountForTarget(Role, Target) | Per-pool role usage |
Deprecated (compiler warnings)
| Legacy | Replacement |
|---|---|
SetCombatTarget | RegisterCombatTarget(..., true) |
GetCombatTarget | GetCombatantTarget or GetAllCombatTargets (returns first registered target) |
AdvancedSubsystem delegates
| Delegate | Fires when |
|---|---|
OnCombatTargetRegistered / OnCombatTargetUnregistered | Target added or removed |
OnCombatantsOrphaned | Target lost; controllers need a new assignment |
OnCombatRoleChangedForTarget | Role change with target context |
Full list: Combat Roles → Subsystem delegates.