Combat Roles
Documentation Unreal Engine AI Combat Roles
Coordinate a pack: cap how many enemies attack at once, and swap each enemy's moveset, reactions and movement to match the role it holds.
Four enemies around one player: one pressing the attack, the rest circling and waiting their turn. Roles decide who is which, and swap each enemy's behaviour assets to match.
Set it up
60 seconds- 1
Register the player as a combat target
In the player pawn's BeginPlay, get the AI Combat Role Subsystem and call Register Combat Target withSelf.BP_SEC_PlayerCharacterin the showcase content does exactly this, andBP_Dummyshows the same wiring on a stationary target. - 2
Drop three or more enemies into the level
Each enemy joins the pool when its AI controller possesses it. Auto-Register for Combat Roles on the Enemy AI Config is ticked out of the box, so this step is already done.EnemyConfig_Grunt▾Combat RoleAuto-Register for Combat RolesAllowed Roles (empty = any)EmptyPriority0An untouched config already joins the pool and accepts any role. - 3
Cap how many attack at once
Open Project Settings, Plugins, Soulslike Enemy Combat. Under Role Limits, set the Attacker row to 1 for the classic one-at-a-time fight. It ships at 3. - 4
Give the roles something to do
Open the Enemy AI Config. Under Movement Profiles, set Default Movement Profile to a patient profile, then add a Role Movement Profiles entry forSEC.Role.Attackerpointing at an aggressive one. The showcase content ships five to start from,DA_MovementBehavior_AttackerthroughDA_MovementBehavior_Elite.EnemyConfig_Grunt▾Movement ProfilesManage Movement Profiles AutomaticallyDefault Movement ProfileNone▾Role Movement Profiles0 Array elementsThe default covers every role that has no entry of its own, so one override is enough to see the difference. - 5
Play, and watch the roles swap
Press Play and runSEC.Debug.Role 1in the console. Each enemy gets its name and role above its head, coloured by role: red Attacker, orange Flanker, blue Waiter, green Supporter, purple Elite.
An enemy with no combat target holds no role. Role slots are counted per target, so an enemy the subsystem has not paired with one is skipped both when it registers and on the reassignment timer. If the roles above every head read empty, nothing registered the player. See Getting Started and Targeting.
The six roles
Roles are gameplay tags under
SEC.Role.| Role | Tag | What it is for |
|---|---|---|
| Attacker | SEC.Role.Attacker | Front-line pressure. The role the fight revolves around. |
| Waiter | SEC.Role.Waiter | Holds distance and waits for a slot to open. |
| Flanker | SEC.Role.Flanker | Circles for an angle. |
| Supporter | SEC.Role.Supporter | Ranged fire or support from the back. |
| Elite | SEC.Role.Elite | Boss-tier enemy. Unlimited slots out of the box. |
| None | SEC.Role.None | What an enemy gets when the pass finds no free slot for it. |
An enemy the pass cannot seat holds
SEC.Role.None; the Fallback Role field in Project Settings takes no part in that.Viewport
LVL_SEC_Showcase>Combat Roles
Player
Attacker
Waiter
Waiter
Flanker
Combat Slots
Attackers:1 / 1
(Others must wait)
Enemies dynamically swap roles based on slot availability and priority.
To add a role of your own, define a tag under
SEC.Role in Project Settings, Gameplay Tags, then list it under Additional Roles in Project Settings, Plugins, Soulslike Enemy Combat. It is scored and filled alongside the built-ins.Cap how many hold each role
Role Limits in Project Settings, Plugins, Soulslike Enemy Combat is a list of role and limit pairs. A role with no row has unlimited slots.
| Role | Ships at |
|---|---|
| Attacker | 3 |
| Flanker | 2 |
| Supporter | 2 |
| Waiter | unlimited |
| Elite | unlimited |
| None | unlimited |
Each combat target counts its own slots, so two players can each field three Attackers.
Role limits cap how many enemies hold a role. To cap how many swing at the same moment, reach for Combat Tokens, which gate one action rather than an enemy's whole behaviour set. The two stack.
Give each role its own behaviour
When an enemy's role changes, its controller resolves three assets and applies them.
| Per-role list | Falls back to | Master switch |
|---|---|---|
| Role Action Sets | Default Action Set | Manage Action Sets Automatically |
| Role Reaction Sets | Default Reaction Set | Manage Reaction Sets Automatically |
| Role Movement Profiles | Default Movement Profile | Manage Movement Profiles Automatically |
All three live on the Enemy AI Config. A role with no entry in a list takes that category's default. An equipped weapon's own sets beat the config's role entries, so a bow-carrying skeleton keeps its archer moveset whatever role it holds. See Actions, Reactions, Movement.
Fill in the default as well as the role list. An enemy holding a role you did not list, or no role at all, gets whatever the default names; leave the default empty and it gets nothing.
Steer which enemy gets which role
These sit under Combat Role on the Enemy AI Config.
Combat Role
Auto-Register for Combat Roles
Allowed Roles (empty = any)
0 Gameplay Tags
Priority
0
Preferred Role
None
Fitness Evaluators
0 Array elements
Target Selector
None
Target Filter
None
Ignore Target Redistribution
| Field | What to do with it |
|---|---|
| Auto-Register for Combat Roles | Untick it to keep this enemy out of the pool. It takes no slot and holds no role, and its controller still applies the config's default action set, reaction set and movement profile at possession. |
| Allowed Roles (empty = any) | Empty lets the enemy hold any role. List tags to pin an archer to Waiter and Supporter so it is not sent to the front. |
| Priority | Higher values get first pick of limited slots. 0 for a grunt, 50 for an elite, 100 for a mini-boss, 200 for a boss. |
| Preferred Role | Adds 0.05 to that role's fitness score, enough to break a tie. It must also appear in Allowed Roles, unless that list is empty. |
| Fitness Evaluators | Empty scores on distance alone, closest first, out to 2000 cm. See the drawers below. |
| Target Selector | Which registered target this enemy fights when several exist. See Targeting. |
| Target Filter | Which actors this enemy will fight at all, asked before any target is selected. See Targeting. |
| Ignore Target Redistribution | Pins this enemy to its target while the rest of the pack redistributes. Use it for a boss. |
How often roles change
Timing
Role Reassignment Interval
8
Min Time In Role
8
Reevaluate Targets On Reassignment
| Field | Default | What to do with it |
|---|---|---|
| Role Reassignment Interval | 8 s | How often the whole pool is re-scored. Lower it for a fight that reshuffles quickly. Set it to 0 to stop the timer and drive reassignment by hand. |
| Min Time In Role | 8 s | Hysteresis. An enemy still inside this window gets 0.3 added to its current role's score, which keeps it where it is. Lower it for more churn. |
| Reevaluate Targets On Reassignment | off | Re-runs each enemy's Target Selector before roles are scored. Needs two or more registered targets; it does nothing with one. |
A newly registered enemy does not wait for the timer. It takes the best free slot the moment it registers, without disturbing anyone else.
Debug it
SEC.Debug.Role 1 // Name and role above each enemy, coloured by role
SEC.Debug.LogRoleAssignments 1 // Each role change, with the fitness score behind it
SEC.Debug.LogRoleAssignments 2 // Adds every enemy-to-role pair the pass considered
SEC.Debug.All 1 turns every overlay section on at once, and SEC.Debug.WatchPawn <name> narrows them to one enemy. The role line replicates, so it reads on a client as well as on the server.The SEC Combat Role component on the pawn carries a Debug Overlay Role checkbox that draws the same line for that one enemy without touching the console.
AdvancedNothing changes role
In the order worth checking:
- No registered combat target. Slots are counted per target, so an enemy paired with none is skipped by registration and by the timer alike. This is the common one and it logs at Verbose.
- Auto-Register for Combat Roles is unticked on the config.
- The controller has no SEC Combat Controller component. That component is what calls Register Combatant at possession, so an enemy without it never joins the pool. Calling Register Combatant yourself succeeds either way, on default parameters: any role, priority 0, no evaluators. Enemy Controller Base ships with the component.
- Only one enemy is in the fight. With default limits it takes Attacker and stays there. Roles move when there are more enemies than slots.
- Role Reassignment Interval is 0. The timer auto-starts only when the interval is above 0. Call Force Reassignment yourself, or Start Reassignment Timer after setting an interval.
- Reassignment is paused. Pause Reassignment freezes role changes until Resume Reassignment.
- The role is locked. Force Assign Role with its lock argument set makes the evaluator skip that enemy. Unlock Role clears it.
- The enemy is mid-action. An enemy whose action is executing counts as locked for that pass, keeps its role and holds its slot. Cap Attacker at 1, give it a long attack, and the pool reads frozen from the outside.
- Min Time In Role is long against the interval. The hysteresis bonus then holds a role through most passes.
An enemy reading
SEC.Role.None was scored and found no free slot. Before its first assignment the tag is empty rather than None.AdvancedScore the fit yourself
Leave Fitness Evaluators empty and role fitness comes from distance alone, closest scoring highest, out to 2000 cm. Add evaluators to that list to score on anything else. Several combine into a weighted average.
Three fields sit on each one:
- Role Influence Weights maps a role tag to how strongly this evaluator counts for it: 1.0 full, 0.5 half, 0.0 not at all.
- Influence On Unlisted Roles (0) is what a role missing from that map gets.
- Score Mode reads the raw score as-is (Higher Score = Better Fit) or inverts it (Lower Score = Better Fit).
Distance Evaluator
Scores how far the enemy stands from its assigned target. The score peaks at 1 at Ideal Distance and falls off linearly to 0 at Ideal Distance plus or minus Effective Range. It ships weighted onto Attacker.
Affected Roles
Role Influence Weights
SEC.Role.Attacker → 1.0
Influence On Unlisted Roles
0
Scoring
Score Mode
Higher Score = Better Fit
Distance Settings
Ideal Distance
0
Effective Range
2000
- Closest enemies become Attackers: Ideal Distance 0, Effective Range 2000, Higher Score = Better Fit, weights
{ Attacker: 1.0 }. - Farthest become Waiters: the same distances, Lower Score = Better Fit, weights
{ Waiter: 1.0 }. - Mid-range enemies become Flankers: Ideal Distance 1000, Effective Range 500, Higher Score = Better Fit, weights
{ Flanker: 1.0 }.
Cooldown Evaluator
Scores how much of the enemy's moveset is on cooldown, so a spent Attacker rotates out and a fresh one steps in.
Affected Roles
Role Influence Weights
0 Map elements
Influence On Unlisted Roles
0
Scoring
Score Mode
Higher Score = Better Fit
Cooldown Settings
Current Role Penalty
0.5
It answers 0.5, a neutral score, whenever the role the enemy currently holds is missing from Role Influence Weights. List the roles you want to fatigue (Attacker, Flanker) and leave the passive ones out. Current Role Penalty (0.5) multiplies the score while the pass considers keeping the enemy in the role it already holds, so a lower value pushes harder for a swap.
AdvancedWriting your own evaluator
Subclass Role Evaluator in Blueprint or C++ and override Evaluate Fitness, returning 0 for unfit and 1 for a perfect fit. The subsystem applies Score Mode and the per-role weights afterwards, so return the raw number.
The context hands you four things:
| Field | Use |
|---|---|
| Controller | Reach the pawn and its components from here. |
| Role | The role under evaluation. |
| Current Role | The role the enemy holds right now. |
| Time In Current Role | Seconds it has held that role. |
Get the assigned target with Get Combatant Target on the subsystem. Return 0.5 when your criterion does not apply.
AdvancedBosses and enemies that skip coordination
| Goal | Setting |
|---|---|
| Skip slot coordination entirely | Untick Auto-Register for Combat Roles on the Enemy AI Config. |
| Stay on one player while the pack redistributes | Tick Ignore Target Redistribution. |
| Script a fixed role | Force Assign Role with its lock argument set. |
| Wear a role tag without consuming a slot | Apply Local Combat Role on the SEC Combat Controller component. |
Apply Local Combat Role resolves and applies the action set, reaction set, weapon sets and movement profile for a role, and publishes the tag for clients, without joining the coordination. An enemy still registered with the subsystem has it overwritten on the next pass, so pair it with an unregistered enemy.
An unregistered enemy wires its own sets through the config defaults and consumes no slots, which leaves the pack's Attacker cap for the enemies that need it.
AdvancedSeveral players, several pools
Each registered target keeps an independent pool with its own slot counts. Register a second player and the two pools fill separately.
| Function | Does |
|---|---|
| Register Combat Target | Adds the actor to the pool. Auto Assign Unassigned evaluates roles straight away instead of waiting for the timer; Reevaluate Existing also re-runs every assigned enemy's Target Selector. |
| Unregister Combat Target | Removes it. Enemies left behind lose their target, reset to SEC.Role.None, and re-run their own Target Selector across the remaining targets. One whose selector answers nothing stays unassigned. |
| Assign Combatant To Target | Moves one enemy to a pool. This one carries out the order as given, without asking the enemy's Target Filter. |
| Reassign Combatant Target | Re-runs one enemy's Target Selector. |
| Reevaluate All Targets | Re-runs every assigned enemy's Target Selector. Honours Ignore Target Redistribution. |
| Transfer Combatants To Target | Hands one pool over to another target, for a player death handoff. |
| Balance Combatants Across Targets | Round-robins the pack evenly, ignoring Target Selectors. |
| Set Primary Target And Assign All | Puts the whole pack on one target, for a boss or a VIP. |
| Force Reassignment For Target | Re-scores one pool instead of all of them. |
| Get Role Count For Target, Get Combatants With Role For Target | Per-pool slot usage. |
Team rules still apply. An enemy whose own Target Filter turns a target aside keeps what it had rather than being handed something it will not fight.
MultiplayerReading the role on a client
Role evaluation runs on the server, and AI controllers do not exist on clients. The SEC Combat Role component on the pawn replicates the tag so a nameplate, an icon or a debug overlay can read it anywhere.
Details
SEC Combat Role
AI|Combat Role
Combat Role
SEC.Role.Attacker
Replicated runtime state on the pawn. SECCombatControllerComponent calls SetCombatRole on the server; clients bind OnCombatRoleChanged.
Enemy Character Base adds the component by default. Read Get Combat Role, or bind On Combat Role Changed, which fires on the server and on every client. Set Combat Role is server only; the controller calls it, and calling it anywhere else changes what clients see and nothing more, since the behaviour assets resolve on the controller.
AlternativeDriving roles from your own controller
A custom AI controller with no SEC Combat Controller component still receives the role change on the global delegate, but nothing swaps its assets. Bind On Combat Role Changed and sync by hand through the SEC Combat Role Sync Library:
| Function | Syncs |
|---|---|
| Sync All For Combat Role | Action set, reaction set, the sets the equipped weapon gives, and the movement profile. |
| Sync Action Set For Role | The action set alone. |
| Sync Reaction Set For Role | The reaction set alone. |
| Sync Movement Profile For Role | The movement profile alone. |
Each takes the new role tag and finds the components it needs on the pawn and the controller. Sync All For Combat Role and Sync Movement Profile For Role also take the resolved Enemy AI Config; the action set and reaction set functions take the pawn and the role tag alone.
AdvancedChanging the rules at runtime
Every function below sits on the AI Combat Role Subsystem and is callable from Blueprint.
Slots and timing
| Function | Does |
|---|---|
| Set Role Limit Override | Caps a role for this session. -1 is unlimited, 0 disables the role. Clear Role Limit Override and Clear All Role Limit Overrides put the project settings back. |
| Set Reassignment Interval | Seconds between passes. 0 stops the timer. |
| Set Min Time In Role | The hysteresis window. |
| Set Reevaluate Targets On Reassignment | Target re-pick before each pass. |
| Update Config | Bulk update. With attrition on, existing holders keep their roles when a limit shrinks instead of being stripped. |
| Reset To Default Settings | Drops every runtime override. |
Assignment and queries
| Function | Does |
|---|---|
| Force Reassignment | Re-scores every pool now. |
| Pause Reassignment, Resume Reassignment | Freeze role changes through a cutscene. |
| Start Reassignment Timer, Stop Reassignment Timer | Timer control. Start Reassignment Timer logs a warning and returns while the interval sits at 0, so set an interval first. |
| Force Assign Role, Unlock Role, Is Role Locked | Scripted roles. |
| Get Role, Get Role Count, Get Combatants With Role | Current assignments. |
| Is Registered, Get All Combatants, Get Total Combatant Count | Pool membership. |
An enrage phase that opens a slot is Set Role Limit Override on the Attacker tag followed by Force Reassignment, so the extra attacker steps in on the same frame rather than at the next 8 second pass.
AdvancedEvents you can bind
| Event | Fires when |
|---|---|
| On Combat Role Changed | Any role change. |
| On Combat Role Changed For Target | The same, carrying which target the enemy is assigned to. |
| On Combatant Registered, On Combatant Unregistered | An enemy joins or leaves the pool. |
| On Combat Target Registered, On Combat Target Unregistered | A target is added or removed. |
| On Combat Target Changed | A primary-target handoff. |
| On Combatants Orphaned | A target was lost, carrying the enemies left behind. Their roles reset to SEC.Role.None; reassign them here. |