Getting Started with SoulslikeCombat

Documentation Unreal Engine AI Tutorial

Install the plugin and build a working enemy from scratch: pawn, controller, ability, ActionSet, and config.


What this plugin is

SoulslikeCombat gives Unreal Engine enemies tactical souls-like behavior through a set of drop-in components. The AI maintains distance, strafes, scores its attacks, reacts to hits, and coordinates with other enemies so they take turns instead of swarming.
You build enemies from data assets and Blueprints. Shipping a working enemy takes no C++. Three kinds of collapsed drawers appear throughout this guide, all closed by default so the main path stays clean:
  • Alternative drawers show a different way to reach the same result, no code required.
  • Advanced drawers cover deeper options and tuning.
  • C++ drawers hold code internals for programmers extending the plugin.
Runs on Unreal Engine 5.6, 5.7, and 5.8. Works in single-player and multiplayer (listen and dedicated server). Current plugin version: 2.3.0.

Install

1. Install to your engine version

Get SoulslikeCombat on Fab, open the Epic Games Launcher, and click Install to Engine. Pick the Unreal Engine version you want to use it with (5.6, 5.7, or 5.8). The plugin installs into that engine with prebuilt binaries.

2. Enable it in your project

Open your project, go to Plugins, search "SoulslikeCombat", and tick it on. Restart the editor when prompted.

3. Confirm the dependencies

The plugin depends on four engine plugins and turns them on for you: GameplayAbilities, StateTree, GameplayStateTree, and MotionWarping. Check Edit → Plugins to confirm they are enabled. MotionWarping drives the built-in attack warping; leave it on unless you replace the ability base.

4. Try the showcase

Open Content/SoulslikeEnemyCombat/Showcase/Maps/LVL_SEC_Showcase and press Play. You get a working enemy to study before building your own.

Build your first enemy

You need two Blueprints and two data assets:
PieceMade fromRole
Enemy pawnEnemyCharacterBaseThe body: mesh, collision, combat components
AI controllerEnemyControllerBaseThe brain: movement, action selection, reactions
Attack abilityGA_SEC_AttackOne attack (plays a montage)
ActionSetData AssetThe movelist the AI scores and picks from
The steps below go in order, so nothing references something you have not made yet.

Step 1: Create the enemy pawn

Right-click in the Content Browser → Blueprint Class → search EnemyCharacterBase. Name it BP_MyEnemy.
Set it up like any character:
  • Assign your skeletal mesh
  • Size the capsule collision
  • Set a movement speed (400 is a good start)
EnemyCharacterBase already ships these components:
ComponentClass
Ability System ComponentUAbilitySystemComponent
Melee Trace ComponentUSECMeleeTraceComponent
Action Set ComponentUSECActionSetComponent
Reaction Set ComponentUSECReactionSetComponent
Combat Role ComponentUSECCombatRoleComponent
AlternativeUse your own character, no C++
Already have a character Blueprint? Reparent it to EnemyCharacterBase instead of rebuilding it. No code required.
  1. Open your character Blueprint.
  2. File → Reparent Blueprint, then pick EnemyCharacterBase.
  3. Your existing components, variables, and graphs stay. You inherit the ability system component, the four SEC components (melee trace, action set, reaction set, combat role), the three combat interfaces, and the dedicated-server socket fix.
  4. If your Blueprint already added its own AbilitySystemComponent or any SEC component, delete the duplicates so only the inherited ones remain.
  5. Set AIConfig on the Blueprint (Step 5 covers this), then assign the mesh and controller as usual.
The action and reaction loops read the pawn's ability system component through IAbilitySystemInterface, and a pure Blueprint cannot implement that interface. Reparenting to EnemyCharacterBase is how you get it without code, so reach for it whenever your character can inherit from EnemyCharacterBase.
C++Replicate EnemyCharacterBase in C++
Reparenting fails when your character must inherit from a different C++ class. Subclass ACharacter (or your own base) in C++ and reproduce these five things:
  1. Implement three interfaces: IAbilitySystemInterface, IEnemyAIConfigProvider, and ISECDamageable.
  2. Create the components as default subobjects in the constructor:
    • UAbilitySystemComponent
    • USECMeleeTraceComponent
    • USECActionSetComponent
    • USECCombatRoleComponent
    • USECReactionSetComponent
  3. Return the ability system component from GetAbilitySystemComponent(). The action and reaction loops read the pawn's ASC through this interface, so this step is mandatory.
  4. Return the config from GetAIConfig_Implementation(). Expose an EditAnywhere TObjectPtr<UEnemyAIConfig> property and return it, so the controller reads a per-pawn config on possession. Return nullptr to fall back to the controller's config.
  5. Keep dedicated-server sockets correct in BeginPlay: when GetNetMode() == NM_DedicatedServer, set the mesh's VisibilityBasedAnimTickOption to AlwaysTickPoseAndRefreshBones. A dedicated server never renders, so without this the mesh holds its bind pose and socket-based melee traces fire from the wrong location.
The base also disables actor tick (PrimaryActorTick.bCanEverTick = false); the combat components run their own updates, so the pawn needs no tick. ISECDamageable::ApplyDamage receives structured hits from the melee trace pipeline; override it to apply damage the way your game needs. Skip the interface and the actor still takes damage through Unreal's ApplyPointDamage fallback.

Step 2: Create the AI controller

Right-click → Blueprint Class → search EnemyControllerBase. Name it BP_MyEnemyController.
It comes pre-wired with every component the AI needs:
ComponentPurpose
SECCombatControllerComponentCore plumbing: config, roles, subsystem registration
MovementEvaluatorComponentTactical positioning and strafing
ActionEvaluationComponentScores and runs actions
ReactionEvaluationComponentRuns event-driven reactions (parry, flinch)
ThreatDetectionComponentTracks when the player looks at the AI (disabled by default)
AwarenessComponentPer-AI perception memory (sight, hearing, damage)
SECBrainComponentRuns your StateTree, or a native combat loop if none is set
HelperBTComponentRuns short behavior-tree sequences for Do Action (not the brain)
AlternativeUse your own AI controller, no C++
Already have an AI controller Blueprint? Reparent it to EnemyControllerBase. No code required.
  1. Open your controller Blueprint.
  2. File → Reparent Blueprint, then pick EnemyControllerBase.
  3. You inherit the combat evaluators, HelperBTComponent (Do Action BT helper), the committed-attack rotation lock, smooth focus, and awareness-based facing.
Adding the SEC components to a bare AAIController is not equivalent. EnemyControllerBase overrides UpdateControlRotation to hold the AI's facing during an attack and to face a target's last-known location, and the attack abilities lock rotation by casting the controller to AEnemyControllerBase. A plain controller keeps tracking the target mid-swing and loses that facing logic, since none of it is exposed to Blueprint. Reparent to EnemyControllerBase, or subclass it in C++ if you need a different base.

Step 3: Create the attack ability

Right-click → Blueprint Class → search GA_SEC_Attack. Name it GA_LightAttack. GA_SEC_Attack is a ready-made attack ability: it plays a montage, forwards the montage's notify events, and ends the ability when the montage finishes.
Set it up:
  • Assign your attack montage. Assign several montages and the ability plays a random one each activation.
  • Wire On Notify Begin and On Notify End on the montage task (USECAbilityTask_PlayMontage in GA_SEC_Attack) to run logic at notify windows: enable the melee trace, spawn VFX, apply effects.
That covers a basic attack. GA_SEC_Attack inherits from GameplayAbilityBase, so the action system detects when the attack starts and ends.
AlternativeStart from GameplayAbilityBase instead
For an attack that does not fit the montage flow, build one straight from GameplayAbilityBase. Create a Blueprint from GameplayAbilityBase, then in the graph:
  1. ActivateAbility
  2. Play Montage and Wait with your attack montage
  3. End Ability
GameplayAbilityBase broadcasts its end tag (SEC.Action.End) on EndAbility, and the action system waits for that tag to know the attack finished. GA_SEC_Attack is this same wiring plus montage randomization and the notify events, so reach for the base class only when you need something the ready-made ability does not do.
C++What GameplayAbilityBase does for you
Inherit from GameplayAbilityBase (not the raw UGameplayAbility) so the action system integrates automatically. It provides:
  • Ability end tag. AbilityEndTag defaults to SEC.Action.End, broadcast on EndAbility. The execution method waits on this to complete the action.
  • Rotation lock. bLockAIRotation freezes facing during the swing, so the AI commits instead of tracking the player mid-attack.
  • Motion warping. Sets a warp target from the AI's focus actor (MotionWarpingTargetName, MotionWarpingOffset), so attacks land at range.
  • Action context. GetActionContext() returns the target, distance, direction, and magnitude, valid from CanActivateAbility through activation.

Step 4: Create the ActionSet

Right-click → Miscellaneous → Data AssetActionSet. Name it DA_MyEnemyActions. Add one entry to Actions and fill it in:
FieldValue
Action IDLightAttack
Execution MethodGameplay Ability → set Ability Class to GA_LightAttack
Cooldown → Duration2.0
Selection Weight1.0
Then add a scorer so the AI only picks this attack at the right distance. In the action's Scoring → Scorers list, add a Distance Scorer. Its Range defaults to a melee band (0 / 100 / 250 / 500), meaning full score between 100 and 250 cm, fading to zero by 500. Add an Angle Scorer too if you want the AI to attack only when facing the target; its range defaults to a frontal 0 / 0 / 30 / 90.
Ability Tag and Ability End Tag on the execution method fill in automatically from the ability's class defaults. You only set them by hand for a custom override.
AdvancedHow an action is scored
Each action produces a score, and the highest valid one wins:
FinalScore = SelectionWeight × CtxMult × Novelty × Chain × (1 / RiskPenalty) × RuntimeMod × CustomMod × Jitter
CustomMod folds every scorer you add (distance, angle, health, speed, custom). Distance, angle, health, and speed are not automatic: each needs its own scorer in the Scoring → Scorers list.
Other factors:
  • Attribute Scorer / Attribute Gate / Stamina Gate for GAS-backed rules.
  • RuntimeMod from SetGlobalMultiplier and per-action overrides on ActionEvaluationComponent.
  • Jitter is a deterministic ±5% tie-breaker from the decision context seed.
Actions scoring zero or failing a gate are skipped. Chaining (Preferred Follow-Up Action on the source action plus Chain Bonus Multiplier on the follow-up) boosts a combo once the current action completes.

Step 5: Create the AI config

Right-click → Miscellaneous → Data AssetEnemyAIConfig. Name it DA_MyAIConfig. Set:
  • Default Action SetDA_MyEnemyActions
  • Default State Tree → leave empty to run the native combat loop (build context, move, then pick and run the best action on the brain's evaluation cadence, default 0.1 s). Set it to Content/SoulslikeEnemyCombat/Showcase/AI/StateTree/StateTree_SEC_Core to drive the AI with that StateTree instead.
  • Default Reaction Set → optional. Assign one if you built a ReactionSet. See the Reaction System.
AdvancedPer-role overrides and config resolution
EnemyAIConfig resolves per combat role. RoleActionSets, RoleReactionSets, and RoleMovementProfiles override the defaults for a specific role (for example a heavier set for SEC.Role.Attacker); anything not listed falls back to DefaultActionSet, DefaultReactionSet, and DefaultMovementProfile.
The controller reads the config on possession. When the pawn implements IEnemyAIConfigProvider (which EnemyCharacterBase does), it uses the pawn's config, so one controller class serves many enemy types. Without the interface, the controller falls back to its own config.

Step 6: Connect the pieces

Two links wire everything together:
  1. In BP_MyEnemyClass Defaults, set AI Controller Class to BP_MyEnemyController.
  2. In BP_MyEnemyClass Defaults, set AIConfig to DA_MyAIConfig.

Step 7: Register the target and play

The AI needs to know who to fight. Register the player once, on spawn:
  1. In your player pawn's BeginPlay, get the AICombatRoleSubsystem.
  2. Call RegisterCombatTarget with Self as the target actor.
BP_Dummy in Content/SoulslikeEnemyCombat/Showcase/AI/Characters/Dummy shows this exact wiring.
Drop BP_MyEnemy into a level with a built NavMesh and press Play. The enemy should:
  • close to its scored distance (100–250 cm for the melee band above)
  • attack when in range and facing the target
  • wait out the cooldown, reposition, and repeat
No NavMesh means no pathfinding. If the enemy will not move, add a NavMeshBoundsVolume and build navigation.
AdvancedWatch the decision-making
Turn on the component debug flags to watch the AI decide. On MovementEvaluatorComponent, enable bDebugDrawScoring (or SEC.Debug.Movement.DrawScoring 1) to draw scored movement directions. On ActionEvaluationComponent, enable bDebugLogDecisions (or SEC.Debug.LogActionDecisions 1) to print each action's score breakdown. Enable these in the editor only, never in a shipping build.