Build a Melee Combat System in UE5

Build a 3-hit combo system with AI enemies and hit detection in UE5.

Introduction

30 Second Summary

Think about the last time you button-mashed your way through a group of enemies in a game. Behind every combo, hit reaction, and health bar is a combat system deciding when attacks connect and what happens next.

In this project, you will build a melee combat system in Unreal Engine 5. Starting from the Third Person template, you'll create a 3-hit combo chain, add hit detection, and fight an AI enemy that attacks back.

What You'll Build

A playable third-person melee combat arena with combo attacks, sphere trace hit detection, a Blueprint Interface damage system, and an AI enemy that chases and fights back.

By the end of this project, you'll have:

  • A 3-hit combo system driven by Animation Montages and timed input windows that chain or reset based on your timing.
  • Sphere trace hit detection that fires during specific animation frames, with a Blueprint Interface keeping the damage system decoupled from any specific actor.
  • An AI enemy with a UMG health bar that chases you, attacks within range, and reacts to being hit.
  • Secret Mission: A dodge-roll with invincibility frames so you can roll through enemy attacks untouched.

Are there any prerequisites?

This project assumes you're familiar with the basics of Unreal Engine 5: navigating the editor, creating Blueprints, and working with variables and events. You'll also need UE 5.7 installed and a free Mixamo account for downloading combat animations.

Before We Start

Before We Start...

Set Up Your Project and Download Combat Animations

Every action game starts with a character that can throw punches, swing swords, or land kicks. Before you can build any of that, you need three things: a project with a movable character, attack animations to play, and an input trigger to start the combat.

In this step, you'll create an Unreal Engine Third Person project, download melee animations from Mixamo, and set up the mouse click that will drive your combo system.

In this step, get ready to:
  • Create a Third Person Blueprint project in UE 5.7.
  • Download and import 3 attack animations from Mixamo.
  • Set up an Enhanced Input Action for attacking.
Create a new Third Person Blueprint project

The Third Person template gives you a working character with movement, a camera, and input already configured. This lets you jump straight into building combat mechanics.

  • Press Cmd+Space to open Spotlight.
  • Type Epic Games Launcher and press Enter to open it.
  • In the Epic Games Launcher, click Unreal Engine in the left sidebar.
  • Click Launch next to Unreal Engine 5.7.

The Unreal Project Browser opens. This is where you choose a template for your new project.

  • Select the Games category on the left.
  • Click the Third Person template.
  • In the Variants dropdown, select None for the standard template.
  • Make sure Blueprint is selected as the project type (not C++).
  • Set the project name to MeleeCombat.
  • Click Create.

What is a Blueprint project?

Blueprint is Unreal Engine's visual scripting system. You build game logic by connecting nodes in a graph instead of writing code. The Third Person template comes pre-configured with character movement, a camera, and Enhanced Input already wired up.

The editor takes a moment to load. Once it opens, you should see a default level with platforms and stairs.

  • Click Play in the main toolbar at the top of the editor.
  • Use WASD to move, Space to jump, and move your mouse to control the camera.
  • Press Esc to stop the play session.

You should see the default Quinn mannequin running around the level. This character will become your melee fighter.

Character not moving?

Click inside the viewport after pressing Play. The game window needs focus to receive input from your keyboard and mouse.

help me debug why my character won't move after pressing Play

Download and import combat animations from Mixamo

The Third Person template does not include attack animations. You'll download three melee attacks from Mixamo, a free web-based tool that provides character animations in FBX format.

  • Open your browser and go to mixamo.com.
  • Sign in with your Adobe account.
  • In the Search bar, type Cross Punch and press Enter.
  • Click an animation you like from the results to preview it.
  • Click Download.
  • In the download settings dialog, set Format to FBX Binary.
  • Set Skin to Without Skin.
  • Set Frames per Second to 30.
  • Click Download to save the file to your Mac.
  • Repeat the same process for Hook Punch and Roundhouse Kick, using the same download settings each time.

Why Without Skin?

The Without Skin option downloads only the animation data and skeleton, not the character mesh. You'll apply these animations to the UE5 mannequin that already exists in your project.

Now you'll import these three FBX files into your Unreal Engine project.

  • Switch back to the Unreal Editor.
  • In the Content Browser at the bottom of the screen, navigate to the Content folder.
  • Right-click in an empty area and select New Folder.
  • Name the folder Animations.
  • Double-click the Animations folder to open it.
  • Drag all 3 downloaded FBX files from Finder into the Content Browser inside this folder.

The FBX Import Options dialog appears for each file. This is where you tell Unreal which skeleton to use for the animation.

  • In the Skeleton dropdown, click the browse button and select SK_Mannequin (the UE5 mannequin skeleton).
  • Make sure Import Animations is checked.
  • Click Import (or Import All if importing all files at once).
  • Repeat for each FBX file if importing one at a time.

Why assign SK_Mannequin?

Every animation in Unreal Engine is bound to a skeleton asset. By assigning SK_Mannequin, you tell the engine to play these Mixamo animations on your Third Person character's skeleton. Without this assignment, Unreal creates a new orphaned skeleton that your character cannot use.

With the animations imported, you'll now create Animation Montages from each one. Montages wrap animation sequences and add playback controls that you can trigger from Blueprint code.

  • In the Content Browser, right-click your first imported animation sequence (the Cross Punch animation).
  • Select Create > Animation Montage.
  • Rename the new montage to AM_Attack1.
  • Repeat for the Hook Punch animation, naming the montage AM_Attack2.
  • Repeat for the Roundhouse Kick animation, naming the montage AM_Attack3.

What is an Animation Montage?

An Animation Montage is a container that wraps an animation sequence and lets you trigger it from Blueprint code. Montages also let you define timing windows for events like combo inputs and hit detection.

You'll use these montages throughout this project to control exactly when attacks play and how they chain together.

You should see 6 assets in your Animations folder: 3 animation sequences and 3 montages.

Animations look wrong or skeleton errors?

If the animation preview shows the character in a T-pose or you see a skeleton mismatch error, you may have imported without selecting SK_Mannequin as the skeleton. Delete the imported assets, then re-import and make sure to select SK_Mannequin in the Skeleton dropdown of the FBX Import Options dialog.

help me fix skeleton mismatch errors when importing Mixamo animations

Create the attack input action

Your project uses the Enhanced Input system, which maps keyboard and mouse inputs to named Input Actions. You'll create a new action for attacking and map it to the left mouse button.

  • In the Content Browser, navigate to Content/ThirdPerson/Input/Actions.
  • Right-click in an empty area and select Input > Input Action.
  • Name the new asset IA_Attack.
  • Double-click IA_Attack to open it.
  • Confirm that the Value Type is set to Digital (Bool). This is the default for a new Input Action.
  • Save and close the asset.

Now add IA_Attack to the existing Input Mapping Context so it responds to the left mouse button.

  • In the Content Browser, go up one level to Content/ThirdPerson/Input.
  • Double-click IMC_Default to open it.
  • Click the + button next to Mappings to add a new mapping.
  • In the new mapping row, set the Input Action to IA_Attack.
  • Click the + button next to the mapping to add a key binding.
  • Set the key to Left Mouse Button.
  • Save and close IMC_Default.

What is an Input Mapping Context?

An Input Mapping Context (IMC) is a collection of Input Actions mapped to specific keys or buttons. The Third Person template's IMC_Default already contains mappings for movement, looking, and jumping. By adding IA_Attack here, your attack input gets processed alongside the existing controls.

Finally, wire up a test output so you can confirm the input works.

  • In the Content Browser, navigate to Content/ThirdPerson/Blueprints.
  • Double-click BP_ThirdPersonCharacter to open it in the Blueprint Editor.
  • Click the Event Graph tab.
  • Right-click in an empty area of the graph and search for IA_Attack.
  • Select EnhancedInputAction IA_Attack from the results to place the event node.
  • From the Started execution pin (the top white arrow), drag out a wire and search for Print String.
  • In the In String field of the Print String node, type Attack Input Received.
  • Click Compile in the toolbar, then click Save.

What does this do?

The EnhancedInputAction IA_Attack node fires whenever the player clicks the left mouse button. The Started pin triggers on the initial button press.

The Print String node outputs text to the screen so you can confirm the input is working. In the next step, you'll replace this test output with actual combo attack logic.

  • Click Play to test your project.
  • Click the left mouse button.

You should see Attack Input Received printed in the top-left corner of the viewport each time you click.

Don't see the print message?

Make sure you clicked Compile in the Blueprint Editor before pressing Play. Uncompiled changes will not take effect.

Also check that IA_Attack is added to IMC_Default with Left Mouse Button as the key binding. If the mapping is missing, the input event will never fire.

help me debug why my IA_Attack input is not printing to screen

Your project is armed with three attack animations and a working attack input. Next up, you'll wire these animations into a full 3-hit combo system that chains based on input timing.

Build the Combo Attack System

Your project is set up with three attack montages and an input action that responds to mouse clicks. Now it's time to turn those individual animations into a proper combo chain.

A combo system needs to know which attack comes next, when the player is allowed to chain another hit, and when to reset back to the first attack. You will wire all of this up using variables, Animation Montages, and Anim Notify States that define precise timing windows.

In this step, get ready to:
  • Create combo system variables and wire up the attack logic in your character Blueprint.
  • Add combo timing windows to each montage using Montage Notify Windows.
  • Connect the Animation Blueprint so montages play correctly over locomotion.
Create the combo variables and attack logic

The combo system tracks three things: which attack comes next, whether the player is allowed to chain, and the list of montages to cycle through. You will add these as variables in BP_ThirdPersonCharacter and then replace the test Print String with real attack logic.

  • Open BP_ThirdPersonCharacter in the Blueprint Editor by double-clicking it in Content/ThirdPerson/Blueprints.
  • In the My Blueprint panel on the left, click the + button next to Variables to create a new variable.
  • Name the variable AttackIndex.
  • In the Details panel on the right, set Variable Type to Integer.
  • Check Instance Editable if you want to tweak it at runtime, but leave the Default Value at 0.
  • Click Compile in the toolbar so the default value is saved.

What is AttackIndex?

AttackIndex tracks which attack in the combo chain plays next. When it is 0, the first attack plays. After each attack, the index increments and wraps back to 0 after the third attack.

  • Create a second variable called bCanCombo.
  • Set its type to Boolean and leave the default value as false.
  • Create a third variable called AttackMontages.
  • Set its type to Anim Montage, then click the grid icon next to the type to change it to an Array.
  • Click Compile again so the array default values become editable.
  • In the Details panel under Default Value, click the + button three times to add three array elements.
  • Set element 0 to AM_Attack1, element 1 to AM_Attack2, and element 2 to AM_Attack3.

Why use an array for montages?

Storing the montages in an array lets you index into them with AttackIndex. Instead of writing separate logic for each attack, a single Play Anim Montage call plays whichever montage the index points to.

Now you will replace the test Print String on the IA_Attack event with real combo logic.

  • In the Event Graph, find the IA_Attack Started event you created in Step 1.
  • Delete the Print String node connected to it.
  • Drag off the Started execution pin and add a Branch node.
  • For the Condition pin, create an OR Boolean node.
  • Connect the first input of the OR node to a NOT node, and feed that NOT node with an Is Playing Montage check on the character's Mesh.
  • Connect the second input of the OR node to a Get bCanCombo variable.
  • From the True pin of the Branch, add a Play Anim Montage node.
  • For the Montage to Play input, drag in your AttackMontages variable, then use a Get (a copy) node with AttackIndex as the index.
  • After the Play Anim Montage node, add a Set AttackIndex node.
  • Feed it the result of (AttackIndex + 1) % 3 using an Integer + Integer node (add 1) followed by a % (Modulo) node (modulo 3).
  • After setting AttackIndex, add a Set bCanCombo node and set it to false.

What does this logic do?

The Branch checks two conditions: either no montage is currently playing (this is the first attack), or bCanCombo is true (the player clicked during the combo window).

When the condition passes, the Blueprint plays the montage at the current AttackIndex, advances the index by one (wrapping back to 0 after index 2 thanks to the modulo), and immediately sets bCanCombo to false so the player cannot chain again until the combo window opens.

  • Click Compile and Save the Blueprint.

Compile errors?

Make sure the AttackMontages array has exactly 3 elements and each one is set to a valid montage asset. A None entry causes an index-out-of-range error at runtime.

Double-check that the modulo (%) node's second input is set to 3, not 2. Help me fix a compile error in my combo attack Blueprint.

Add combo windows to each attack montage

Right now, every click plays the next attack with no timing constraint. You need to define when the player is allowed to chain into the next hit. A Montage Notify Window creates a timed window inside the montage that fires begin and end callbacks.

  • In the Content Browser, navigate to Content/Animations and double-click AM_Attack1 to open the Montage Editor.
  • At the bottom of the editor, find the Notifies track in the timeline.
  • Right-click on the Notifies track and select Add Notify State then Montage Notify Window.
  • A colored bar appears on the timeline. Click the bar to select it, then in the Details panel, set the Notify Name to ComboWindow.
  • Drag the start handle of the bar to position it just after the hit impact frame (roughly where the punch connects).
  • Drag the end handle to position it before the recovery phase (where the character returns to idle).
  • Save AM_Attack1.

What is a Montage Notify Window?

A Montage Notify Window is a built-in Anim Notify State that defines a duration with begin and end callbacks. The begin callback fires when the playhead enters the window, and the end callback fires when it leaves.

By naming it ComboWindow, you can catch these callbacks in the Animation Blueprint and toggle bCanCombo on and off at exactly the right frames.

  • Repeat the same process for AM_Attack2 and AM_Attack3: add a Montage Notify Window named ComboWindow positioned after the hit impact and before recovery.
  • Save both montages.

Now you need to handle these notify events in the Animation Blueprint so they toggle bCanCombo on the character.

  • Open the Animation Blueprint used by your character. If you are using the default Quinn mannequin, look for the ABP asset in Content/Characters/Mannequins/Animations.
  • Switch to the Event Graph tab.
  • Right-click in the graph and search for PlayMontageNotifyBegin. Add the PlayMontageNotifyBegin event node.
  • Drag off the Notify Name output pin and add an Equal (Name) comparison node.
  • Set the comparison value to ComboWindow.
  • Connect the result to a Branch node.
  • From the True pin, use Try Get Pawn Owner to get a reference to the character, cast it to BP_ThirdPersonCharacter, and call Set bCanCombo to true.
  • Now add a second event. Right-click in the graph and search for PlayMontageNotifyEnd. Add the PlayMontageNotifyEnd event node.
  • Use the same pattern: compare Notify Name to ComboWindow, branch on the result.
  • From the True pin, get the pawn owner, cast to BP_ThirdPersonCharacter, and call Set bCanCombo to false.
  • Immediately after, add a Set AttackIndex node and set it to 0.

Why reset AttackIndex when the window ends?

If the combo window closes and the player did not click, it means they missed the timing. Resetting AttackIndex to 0 ensures the next attack always starts from the beginning of the combo chain.

  • Click Compile and Save the Animation Blueprint.

Notify events not showing up in the search?

Make sure you are searching in the Event Graph of the Animation Blueprint, not the AnimGraph. The PlayMontageNotifyBegin and PlayMontageNotifyEnd events only appear in the Event Graph.

Also confirm you are editing the correct Animation Blueprint. Open BP_ThirdPersonCharacter, select the Mesh (CharacterMesh) component, and check the Anim Class property in the Details panel to find the ABP name.

Help me find the PlayMontageNotifyBegin event in my Animation Blueprint.

Add the DefaultSlot and test the combo

Animation Montages require a Slot node in the AnimGraph to play. Without it, the Play Anim Montage call succeeds but nothing appears on screen because the AnimGraph has no entry point for montage playback.

  • Return to the Animation Blueprint and switch to the AnimGraph tab.
  • Look at the existing graph. You should see a locomotion state machine or blend space connected to the Output Pose node.
  • Right-click in an empty area of the AnimGraph and search for Slot 'DefaultSlot'.
  • Add the Slot 'DefaultSlot' node to the graph.
  • Disconnect the wire going from the locomotion output into Output Pose.
  • Connect the locomotion output into the Source pin of the DefaultSlot node.
  • Connect the DefaultSlot node's output into the Output Pose node.
  • Click Compile and Save.

What does the DefaultSlot node do?

The DefaultSlot node is the entry point where montages inject their animation into the graph. When no montage is playing, the slot passes the locomotion pose through unchanged. When a montage plays, it overrides the pose for the duration of the montage.

Time to test the combo system.

  • Click Play in the Unreal Editor toolbar to start a Play In Editor session.
  • Left-click once. Your character should play the first attack animation (Attack1).
  • While Attack1 is playing, left-click again during the combo window (after the hit impact but before the recovery). Attack2 should play.
  • Click a third time during Attack2's combo window. Attack3 should play.
  • Now wait until an attack animation finishes completely without clicking. The next click should restart from Attack1.

You should see a smooth 3-hit combo chain when you click within the timing windows, and a reset to Attack1 if you miss the window.

Attacks not playing at all?

If no animation plays when you click, the most common cause is a missing DefaultSlot node in the AnimGraph. Open the Animation Blueprint's AnimGraph and confirm the Slot node is wired between the locomotion output and Output Pose.

If the first attack plays but the combo never chains, check that your Montage Notify Windows are named exactly ComboWindow (case-sensitive) in all three montages. Then verify the PlayMontageNotifyBegin event in the Animation Blueprint compares against the same name.

If the combo always plays all three attacks regardless of timing, check that Set bCanCombo is set to false both in the character Blueprint (after playing the montage) and in the PlayMontageNotifyEnd event.

Help me debug why my combo system is not working.

Your character can now throw a 3-hit combo chain with proper timing windows. Next up, you will add hit detection so those punches actually connect with targets and deal damage.

Add Hit Detection and the Damage Interface

Your combo system is working. You can chain three attacks with precise timing windows driven by Animation Montages and Anim Notify States.

Right now, those attacks are just animations. They do not actually hit anything in the world. In this step, you will build a damage system using a Blueprint Interface and wire up sphere trace hit detection that fires during specific animation frames.

In this step, get ready to:
  • Create a Blueprint Interface for damage communication.
  • Add hit detection AnimNotifies to your attack montages.
  • Test damage delivery with a target cube in the level.
Create the Blueprint Interface for damage

Your player character needs a way to tell other actors "you just got hit." A Blueprint Interface defines a shared function signature that any actor can implement. The attacker does not need to know what it is hitting. It just calls the interface function.

  • In the Content Browser, right-click in an empty space.
  • Select Blueprints, then Blueprint Interface.
  • Name it BPI_Damageable.
  • Double-click BPI_Damageable to open it.
  • In the My Blueprint panel on the left, you should see a default function already created. Rename it to ReceiveDamage.
  • With ReceiveDamage selected, look at the Details panel on the right.
  • Under Inputs, click the + button to add a new input parameter.
  • Set the type to Float and name it DamageAmount.
  • Click Compile and Save.

What is a Blueprint Interface?

A Blueprint Interface defines function signatures without any implementation. Any Blueprint that adds the interface is guaranteed to have those functions, but each one decides what the function actually does.

For your combat system, this means your player character can call ReceiveDamage on anything it hits without knowing whether it is an enemy, a destructible barrel, or a training dummy. Each target handles the damage its own way.

Why an interface instead of calling damage directly?

Without an interface, your attack code would need a direct reference to every type of actor it can damage. That means casting to specific classes and updating the attacker every time you add a new target type.

With BPI_Damageable, the attacker stays completely decoupled. You can add new damageable actors without touching the player Blueprint at all.

  • Confirm BPI_Damageable appears in your Content Browser with the ReceiveDamage function listed inside it.

Don't see the function input?

Make sure you have ReceiveDamage selected in the My Blueprint panel before looking at the Details panel. The Inputs section only appears when a function is selected.

Help me set up my BPI_Damageable interface.

Add hit detection notifies to your attack montages

Your combo system already uses a ComboWindow Notify State to control input timing. Now you will add two simple AnimNotifies to each montage that toggle hit detection on and off during the exact frames where the attack connects.

  • In the Content Browser, open AM_Attack1 under Content/Animations.
  • In the Notifies track, right-click at the frame where the punch or kick starts making contact.
  • Select Add Notify > Montage Notify.
  • Name this notify EnableHitDetection.
  • Right-click again at the frame where the impact ends (a few frames later).
  • Select Add Notify > Montage Notify.
  • Name this notify DisableHitDetection.
  • Click Save.
  • Repeat these same steps for AM_Attack2 and AM_Attack3. Each montage needs both EnableHitDetection and DisableHitDetection notifies placed at the appropriate impact frames.

Why simple notifies instead of a Notify State?

Your ComboWindow uses a Notify State because it defines a duration with automatic begin and end callbacks. Hit detection uses two separate single-frame notifies because you want precise control over exactly which frame starts and stops the sphere trace. This is the standard approach used in production combat systems.

Finding the right frames

Scrub through the montage timeline to find where the character's fist or foot is moving forward. Place EnableHitDetection just as the limb begins its strike arc. Place DisableHitDetection right after the limb passes through where a target would be. Hold Shift while dragging a notify keyframe to preview the animation at that time.

Now you need to handle these notifies in the character Blueprint and perform the actual hit detection.

  • Open BP_ThirdPersonCharacter.
  • In the My Blueprint panel, add a new variable called HitActorsThisSwing. Set its type to an Array of Actor.
  • Click Compile.

What is HitActorsThisSwing for?

Without this array, a single attack swing could hit the same enemy multiple times as the sphere trace fires across several frames. By storing every actor that has already been hit during the current swing, you can skip duplicates and ensure each target only takes damage once per attack.

  • In the Event Graph of BP_ThirdPersonCharacter, add a PlayMontageNotifyBegin event if you do not already have one from the combo setup.
  • From the notify name output, add a Switch on Name node (or use a Branch comparing against the string EnableHitDetection).
  • On the EnableHitDetection branch, add a Sphere Trace By Channel node (search for it by right-clicking in the graph).
  • Set the Start pin to Get Actor Location.
  • Set the End pin to Get Actor Location + (Get Actor Forward Vector * 150).
  • Set Radius to 50.
  • Set Trace Channel to Pawn.
  • From the Out Hit pin, add a Break Hit Result node.
  • Drag off Hit Actor and check if it is already in the HitActorsThisSwing array using a Contains node. If it is, skip this hit.
  • If it is not in the array, add the Hit Actor to HitActorsThisSwing using an Add node.
  • Add a Does Implement Interface node. Set the Interface pin to BPI_Damageable.
  • If the check returns true, call ReceiveDamage (Message) on the Hit Actor with DamageAmount set to 20.

What does this sphere trace do?

The Sphere Trace By Channel node sweeps an invisible sphere from the character's position forward along the direction they are facing. It returns the first actor it collides with that responds to the Pawn trace channel.

A forward distance of 150 units and a radius of 50 units creates a detection zone roughly the length of an arm swing. You can tune these values later to match your animations.

Why use Does Implement Interface?

Not every actor the sphere trace hits will be damageable. The trace might hit a wall, a decoration, or another player. The Does Implement Interface node checks whether the hit actor has BPI_Damageable before calling ReceiveDamage. This prevents runtime errors on actors that cannot take damage.

  • Click Compile and Save BP_ThirdPersonCharacter.

You will not see any change yet. The next substep adds the cleanup logic and a test target to confirm everything works.

Sphere Trace By Channel not appearing in search?

Make sure Context Sensitive is unchecked in the top-right corner of the right-click search menu. With it checked, the node may be filtered out depending on which pin you dragged from.

Help me find the Sphere Trace By Channel node.

Clear hit tracking and test with a target cube

The HitActorsThisSwing array needs to be cleared at the end of each attack so the next swing can hit the same targets again.

  • In the Event Graph of BP_ThirdPersonCharacter, find your PlayMontageNotifyBegin event (or add a second case to your Switch on Name node).
  • On the DisableHitDetection branch, add a Clear node targeting the HitActorsThisSwing array.
  • Click Compile and Save.

Why clear at DisableHitDetection?

Clearing the array when hit detection turns off means the slate is clean before the next attack begins. If the player chains into Attack 2, the sphere trace can hit the same enemy again because HitActorsThisSwing was reset between swings.

Now create a test target to verify the whole system works.

  • In the Content Browser, right-click and select Blueprint Class > Actor.
  • Name it BP_TestCube and open it.
  • In the Components panel, click Add and add a Cube (Static Mesh component). This gives the actor a visible shape in the level.
  • Click Class Settings in the toolbar.
  • In the Details panel, scroll to Interfaces and click Add.
  • Select BPI_Damageable from the list.
  • Click Compile.
  • In the My Blueprint panel, under Interfaces, you should see ReceiveDamage. Double-click it to open the interface function graph.
  • Add a Print String node. Connect the DamageAmount input to the string pin so it prints the damage value.
  • Click Compile and Save.
  • Drag BP_TestCube from the Content Browser into your level. Place it near the player start so you can reach it quickly.
  • Press Play and walk up to the test cube.
  • Click the left mouse button to attack. You should see the damage value (20) printed on screen when your attack connects.
  • Try a full 3-hit combo. Each attack should print 20 exactly once per swing, with no double-hits.

Not seeing the damage print?

Check that your EnableHitDetection and DisableHitDetection notify names in the montages match the exact strings you are checking in the PlayMontageNotifyBegin event. Notify names are case-sensitive.

Also verify that BP_TestCube has BPI_Damageable listed under Interfaces in its Class Settings. If the interface is missing, the Does Implement Interface check returns false and the damage call is skipped.

Help me debug my hit detection system.

✔️ I see damage printing on screen

Your hit detection and damage interface are working. Each swing prints 20 once with no double-hits. Make sure to save all assets before moving on.

ⓧ I see double-hits or no output

  • Double-hits: make sure the Contains check on HitActorsThisSwing is wired correctly. The Branch should skip the damage call when Contains returns true.
  • No output: set the Draw Debug Type on the Sphere Trace By Channel node to For Duration. This draws the trace in-game so you can see if it reaches the cube.
  • If the trace is visible but does not hit the cube, make sure the cube has collision enabled. Select the Static Mesh component in BP_TestCube and verify that Collision Presets is not set to No Collision.

Help me fix my sphere trace hit detection.

Your attacks now connect. Sphere traces fire during the correct animation frames, and any actor implementing BPI_Damageable receives damage calls without double-hits. Next up, you will create a real enemy with a health bar that reacts to these hits.

Create the Enemy with Health and Hit Reactions

Your combo system can detect hits and deliver damage through the BPI_Damageable interface. The only thing missing is a real target to fight.

In this step, you'll build an enemy character with a health pool, a floating health bar, and a visible reaction when it takes a hit. By the end, you'll swing at a real opponent and watch its health drain to zero.

In this step, get ready to:
  • Create a BP_Enemy character that receives damage and is destroyed at zero health.
  • Build a floating health bar using a UMG widget.
  • Add a hit reaction and test the full damage loop in your level.
Create BP_Enemy and implement the damage interface

Every actor that can take damage needs to implement the Blueprint Interface you created earlier. Your enemy is no exception.

  • In the Content Browser, right-click in an empty area.
  • Select Blueprint Class.
  • Choose Character as the parent class.
  • Name the new Blueprint BP_Enemy.
  • Double-click BP_Enemy to open it.

Next, give the enemy a visible body by assigning the mannequin mesh.

  • In the Components panel, select the Mesh component.
  • In the Details panel, find Skeletal Mesh Asset and set it to SKM_Manny (or SKM_Quinn).
  • Still in the Details panel, find Anim Class and set it to ABP_Manny. This gives the enemy idle and locomotion animations.

Why reuse the player's Animation Blueprint?

The ABP_Manny Animation Blueprint already has a DefaultSlot node that allows Animation Montages to play. The combo-specific notify logic inside it only affects the player character because it reads variables that BP_Enemy does not have. Those nodes simply do nothing on the enemy.

Reusing it here means the enemy gets a working idle pose and montage support without creating a brand-new Animation Blueprint.

Now add the health variables and wire up the damage interface.

  • Open the Class Settings tab at the top of the Blueprint editor.
  • In the Details panel, find Interfaces and click Add.
  • Search for and select BPI_Damageable.
  • Click Compile in the toolbar. You should see ReceiveDamage appear under the Interfaces section in the My Blueprint panel.
  • In the My Blueprint panel, click the + next to Variables to create a new variable.
  • Name it Health, set its type to Float, and set its default value to 100.
  • Create a second Float variable named MaxHealth with a default value of 100.

Why two health variables?

The Health variable tracks the enemy's current health as it takes damage. MaxHealth stores the starting maximum so you can calculate a percentage for the health bar. Without MaxHealth, the health bar would not know what 100% looks like.

Now implement the ReceiveDamage function to subtract health and destroy the enemy at zero.

  • In the My Blueprint panel under Interfaces, double-click ReceiveDamage to open its graph.
  • Drag off the DamageAmount input pin and subtract it from Health using a Float - Float node.
  • Use a Set Health node to store the result back into Health.
  • Add a Branch node after the Set Health node.
  • For the condition, use a <= (less than or equal) node comparing Health to 0.
  • On the True branch, add a Destroy Actor node.
  • Click Compile and Save.

What does this logic do?

When any actor calls ReceiveDamage on this enemy, the function subtracts the incoming DamageAmount from the current Health. If Health drops to zero or below, the enemy is removed from the level entirely.

The player's sphere trace from the previous step already checks for BPI_Damageable and calls this function. So as soon as you place the enemy in the level, hits will start registering.

Don't see ReceiveDamage under Interfaces?

Make sure you clicked Compile after adding the interface. The function only appears in the My Blueprint panel after a successful compile. Also double-check that you selected BPI_Damageable (not a different interface) from the interface list.

Help me troubleshoot my BPI_Damageable interface on BP_Enemy.

Build a floating health bar with UMG

The enemy can take damage now, but there is no visual indicator of how much health it has left. A floating health bar solves this by displaying a Progress Bar above the enemy's head.

  • In the Content Browser, right-click in an empty area.
  • Select User Interface, then Widget Blueprint.
  • Name it WBP_EnemyHealth.
  • Double-click WBP_EnemyHealth to open it in the Widget Blueprint Editor.
  • From the Palette panel on the left, drag a Progress Bar widget into the Visual Designer canvas.
  • In the Details panel, check the Is Variable checkbox so you can access this widget from Blueprint code.
  • Set the Percent property to 1.0 so the bar starts full.
  • Optionally change the Fill Color and Opacity to a red or green colour so the bar is easy to see.
  • Click Compile and Save the widget.

What is a Progress Bar?

A Progress Bar is a UMG widget that fills or empties based on a Percent value between 0.0 (empty) and 1.0 (full). You will update this percent from BP_Enemy every time the enemy takes damage.

Now attach this widget to the enemy so it floats above its head.

  • Switch back to BP_Enemy in the editor.
  • In the Components panel, click Add and search for Widget.
  • Select Widget to add a Widget Component to the Blueprint.
  • In the Details panel for the Widget Component, set Widget Class to WBP_EnemyHealth.
  • Set Draw Size to 150 x 20.
  • Set Space to Screen. This makes the health bar always face the camera and stay a consistent size on screen.
  • Use the transform gizmo in the viewport to move the Widget Component above the character's head.

Screen space vs World space

Setting Space to Screen renders the widget directly on your screen, completely outside of the 3D world. It always faces the camera and never gets hidden behind geometry. This is the standard approach for floating health bars in third-person games.

The Widget Component displays the health bar, but it does not know when to update. Create a function in BP_Enemy that refreshes the bar.

  • In BP_Enemy's My Blueprint panel, click the + next to Functions to create a new function.
  • Name it UpdateHealthBar.
  • Inside the function graph, drag in the Widget component reference.
  • Call Get User Widget Object on it.
  • Cast the result to WBP_EnemyHealth.
  • From the cast output, drag out the ProgressBar reference (the variable you enabled earlier).
  • Call Set Percent on the Progress Bar.
  • For the In Percent value, divide Health by MaxHealth using a Float / Float node.

Now connect this function to the damage logic so it runs every time the enemy is hit.

  • Return to the ReceiveDamage function graph.
  • After the Set Health node (before the Branch node), add a call to UpdateHealthBar.
  • Click Compile and Save.

What does UpdateHealthBar do?

Every time ReceiveDamage runs, it subtracts health and then calls UpdateHealthBar. This function reads the current Health and MaxHealth values, divides them to get a 0.0 to 1.0 fraction, and pushes that fraction into the Progress Bar's Percent property. The bar visually shrinks in real time.

Health bar not appearing in the viewport?

Make sure the Widget Class on your Widget Component is set to WBP_EnemyHealth. If it shows None, the component has nothing to display.

Also check that the Widget Component is positioned above the capsule in the viewport. If it overlaps the mesh, the bar may be hidden behind the character.

Help me fix my enemy health bar widget.

Add a hit reaction and test combat

A shrinking health bar is good feedback, but the enemy should also react physically when it takes a hit. A small knockback impulse gives the player satisfying confirmation that their attack connected.

  • In BP_Enemy, return to the ReceiveDamage function graph.
  • On the False branch of the Health check (meaning the enemy is still alive), add a Launch Character node.
  • Set the Launch Velocity to (0, 0, 300). This pushes the enemy upward briefly when hit.
  • Click Compile and Save.

What does Launch Character do?

The Launch Character node applies a velocity impulse to the character, causing a brief physical stagger. An upward Z value of 300 lifts the enemy slightly off the ground. This serves as a clear visual hit reaction for your prototype.

In a more polished project, you would replace this with a flinch Animation Montage downloaded from Mixamo. For now, the knockback gets the job done.

Upgrade to a flinch animation later

Download a "Hit Reaction" animation from mixamo.com, import it into your project, and create an Animation Montage from it. Then replace the Launch Character node with a Play Anim Montage node pointing at your flinch montage for a more polished result.

Time to drop the enemy into your level and test the full damage loop.

  • Switch to the main Level Editor tab.
  • Drag BP_Enemy from the Content Browser into the level viewport.
  • Place it a short distance from the player start so you can reach it easily.
  • Click Play in the toolbar to start Play In Editor (PIE).

Walk up to the enemy and left-click to attack. You should see three things happen with each hit:

  • The health bar above the enemy's head shrinks.
  • The enemy hops slightly from the knockback impulse.
  • After 5 hits (5 x 20 = 100 damage), the enemy disappears from the level.

Enemy not taking damage?

Open BP_Enemy's Class Settings and confirm BPI_Damageable is listed under Interfaces. The player's sphere trace only calls ReceiveDamage on actors that implement this interface.

If the health bar is not updating, check that UpdateHealthBar is connected in the ReceiveDamage graph after Set Health and before the Branch node.

Help me debug why my enemy is not taking damage.

Your enemy takes damage, shows it on a health bar, and dies when health runs out. Next, you will give it a brain so it chases you down and fights back.

Add Basic Enemy AI (Chase and Attack)

Your enemy takes damage, flinches, and collapses when its health runs out. There is just one problem.

It stands perfectly still while you punch it. In this step, you will give the enemy a brain by building an AI Controller that chases the player and attacks when it gets close. You will also make the player character damageable so combat goes both ways.

In this step, get ready to:
  • Create an AI Controller and set up a navigation mesh so the enemy can find a path to you.
  • Build a chase-and-attack behavior loop that makes the enemy pursue and strike.
  • Make the player character damageable with its own health bar, completing the two-way combat loop.
Create the AI controller and set up navigation

Before the enemy can chase the player, it needs two things: an AI Controller to make movement decisions, and a NavMesh to tell it which areas of the level are walkable.

  • In the Content Browser, right-click in an empty area.
  • Select Blueprint Class.
  • In the class picker, expand All Classes and search for AIController.
  • Select AIController as the parent class and click Select.
  • Name the new Blueprint BP_EnemyAI.

Now assign this AI Controller to your enemy so it takes control when the game starts.

  • Open BP_Enemy.
  • In the Details panel, scroll down to the Pawn section.
  • Set AI Controller Class to BP_EnemyAI.
  • Compile and save BP_Enemy.

What is an AI Controller?

In Unreal Engine, every Pawn can be "possessed" by a Controller. The player character is possessed by a Player Controller (which reads your keyboard and mouse input). An AI Controller does the same job, but instead of reading human input, it runs logic you define to decide what the Pawn should do.

Next, add a NavMesh so the AI knows where it can walk.

  • Switch to the Level Editor (the main viewport where your arena is).
  • Open the Place Actors panel. If it is not visible, click Window in the top menu bar, then select Place Actors.
  • In the Place Actors panel, search for NavMeshBoundsVolume under the Volumes category.
  • Drag a NavMeshBoundsVolume into your level.
  • Use the Scale tool (press R) to resize the volume so it covers your entire arena floor. Make sure the volume extends above and below the floor surface.
  • Press P on your keyboard to toggle the NavMesh visualization.

You should see a green overlay on your arena floor. This green area represents the walkable surface the AI can navigate on.

No green overlay when you press P?

Make sure the NavMeshBoundsVolume is large enough to cover your floor. If the volume is too small or floating above the ground, the engine cannot build a valid NavMesh on the surface.

Also check that you have a solid floor mesh in your level. The NavMesh generates based on collision geometry, so it needs a surface to build on.

Why isn't the NavMesh showing up as green in my level?

Build the chase-and-attack loop

With the NavMesh in place, the enemy can pathfind across the arena. Now you will build the actual AI behavior: chase the player when far away, and attack when close.

  • Open BP_EnemyAI and switch to the Event Graph.
  • Create the following variables in the My Blueprint panel:

Now build the chase logic. The AI will check the distance to the player every tick and decide whether to chase or attack.

  • Right-click in the Event Graph and add an Event Tick node.
  • Drag off Event Tick and add a Get Player Character node to get a reference to the player.
  • Add a Get Controlled Pawn node to get the enemy character this controller possesses.
  • Add a Get Distance To node. Connect the Controlled Pawn as the target and the Player Character as the other actor.
  • Add a Branch node. For the condition, check if the distance is greater than 200 using a > (Greater Than) comparison node.

What does the distance check do?

The value 200 units is roughly arm's length for the UE5 mannequin. When the enemy is farther than 200 units, it chases. When it is within 200 units, it is close enough to swing.

Wire up the chase behavior on the True branch (distance > 200).

  • From the True pin of the Branch, add an AI MoveTo node.
  • Connect the Pawn input to your Get Controlled Pawn output.
  • Connect the Target Actor input to the Get Player Character output.

Now wire up the attack behavior on the False branch (distance <= 200).

  • From the False pin, add another Branch node. The condition should check that bIsAttacking is false AND bCooldownActive is false. Use a NOT Boolean on each and an AND Boolean to combine them.
  • From the True pin of this second Branch, add a Set bIsAttacking node and set it to true.
  • Add a Stop Movement node (call it on Self, the AI Controller) so the enemy stops walking before swinging.
  • Add a Play Anim Montage node. For the Target, connect the Get Controlled Pawn output. For the Montage to Play, select AM_Attack1 (one of the montages you imported earlier).

After the montage plays, add sphere trace hit detection so the enemy can actually damage the player.

  • After Play Anim Montage, add a Delay node set to 0.3 seconds. This roughly times the sphere trace to fire during the hit impact frame of the animation.
  • After the Delay, add a SphereTraceByChannel node. Set it up the same way you did for the player's hit detection:
  • From the hit result, use a Break Hit Result node to get the Hit Actor. Check if it implements BPI_Damageable using a Does Implement Interface node.
  • If it does, call ReceiveDamage (the interface message) on the Hit Actor with a DamageAmount of 20.
  • After the sphere trace logic, set bIsAttacking back to false.
  • Compile and save BP_EnemyAI.

Why use a Delay instead of AnimNotifies for the enemy?

For the player character, you used AnimNotifies because the combo system needed precise frame-by-frame control over hit windows. The enemy uses a single attack with simpler timing, so a short Delay after Play Anim Montage is a quick, effective alternative.

In a larger project, you would use AnimNotifies on the enemy's montage too. For this prototype, the Delay keeps the AI Controller self-contained and easy to understand.

Press Play to test. The enemy should chase you across the arena and play an attack animation when it gets close.

Enemy not chasing you?

Check that the NavMesh covers the area between the player and the enemy. Press P to verify the green overlay reaches both characters.

Make sure BP_Enemy has its AI Controller Class set to BP_EnemyAI. If it is set to None, the AI Controller will never possess the enemy.

Help me figure out why my AI enemy is not chasing the player.

Make the player damageable and add an attack cooldown

The enemy attacks, but the player cannot actually take damage yet. Right now BP_ThirdPersonCharacter does not implement the BPI_Damageable interface, so ReceiveDamage calls bounce off it.

You also need a health bar on the player's HUD so you can see the damage happening. Let's build both.

  • Open BP_ThirdPersonCharacter.
  • In the Class Settings toolbar, click Interfaces and add BPI_Damageable.
  • Create a new Float variable called Health with a default value of 100.
  • Create a Float variable called MaxHealth with a default value of 100.
  • In the My Blueprint panel, find the Interfaces section and double-click ReceiveDamage to open the interface function graph.
  • Subtract DamageAmount from Health using a Float - Float node and a Set Health node.
  • After setting Health, add a Branch checking if Health <= 0. On the True pin, call Destroy Actor (or handle game over however you like).

Why implement the same interface on the player?

This is the power of the Blueprint Interface pattern. The enemy's sphere trace calls ReceiveDamage on whatever it hits. It does not care whether it hit the player, another enemy, or a destructible barrel. Anything that implements BPI_Damageable gets damaged.

Now create a player health bar widget so you can see the damage on screen.

  • In the Content Browser, right-click and select User Interface > Widget Blueprint.
  • Select User Widget as the parent class. Name it WBP_PlayerHealth.
  • Open WBP_PlayerHealth and drag a Canvas Panel from the Palette onto the Designer.
  • Drag a Progress Bar into the Canvas Panel. Position it in the top-left corner of the screen using Anchors.
  • Set the Progress Bar's Fill Color to a green or red color so it is clearly visible.
  • Set Percent to 1.0 in the Details panel so it appears full by default.
  • Click Bind next to the Percent property and create a new binding. In the binding function, use Get Player Character, cast to BP_ThirdPersonCharacter, get Health, divide by MaxHealth, and return the result.

Display the widget on screen when the game starts.

  • Return to BP_ThirdPersonCharacter and open the Event Graph.
  • Find the Event Begin Play node (or add one if it does not exist).
  • Drag off Begin Play and add a Create Widget node. Set the Class to WBP_PlayerHealth.
  • Drag off the Return Value of Create Widget and add an Add to Viewport node.
  • Compile and save BP_ThirdPersonCharacter.

What do Create Widget and Add to Viewport do?

Create Widget instantiates your UMG Widget Blueprint in memory. Add to Viewport draws it on the player's screen. Without Add to Viewport, the widget exists but is invisible.

Finally, add an attack cooldown to the enemy so it does not attack every single tick.

  • Switch back to BP_EnemyAI.
  • At the end of the attack logic (after setting bIsAttacking back to false), add a Set bCooldownActive node and set it to true.
  • Add a Delay node set to 2.5 seconds.
  • After the Delay, add a Set bCooldownActive node and set it back to false.
  • Compile and save BP_EnemyAI.

Why add a cooldown?

Event Tick fires every frame. Without a cooldown, the enemy would attack 60+ times per second the moment it enters range. A 2.5-second cooldown makes the combat feel like a real fight with openings you can exploit between swings.

Press Play to test the full combat loop. The enemy should chase you, attack when close, and wait between attacks. You should see your player health bar decrease when the enemy hits you. Attack the enemy back and watch its health bar shrink too.

Player health bar not appearing?

Make sure the Create Widget node's Class is set to WBP_PlayerHealth. If it shows None, the widget will not be created.

Confirm that Add to Viewport is wired to the Return Value of Create Widget. If the execution path skips Add to Viewport, the widget is created but never drawn on screen.

My player health bar widget is not showing up in the game.

Two-way combat is working. The enemy hunts you down, swings with a cooldown, and you can fight back. You have a complete melee combat prototype. Up next, the Secret Mission challenges you to add a dodge-roll with invincibility frames so you can weave through enemy attacks.

Secret mission

Add a Dodge Roll with Invincibility Frames

Your combat system hits hard, but the player has no way to avoid incoming attacks. Every hit lands, no matter what. Add a dodge roll with invincibility frames so skilled players can time their dodges to phase through enemy strikes.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally with no cloud resources or paid services. There are no ongoing costs to worry about.

Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Your Unreal Engine project folder (all Blueprints, Animation Montages, and combat assets).
  • Downloaded FBX animation files from Mixamo (in your Downloads folder).

Keep everything running

No action needed. Choose this if you're still actively building on your combat system or want to revisit it later.

  • Your project folder stays on disk wherever you saved it when you created the project in Unreal Engine.
  • Your free Adobe account (used for Mixamo) stays active. There is no charge for keeping it.
  • Any downloaded FBX files remain in your Downloads folder.

Pause - I'll come back to this later

Close Unreal Engine to free up memory and system resources. Your project files stay on disk so you can reopen them anytime.

  • Close the Unreal Editor if it is still open.
  • To reopen your project later, launch Unreal Engine and select your project from the Recent Projects list.

Delete - I don't want to use this again

Remove all project files from your machine. This cannot be undone.

  • Close the Unreal Editor if it is still open.
  • Open Finder (macOS) or File Explorer (Windows) and navigate to the folder where you saved your Unreal Engine project.
  • Delete the entire project folder. On macOS, right-click the folder and select Move to Trash. On Windows, right-click and select Delete.
  • Check your Downloads folder for any leftover FBX files you downloaded from Mixamo. Delete those as well.
  • Empty your Trash (macOS) or Recycle Bin (Windows) to reclaim disk space.

That's a Wrap!

That's a Wrap!

Nice work! You just built a fully playable melee combat prototype from scratch in Unreal Engine 5.

You've learned how to:

  • Built a 3-hit melee combo system using Animation Montages and Anim Notify States that chains or resets based on input timing.
  • Implemented sphere trace hit detection tied to specific animation frames and created a Blueprint Interface (BPI_Damageable) for a decoupled damage system that any actor can implement.
  • Built an AI enemy with a UMG health bar, hit reactions, and a chase-and-attack loop that creates a two-way combat encounter.
  • Secret Mission: Added a dodge-roll with invincibility frames using root motion and notify-driven boolean gating.

Ready to quiz yourself?