skip to Main Content

Implementing a Melee Combo System in C++

In this post we’re going to implement a simple melee combo system in C++. Moreover, a basic animation blending technique will be demonstrated to cover our needs.

You will notice that we will type about 30 lines of code and the rest will be done inside the Editor using simple notifies. All the code is available on my github repo.

Before we dive into creating our system, here is my end-result (yours might differ – I will explain why later on, just bare with me).

This tutorial assumes you’re familiar with the following terms:

  1. Persona Editor
  2. Animation Sequence
  3. Blend Spaces
  4. State Machines

This doesn’t mean that you can’t create this system if you aren’t a guru on the mentioned topics. If you know what those “things” are and what they’re used for, then please, keep on reading!

Making a list of the necessary assets

Here is a list of the things we need in order to create the demonstrated system:

  1. A Skeletal Mesh (for our character)
  2. Three animations:
    1. Idle animation
    2. Run animation
    3. Attack animation

The combo moves demonstrated in the end result video are in fact one animation, which contains three hits. However, as demonstrated above, I created three combo moves based on one animation. The logic behind this is simple, you can divide an animation into parts and then tell the engine which part you want to play. Here is the full attack animation that I’ve used:

Understanding the concept of animation blending

By now you may be wondering why my attack animation and my end result are quite different. This has to do with the animation blending I’ve implemented and my desired animations. Animation blending gives you the ability to play two (or more!) animations at the same time. Based on my end-result you might think that this sucks. Well, that’s definitely not the case!

If you watch closely the second video, you will notice that the attack animation I chose moves my character’s legs as well. Since I want to play that particular animation while running I need to use blending and in my case this has some drawbacks. That’s where animators come into play. If you have a dedicated animator in your team he may be able to provide you with an animation that includes running and attacking at the same time, which will result in a more appealing and smooth effect!

Gathering the necessary assets

As mentioned above, your result might differ. This is because I’m unable to share my skeletal mesh and the animations used above, however, there is a workaround for this problem!

Head up to Mixamos’ official site and:

  1. Create a free acount
  2. Choose a character of your liking
  3. Choose the three animations of your liking and “link” them to your character

Mixamo will provide you with a download link which will contain your character with his/hers animations in no-time! Please note that all the services mentioned above are free for a limited time.

Note: Sometime ago, I experienced some problems with two models so if you can’t import the character you downloaded just choose another one.

Create a Third Person C++ Template project, import your character with his animations and let’s get started!

Understanding the logic of the system

Before we start typing our code let’s break-down the logic behind this sytem. In order to achieve the result of the video, we will use some anim notifies to inform our code that if the player taps the attack button “at the right time” he will peform a combo move (in this case a follow-up attack), based on the animation that is already playing. Of course, we will decide the desired “right time”.

So for example, if we have performed two hits and we tap the attack button at the right time we will be able to move on to the next attack etc..

Creating our Anim Instance class and explaining what is an Animation Montage

Create a C++ class based on the Anim Instance class and name it MyAnimInstance. Then, inside the header file, type in the following code:

Switch to the source file of your class and type the following implementations:

Before we move on, save your code and let’s take a step back in order explain what’s going on in the above code.

The bool variables bAcceptsSecondAttackInput and bAcceptsThirdAttackInput are the flags that we’re going to use in order to determine if the character can perform a second and a third attack respectively. These variables will be set to true using our anim notifies from the Persona Editor. If these variables are false then the character will be able to perform only the basic attack.

The UAnimMontage* is an Animation Montage reference. For this tutorial, consider an Animation Montage as an animation that we will explicitly play when something happens (in this case when our character attacks).

We will later implement the Attack function which will contain the logic for our combo moves.

Preparing our Character and setting up inputs

Go to your Project Settings and specify an Action Mapping:

actionmapping

Then, go to your character’s header file and add the following function declaration:

Then, switch to your character’s source file and include the following header file:

#include “MyAnimInstance.h”

When you are done with that, go to the SetupPlayerInputComponent function and add your action mapping like so:

Make sure to change the “PerformMeleeHit” to match your action mapping’s name.

Moreover, provide the following implementation for the Attack function:

Save and compile your code.

Extending our Anim Instance Class

Create an Animation Blueprint based on the skeleton you’ve imported and the class you created. Here’s a screenshot for my case:

anim_bp

Then, inside your Event Graph add the following logic:

animbp_eventgraph

Understanding the need of Cached Poses

Switch to your AnimGraph and add a new state machine. Then, create a new Cached Pose based on your created state machine like the following screenshot suggests:

cached_pose_creation

Before we continue on, let’s explain what a cached pose is and why we use it.

As mentioned above, we need to create a simple animation blending. This means that we want to temporary “store” the animation that is currently playing in order to play more animations of our liking simultaneously. If you try to connect your state machine to more than one nodes the Editor will disconnect any previous connections and connect your state machine to the node that you clicked on. That’s why we store our animations to a Cached Pose and then use that pose to blend our animations. But why we just can’t connect everything to our state machine and be done with it? Well, the answer is simply, no.

Everything will get crystal clear once we explain the following section. Just bare with me for now.

Creating multiple slots

When you store your cached pose, right-click somewhere and use the cached pose you’ve created above. Then, create the following node:

defaultslot

This means that from the Cached Pose we stored before (which contains the state machine) we will get the animation that is currently playing in the DefaultSlot.

But wait, what is the Default Slot? Remember that we want to play the attack animation while our character moves, this means that will we need to divide the skeleton of our character into two parts (slots). For this example we need two parts – slots:

  1. A slot that contains the running animation (DefaultSlot)
  2. A slot that contains the attack animation

When you create the DefaultSlot node, select it and in the details tab tap on the indicating icon:

slot_creation1

Then, click on the Add Slot icon like the following image suggests and create your new slot:

slot_creation2

I named my slot UpperBody. When we create our Animation Montage we will inform the Editor that we want to play that particular animation on the UpperBody slot only!

Once you have added your new slot, select the DefaultSlot node and set its slot name to UpperBody.

Implementing the Animation Blending

When you’re done adding the desired slots, create the following graph:

anim_blend

The node Layered blend per bone performs the actual animations blending. The above graph is like saying the following phrase to the Editor:

“Take the animation that is playing in the DefaultSlot and simultaneously play the animation that is currently playing on the UpperBody”

Hopefully, you will notice the multiple red arrows which point to a Bone Name. If you scroll up to the concept of Animation Blending section you will notice that we talked about dividing our skeleton into two parts. The Bone Name is essentially the part where our skeleton is divided. Please note that your Bone Name might differ based on the animation you selected.

Open up the skeleton asset of your static mesh and click on the Show menu. Then, click on the Bone Names option:

bones

Use the following screenshot in order to find the bone name that matches your case:

bone_names

I cannot stress enough how important it is to include the Bone Name into the details panel of Layered blend per bone node. If you ignore this step your UpperBody slots animations will not play but your code will work fine.

Setting up a basic blendspace

Right click somewhere in the editor and create a BlendSpace 1D. Assign the idle animation into the zero value and the run animation in the max value like the following screenshot suggests:

blendspace

To assign animations just drag and drop the animation of your liking inside the panel. The animation will “lock” on the available values.

The 1D blendspace will ensure that you will have a smooth transition between the idle and the run animation. In case you’re planning on releasing your game with gamepad support, include a walk animation in the middle in order to handle the analog inputs!

Save your blend space and switch to your Blueprint Animation instance. 

Then, implement the following logic inside your state machine:

statemachine1

statemachine2

Setting up our Character

Navigate to the ThirdPersonCharacter Blueprint and assign your own mesh and your own Anim Blueprint Generated Class just like the following screenshot:

thirdpersoncharacterbp

At this point if you play inside the Editor you will have an idle/run animation. We’re almost there!

Note: The ThirdPersonTemplate has a built-in jumping behavior. If you try to jump with the current state machine your character will most likely walk on while falling. I won’t cover the jump behavior in this tutorial, however if you want to learn on how to implement that, I’ve got you covered!

Creating an Animation Montage

Right click somewhere in your content browser and create an Animation Montage. Then, drag and drop your attack animation inside the “Montage” Section:

montage

Don’t forget to assign the UpperBody slot to your animation. Remeber that we want to attack while we move!

When you’ve added your animation and selected the UpperBody slot, create two new montage sections. The montage sections are the parts that we’re going to split our animations like I’ve described above.

Then:

  1. Rename the Default Section to FirstAttack
  2. Name the second section SecondAttack
  3. Name the third section ThirdAttack

If the editor gets messy, click the “Clear” Button on the Section category. This is the end-result you should have:

montage_sections

Then, select the SecondAttack section, and place it right at the start of your second attack. Do the same thing for the ThirdAttack section. If you feel the need to, preview your sections by clicking on the “Preview” button of the corresponding section. I know that this part might get confusing but here’s my attack sections if you need a visual reference:

Beginning of the second attack
Beginning of the second attack
Beginning of the third attack
Beginning of the third attack

Once you have completed the montage sections, create two Anim Notifies, named EnableSecondAttackInput and EnableThirdAttackInput and place them somewhere between your first and second sections respectively.  If you want to create a difficult combo in terms of timing, place the notifies at the end of their sections. Here is a screenshot of my element timing and my notifies:

notifies

The Green Button with the number 2 corresponds to the SecondAttack Montage section and the green button with the number 4 corresponds to the ThirdAttack Montage section. Our montage is ready!

Finalizing our Animation Blueprint

Assign your Attack Montage inside our Animation Blueprint. Don’t forget to click on the Edit Defaults button.

montage_assignment

Moreover, don’t forget to add the functionality for our notifies:

notifies_logic

Finalizing our attack code

Open up the source file of your Anim Instance class and replace the GLog we’ve added before with the following implementation:

Before testing, make sure that your FNames have the same content as your Montage Sections.

You have implemented a basic combo system! Congrats!

 

Avatar photo

This Post Has 7 Comments

  1. Hey Orfeasel, Not the best place for this but I have been trying to recreate your sci-fi shooter example myself. I was wondering is there a way to know what I am supposed to make into a blueprint and place into the level and what doesn’t need to be. I got most of the code complied, action bindings assigned but not to sure where to move from here.

    1. Hello,

      If my header file specifies a UCLASS / USTRUCT / UENUM macro it’s because I have converted them into Blueprints. An exception to this rule is the AI – related code. If you see a class that inherits the BTTask class then the editor auto-generates this in your Behavior Tree.

      -Orfeas

  2. Hello Orfeasel, Nice tutorial ! Your tutorial is very useful.By the way,Could you make a Fighting Game Tutorial like Street Fighter 5?

  3. Hello Orfeasel. I`ve finished this tutorial and everything works well for me except “Beating” between attack animations. How to make this transitions smooth?

  4. Hi very clear tutorial. I have one issue however it seems to only be playing the first section and if i input the attack key again it just resets the montage. Any help would be greatly appreciated, thanks.

Leave a Reply to Οrfeas Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back To Top