Top-down Movement and Attack Game Mechanics in Unity

 by • Reading Time: 14 minutes

Introduction

In this tutorial we are going to investigate the implementation of an RPG top-down movement and attack game mechanics in Unity. This type of style can be commonly seen in a lot of older RPG’s such as Alundra from PS1 era.

Table of contents:

  1. Making simple character sprite animations
  2. Importing sprites into Unity
  3. Setting up character animations
  4. Binding everything together with scripts

1. Making simple character sprite animations

I am going to use Aseprite to create a quite simple version of a game character along with his walking cycles and attack animations in all 4 directions. To keep everything straightforward, the sprites are 16 x 16, and each animation lasts no more than 4 frames. Given that it is such a small canvas we have to make sure that his head occupies most of the designated space. The reason is that the facial area carries the visual information (i.e. mimics) the player will be interested in the most. Start by drawing a circular shape in the upper part. Turn on the ‘Horizontal Symmetry’.

Symmetry Main Dropdown Options in Aseprite
Symmetry Options Panel in Navigation in Aseprite

Add rectangular shapes on the sides and filled-in rectangles in the bottom part. Colour the individual parts of the sprite and add eyes – these can be single pixels located on each side of the head. At this stage you can add more details such as hairstyle and t-shirt cleavage. To finish off and give our character more depth add some shadow to his face. The best way to do this is to pick up a darker ‘Value’ of the face colour in HSV colour space. Click on the colour label on the left, go to ‘HSV’ tab and scroll ‘V’ down a bit.

Picking up color in HSV color space in Aseprite

And here is the result broken down in stages:

Character sprite drawing broken down in stages

Walking Cycles Animations

We are now ready to prepare our walking cycles animations. Press Ctrl + N in order to add a new frame. Turn on the “Onion Skinning” tool so that we can preview the adjacent frames to the one we are currently working on.

Onion skinning option in Aseprite

Move our character up by one pixel, erase left limbs and make them smaller while the right limbs bigger. This will create the illusion of movement. You can also fiddle with the hairstyle to further communicate that the character is moving.

Character animation

Copy the first frame as our third frame. At this stage we just need one more frame – copy the second frame as fourth one and flip it horizontally.

Flip Horizontal option in Aseprite dropdown menu

We now have the front walking cycle animation!

  • Right-click on the first frame and select ‘New Tag’ option to label it.
  • Type in name, such as ‘Run_Down’ and make sure that the animation is set for frames 1 – 4 with ‘forward’ animation direction.

Here’s the final output:

Four frame of character walking animation clip

Now let’s move onto the other directions.

  • Press Ctrl + N again to create a new frame.
  • Copy the first frame and paste the content to it.
  • Fill in the face with hair colour.
  • Create new frame, move entire sprite up by one pixel, make limbs on the left smaller and more saturated, mess the hairstyle a little bit.
  • Create two last frames using the same method as before and add a tag for the animation.

This is what you should end up with:

Four frame of character walking up animation clip
  • Create a new frame and paste the contents of the first frame of the ‘Run_Down’ animation.
  • Erase half of the sprite and take a note of the position of the head, hands, and legs.
  • Make the hairline look like the head is being seen from the side.
  • Erase (cut?) the right-hand side portion of the hair leaving the hairline only on the left-hand side.
  • Add the contour for the rest of the body as if the hand and leg is visible from the side. Keep in mind the limbs height level and reference the front sprite if needed.
  • Colour the individual parts and add final details such as eyes, hairstyle, and shadows.

The idea here is to match the same look visible in the front-view sprite, but don’t worry if it’s not 100% accurate!

Character sprites seen from the side

Create new frame and once again move character up by one pixel, remove legs pixels and make them look as if they are in “contact / passing” position. One leg needs to be shifted further to the right while the other to the left. You can move the hand a back and mess up hair a little bit to create an illusion of motion. For this animation we are going to use only 2 frames. Make a new tag and name it ‘Run_Right’. Mind the frame numbers and direction. In order to create ‘left’ walking cycle, simply copy-paste the ‘right’ walking cycle frames and flip them horizontally. Congratulations! You have now finished walking cycles of your character in all 4 directions!

Character walking to the side animation sprites

Attack Animations

Let’s have a look at the attack animations. I’m going to make 2 frame animations for each direction to keep things simple. The idea here is to make our character ‘punch’ his opponents with fists. Create a new frame and copy-paste the first frame from ‘Run_Down’ sequence. In the next frame make the right hand to appear much bigger so that it is directed straight at us. Complement the pose with making the left leg and arm smaller as if they were pushed to the back. Label this sequence and copy the first frame yet again as the new frame.

Character punching sprites

To make the character punch his opponent in the up direction, fill his face with hair colour and get rid of cleavage from his t-shirt. This is the same method we used while doing the ‘Run_Up’ animation earlier. Then, make the right hand and leg bigger while the left leg and arm smaller to indicate as if the character is almost reaching out forward up.

Character punching up sprite animation

Now for punching right and left we are going to copy the first frame from the ‘Run_Right’ animation. In the next frame stretch the arm of our character and once again make his legs to appear as if his is reaching out. At this point you may also wish to select his head and move it 1 or 2 pixels to the right so indicate this movement better.

Character attacking to the side

As with the side walking cycle animation, just flip the frames horizontally to make our character punch in the opposite direction. Don’t forget to tag all animations so that your final timeline looks more or less like this:

Finished animation clips timeline in Aseprite

This concludes our character animation and we are now ready to import it into Unity engine!

2. Importing sprites into Unity

Still in Aseprite, click on File -> Export Sprite Sheet or press Ctrl + E to open a panel with exporting options. Here you can specify how you would like to export our sprite animations into a single file. All settings here are a matter of preference but I always set the ‘Sheet Type’ to ‘Packed’ and tick the ‘Merge Duplicates’ checkbox to remove duplicate sprites we may have created during the process. In the ‘Output’ tab make sure that the image is going to be saved in ‘png’ format to maintain the transparency of the sprites. Click ‘Export’ to create a tileset containing all our work as a single image file!

Exporting options in Aseprite

Drag the exported image to ‘Project Assets’ panel in Unity window. Click on the newly created asset to open the ‘Inspector’ panel. In here we must change few settings to make our sprite look crisp when used in our game. Make sure that the ‘Texture Type’ is set to ‘Sprite (2D and UI)’, ‘Sprite Mode’ to ‘Multiple’ (as we have many different sprites stored in a single image, ‘Pixels Per Unit’ to 16 (since a single sprite in our image is 16×16 in resolution), ‘Mesh Type’ to ‘Tight’ and ‘Extrude Edges’ to ‘0’. In the bottom section of this panel change ‘Filter Mode’ to ‘Point (no filter)’ and ‘Compression’ to ‘None’ to ensure that our sprite is not going to get blurred along the way. Click ‘Apply’ and enter ‘Sprite Editor’ by clicking the corresponding buttons located within the same ‘Inspector’ panel.

Sprite settings in unity game engine

Inside the ‘Sprite Editor’ click on the ‘Slice’ button located in the top left navigation. In here set ‘Type’ to ‘Grid By Cell Size’ (now you understand why I prefer to have ‘packed’ sprites) and ‘Pixel Size’ to ’16 x 16’ (because again, our individual sprites are 16×16 in resolution inside the exported image).

Setting slicing options in unity

Click ‘Slice’ button and then ‘Apply’ located in the top right navigation bar.

Sprite Editor window in unity

Our sprite had now been properly imported into Unity and just waits to be animated so let us investigate that next!

3. Setting up character animations

Click on a little arrow next to our sprite asset to uncover all animation frames we created in Aseprite and drag the first frame to project ‘Hierarchy’ panel. You may wish to rename this asset to something more meaningful such as ‘Player’. Select the ‘Player’, open the ‘Animation‘ panel, and click on ‘Create’ to create our first clip – the Idle animation. Unity will ask you where you would like to store the animation so provide it with a desired location. Since the idle animation is just going to be our character standing in one place, we don’t have to do much, but we have to create it for all 4 directions so let’s make appropriate clips.

Making Animation clips from sprites in unity

Create a new clip, name it “Character_Idle_Right” and drag and drop the first frame of our character standing from the side and you’re done. Continue with setting up the rest of the idle animations in the similar manner for ‘Idle Up’ and ‘Idle Left’.

Making animation clips from sprites in unity

To create a walking cycle animation, we have to follow similar procedure but instead of dragging and dropping a single sprite frame we have to do it for all frames being composites of a corresponding animation. When you play the animation, you may notice that our character is a speeding demon who appears to have a seizure right now! Let’s fix that. Reveal the ‘Sample Rate’ box by clicking the three dots on the right hand-side of the animation panel and ‘Show Sample Rate’. Set ‘Samples’ to a small number such as 6. Now that’s better! Our character is running much smoother.

Setting speed of the animation by with samples in Unity

Repeat this process to create a full range of walking cycles in all 4 directions. For the attack animations set higher sample rate to say 12 since we want our character to trigger animation immediately upon pressing the attack button.

Setting animation transitions in animator panel

Once we have created all clips let’s open the ‘Animator’ panel. In here delete (yes you read that right) all animation nodes that you have just created from the panel. Right-click on an empty space, select ‘Create State -> From Blend Tree’, name it “Idle” and open up the new Blend Tree by double clicking it. Here we must set up all the transitions between different animations depending which direction player is facing. We want this functionality to be driven by player actions and mapped directly to correct buttons.

First we need to specify few parameters in the ‘Parameters’ tab. Press on the ‘Plus’, select ‘Float’ and name this parameter ‘Horizontal’ – this parameter will be used to detect whether player press ‘left’ (-1) or ‘right’ (1) button. Create the same for ‘Vertical’ movement. Because we also want our character to attack and stay idle in each of the 4 directions, depending on which direction he previously faced, we need to store his last recorded position. To this end, we are going to specify ‘Last_Horizontal’ and ‘Last_Vertical’ parameters that will store that information. The ‘Speed’ parameter will store the information about character speed that we may use for transitioning between different animations. That leaves us with just two more parameters of type ‘Bool’, namely the ‘isAttack’ and ‘isHurt’. We will use the former to determine if the player has pushed the attack button and the latter to know if our player got hurt.

Create Parameters in Animator Window Panel

Once we have set all parameters, click on the blend node in the graph and in “Inspector” panel set “Blend Type” to “2D Simple Directional”, “Parameters” to “Last_Horizontal” and “Last_Vertical”. Next, make sure you add 4 motions to the list (for each of 4 directions). Set ‘Player_Idle’ animation to occur when ‘Pos X’ is ‘0’ and ‘Pos Y’ is ‘-1’. This can be translated to: “make our character face down if the player’s last recorded action was to walk down”. We have to do it for the rest of the motion so set ‘Player_Idle_Right’ to ‘Pos X’ ‘1’ and ‘Pos Y’ ‘0’, ‘Player_Idle_Left’ to ‘Pos X’ ‘-1’ and ‘Pos Y’ ‘0’, ‘Player_Idle_Up’ to ‘Pos X’ ‘0’ and ‘Pos Y’ to ‘1’.

Blend Tree settings for player idle animations

Return to ‘Base_Layer’ of our ‘Animator’ by clicking it in the breadcrumb navigation and create another ‘Blend Tree’.

Returning to base layer of animator panel

Name it ‘Movement’. Here we have to do the similar setup we did with ‘Idlening’ blend tree, but now set ‘running’ animations in motion list. Don’t forget to set ‘Horizontal’ and ‘Vertical’ as parameters.

Set running clips in motion list of a blend tree

Now let’s create the ‘Attack’ blend tree. This will slightly differ from the other two because we need to make sure that our character is displaying the correct animations even if he faces the diagonal direction. For the sake of this tutorial and to keep things simple, we are going to set, for instance, ‘left attack’ animation when character faces diagonal left-up direction and so on. This time we have to use the last recorded viewing direction of our character, so we are going to use “Last_Horizontal” and “Last_Vertical” parameters. To set the diagonal positions we have to use both “Pos X” and “Pos Y” so for instance, the ‘right-down’ position will have these parameters set to “1” and “-1” accordingly.

Set attack clips in motion list in Attack blend tree

At this point you should have 3 blend trees, namely “Idleing”, “Movement” and “Attack”. Let us now set the transitions between them!

  • Back in the ‘Animator’ base layer panel, right-click on the ‘Idlening’ blend tree node and select ‘Set as Layer Default State’.
  • Right-click on ‘Idlening’ again, select ‘Make Transition’ and drag the arrow to ‘Movement’ blend tree node.
  • Click on the newly created arrow and in the ‘Inspector’ panel untick ‘Has Exit Time’ (since we want our animation to start straight away when player moves the character around) and ‘Fixed Duration’ fields.
  • The ‘Transition Duration’ and ‘Transition Offset’ fields should be set to ‘0’.
  • In the ‘Conditions’ we are going to use ‘Speed’ to determine if the player is indeed moving. If the ‘Speed’ is greater than 0.01 then we will transition to this blend tree node from idle state.
  • Right-click on the ‘Movement’ node and make transition to the ‘Idleing’ node. Set ‘Speed’ condition to be ‘if Speed is less than 0.01’.

To make our character attack regardless of whether he was walking or standing still, we need to create transitions from both blend node trees. Right-click on ‘Movement’ node, drag the arrow to ‘Attack’ and set all the parameters as before, but set ‘isAttack’ to ‘true’ as a condition. Now create 4 transition arrows from ‘Idlening’ node to ‘Attack’ node. The parameters need to be set as before but the conditions need to reflect

  • a) the last recorded facing direction of the player
  • b) whether the player pressed the ‘attack’ button.

We are going to use ‘Last_Horizontal’ and ‘Last_Vertical’ to determine this once again. Check ‘Last_Horizontal’ whether is it ‘greater’ or ‘less’ than 0.01 to determine the right and left directions and do the same for vertical directions. Set 2 conditions per one transition from idle state.

Set conditions for attack transitions in unity

Right-click on the ‘Attack’ state and drag the arrow back to idle state node. As usual, set the parameters as before but this time only one condition – check if ‘isAttack’ equals ‘false’. Well done, you have now completed all the animations and transitions! Let’s now move to the final stage of this tutorial and bind everything together with scripts!

4. Binding everything together with scripts

Click on the ‘Player’ asset in ‘Project Hierarchy’ and add ‘RigidBody2D’ component. Set ‘Body Type’ to ‘Dynamic’, ‘Gravity Scale’ to ‘0’ and tick ‘Freeze Position’ to ‘Z’. This will ensure the correct behaviour when we steer our character inside the game world. Now add new component ‘Script’, name it ‘PlayerMovement’ and start editing it using your favourite text editor. I’m going to use Visual Studio Community 2019.

We are going to need some references to other components of the ‘Player’ asset to make him respond to input coming from the player. Let’s start by creating 4 fields:

public float moveSpeed = 5f;
public Rigidbody2D rb;
public Animator animator;
Vector2 movement;

In the ‘Update()‘ function we have to keep track of input coming from the player, register whether the attack button has been pressed and set appropriate parameters values we have defined in the ‘Animator’.

// Update is called once per frame
void Update()
{
    movement.x = Input.GetAxisRaw("Horizontal");
    movement.y = Input.GetAxisRaw("Vertical");

    animator.SetFloat("Horizontal", movement.x);
    animator.SetFloat("Vertical", movement.y);
    animator.SetFloat("Speed", movement.sqrMagnitude);

    if(Input.GetAxisRaw("Horizontal") == 1 || 
       Input.GetAxisRaw("Horizontal") == -1 || 
       Input.GetAxisRaw("Vertical") == 1 || 
       Input.GetAxisRaw("Vertical") == -1)
    {
       animator.SetFloat("Last_Horizontal", 
                         Input.GetAxisRaw("Horizontal"));
       animator.SetFloat("Last_Vertical", 
                         Input.GetAxisRaw("Vertical"));
    }

    if(Input.GetKeyDown(KeyCode.Space)
       animator.SetBool("isAttack", true);
    
}

At this point if you run the game you may notice that nothing is happening when you press buttons. That’s because we haven’t yet told Unity how it needs to execute your action. Since we are using the physical rigid body (RigidBody2D) we are going to use FixedUpdate() funtion. The reason for this is that there may be some discrepancies between physics calculations carried out at different frames during gameplay and we may run into glitches if we simply update the position at every frame in the Update() function. We also want to prevent our character from moving when he is attacking his opponents.

void FixedUpdate()
{
    if (animator.GetBool("isAttack") == true)
    {
        rb.velocity = Vector2.zero;
    } else
    {
        rb.MovePosition(rb.position + movement * moveSpeed *
                        Time.fixedDeltaTime);
    }
}

The code is relatively straightforward: we are taking the current position of the character, add the movement vector and multiply it by the speed. To make everything run smoothly regardless on the machine at which we are running the game, we also multiple it by built-in variable Time.fixedDeltaTime. We now have to finish everything off by adding one more function to return character to normal idle state after the execution of the attack:

void StopAttack()
{
    if(animator.GetBool("isAttack"))
        animator.SetBool("isAttack", false);
}

The whole script file now looks like this:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;
    public Rigidbody2D rb;
    public Animator animator;
    Vector2 movement;
    
    // Update is called once per frame
    void Update()
    {
        movement.x = Input.GetAxisRaw("Horizontal");
        movement.y = Input.GetAxisRaw("Vertical");

        animator.SetFloat("Horizontal", movement.x);
        animator.SetFloat("Vertical", movement.y);
        animator.SetFloat("Speed", movement.sqrMagnitude);

        if(Input.GetAxisRaw("Horizontal") == 1 ||
           Input.GetAxisRaw("Horizontal") == -1 || 
           Input.GetAxisRaw("Vertical") == 1 || 
           Input.GetAxisRaw("Vertical") == -1)
        {
            animator.SetFloat("Last_Horizontal",
                              Input.GetAxisRaw("Horizontal"));
            animator.SetFloat("Last_Vertical", 
                              Input.GetAxisRaw("Vertical"));
        }

        if(Input.GetKeyDown(KeyCode.Space))
            animator.SetBool("isAttack", true);

    }

    void FixedUpdate()
    {
        if (animator.GetBool("isAttack") == true)
        {
            rb.velocity = Vector2.zero;
        } 
        else
        {
            rb.MovePosition(rb.position + movement * moveSpeed * 
                            Time.fixedDeltaTime);
        }
    }

    void StopAttack()
    {
        if(animator.GetBool("isAttack"))
            animator.SetBool("isAttack", false);
    }

}

The StopAttack() should be really called after the execution of any of the attack actions. However, to keep things clean we don’t want to write any complicated codes inside our Update() function block. Instead, we are going to add event on each of our individual attack animation timeline that will trigger this function every time it reaches its end.

Click on the event icon on the timeline and in the ‘Inspector’ panel select ‘StopAttack()’ function from the dropdown menu. Congratulations! You have now implemented a top-down movement and attack mechanics in Unity!

Conclusion

The example provided in this tutorial is just a mere opening to endless possibilities of what can be done. We have created character sprites and animations and imported them into Unity. Then we took the advantage of the powerful animation engine it provides to define our character dynamics. At the end we bound everything together with custom script to move our character around inside the game world. At this point you may wish to add more animations, maybe you wish to add more weapons or fx sounds? In the next tutorial we are going to look into how this example can be expanded with additional features.

References

Pixelart 101 FREE College Course “Game Boy” by Pixel Pete
TOP DOWN MOVEMENT in Unity! by Brackeys
Topdown 2D RPG In Unity – 19 Attacking by Hundred Fires Games