Animated Character 2D in Unity (Part 2)

Animated Character 2D in Unity (Part 2)

When you are attempting to build a 2D platform game, one of the most important elements will help me a character movement. So in this post, I will show you how to make a simple animated character in Unity.

That character will have an idle animation, walking, and running animations, and jumping and falling down animations.

I will be using a few assets make by Kenney so buckle up!

Read part 1 first! πŸ“š

This post is a continuation of Animated Character 2D in Unity (Part 1). You should start reading there before reading this post. πŸ˜‰

Start walking! πŸšΆβ€β™€οΈ

So it’s finally time to make our character walk! To do that, we would need to create a little script that will be responsible for that. Let’s call it CharacterMovement.

This script will read player input and translates it into a character walk.

Let’s start with gathering necessary references for our component. We will need here Animator and Rigidbody2D.

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{
    // Reference to the Animator
    private Animator characterAnimator;
    // Reference to the Rigidbody2D
    private Rigidbody2D rb;

    /// <summary>
    /// Setup component
    /// </summary>
    private void Awake()
    {
        // Get references
        characterAnimator = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
    }
}

The next step will be to configure walk speed and read player input. We can also set the value for our animator to make our character “walk”.

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{

   ...

    [Header("Movement")]
    /// <summary>
    /// Character Walk Speed
    /// </summary>
    [SerializeField]
    private float walkSpeed = 1f;

    // Input
    private float horizontalInput;

   ...

    /// <summary>
    /// Called on each frame
    /// </summary>
    private void Update()
    {
        // Get input
        horizontalInput = Input.GetAxisRaw("Horizontal");

        // Set animation movement speed
        var animSpeed = horizontalInput;
        characterAnimator.SetFloat(Keys.ANIMATION_SPEED_KEY, Mathf.Abs(animSpeed));
    }
}

You can also spot a little Keys class. I’m using that to store all of my string keys, so I won’t make any typos in my code. πŸ˜‡

That’s how it looks:

/// <summary>
/// This class contains constant strings for use in code.
/// </summary>
public static class Keys
{
    public const string ANIMATION_SPEED_KEY = "Speed";
    public const string ANIMATION_INAIR_KEY = "InAir";
    public const string ANIMATION_AIRSPEED_KEY = "AirSpeed";
}

So the result of that code is…

Walk animation in action!

That’s not yet completed! As you can see, our character is not moving! And it’s not changing direction…

Because we are using Rigidbody (and physics system) for our movement, we should only move our character in FixedUpdate method!

So let’s do just that!

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{
    ...

    /// <summary>
    /// Called on each physics frame
    /// </summary>
    private void FixedUpdate()
    {
        // Get character velocity
        var velocity = rb.velocity;

        // Calculate character movement
        var moveSpeed = 0f;
        var moveDirection = 1f;

        // Calculate move speed and direction
        if (!Mathf.Approximately(horizontalInput, 0))
        {
            moveSpeed = walkSpeed;
            moveDirection = Mathf.Sign(horizontalInput);
        }

        // Set character velocity and direction
        velocity.x = moveSpeed * moveDirection;
        transform.localScale = new Vector3(moveDirection, 1, 1);
        rb.velocity = velocity;
    }
}

And that will make our character walk freely in the scene.

Our character walks!

How about running? πŸƒβ€β™€οΈ

Walking in games is really boring, so let’s ease that pain by introducing running for our character!

We would need to read another input. This time we will have to define “Sprint” in InputManager.

Go to the Project Settings, and then select Input Manager. Add a new entry and name it “Sprint”.

Sprint definition in the InputManager

Now we can use our new “Sprint” button in our code, but we shouldn’t forget about adding a new property to control character running speed.

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{
    ...

    /// <summary>
    /// Character Run Speed
    /// </summary>
    [SerializeField]
    private float runSpeed = 1.5f;

    // Input
    private float horizontalInput;
    private bool sprintInput;

   ...

    /// <summary>
    /// Called on each frame
    /// </summary>
    private void Update()
    {
        // Get input
        horizontalInput = Input.GetAxisRaw("Horizontal");
        sprintInput = Input.GetButton("Sprint");

        // Set animation movement speed
        var animSpeed = horizontalInput * (sprintInput ? 2 : 1);
        characterAnimator.SetFloat(Keys.ANIMATION_SPEED_KEY, Mathf.Abs(animSpeed));
    }

    /// <summary>
    /// Called on each physics frame
    /// </summary>
    private void FixedUpdate()
    {
        ...

        // Calculate move speed and direction
        if (!Mathf.Approximately(horizontalInput, 0))
        {
            moveSpeed = sprintInput ? runSpeed : walkSpeed;
            moveDirection = Mathf.Sign(horizontalInput);
        }

        ...
    }
}

After adding all of that code, our character will be able to run! Just like this:

Our character runs!

But can it jump? 🧐

Every 2D platformer need to have jumped! Or at least should have it… πŸ™ƒ

So let’s make our character do that! But be aware that it might be a little bit more complex, so pay attention to the code!

First of all, we have to check if our character is on the ground. That information is needed as our character will only jump if it’s on the ground.

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{
    ...

    [Header("Ground check")]
    /// <summary>
    /// Ground check origin offset
    /// </summary>
    [SerializeField]
    private float groundOffsetCheck = 1f;

    /// <summary>
    /// Ground check distance
    /// </summary>
    [SerializeField]
    private float groundDistanceCheck = 0.4f;

   ...

    /// <summary>
    /// Checks if player is on the ground.
    /// </summary>
    /// <returns><c>true</c>, if on ground, <c>false</c> otherwise.</returns>
    private bool IsOnGround()
    {
        // Get ground layer mask
        var groundMask = LayerMask.GetMask("Ground");

        // Raycasting
        var origin = rb.position + Vector2.down * groundOffsetCheck;
        var result = Physics2D.Raycast(origin, Vector2.down, groundDistanceCheck, groundMask);

        // Draw debug line to adjust parameters
        Debug.DrawLine(origin, origin + Vector2.down * groundDistanceCheck);

        // Check if something was hit
        return result.transform != null;
    }
}

The next step will be to make our character jump! We would have to read another input from the player and extend our FixedUpdate method with code for jumping.

We would also need to pass some info to the Animator, so our character can be properly animated.

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{
    ...

    // Input
    private float horizontalInput;
    private bool sprintInput;
    private bool jumpInput;

    // Property - if player is on ground
    private bool isGrounded = false;

    ...

    /// <summary>
    /// Called on each frame
    /// </summary>
    private void Update()
    {
        // Get input
        horizontalInput = Input.GetAxisRaw("Horizontal");
        sprintInput = Input.GetButton("Sprint");
        jumpInput = Input.GetButtonDown("Jump");

        ...
    }

    /// <summary>
    /// Called on each physics frame
    /// </summary>
    private void FixedUpdate()
    {
        ...

        // Check if grounded
        isGrounded = IsOnGround();

        // Assign ground status to animator
        characterAnimator.SetBool(Keys.ANIMATION_INAIR_KEY, !isGrounded);

        // Initial jump only on ground
        if (isGrounded)
        {
            if (jumpInput)
            {
                velocity.y = jumpForce;
            }
        }

        // Set air velocity
        characterAnimator.SetFloat(Keys.ANIMATION_AIRSPEED_KEY, velocity.y);

        ...
    }

    ...
}

This should make our character jump!

Our character jumps!

I need double jump! 😫

Okay, I missed that one… But yeah, we need a double jump!

Luckily, this won’t be that hard to implement at this point! πŸ₯°

using UnityEngine;

/// <summary>
/// Simple Character movement.
/// </summary>
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody2D))]
public class CharacterMovement : MonoBehaviour
{
    ...

    // Property - if player can do second jump
    private bool consumedDoubleJump = false;

    ...

    /// <summary>
    /// Called on each physics frame
    /// </summary>
    private void FixedUpdate()
    {
        ...

        // Initial jump only on ground
        if (isGrounded)
        {
            consumedDoubleJump = false;
            if (jumpInput)
            {
                velocity.y = jumpForce;
            }
        }
        else
        {
            // Double jump
            if (!consumedDoubleJump && jumpInput)
            {
                velocity.y = jumpForce;
                consumedDoubleJump = true;
            }
        }

        ...
    }
}

Result! πŸ†

So it’s finally time to see the character movement completed!

Our character in final form!

I hope you enjoyed this little journey with me! Let me know what you think of it in the comment section below! πŸ”₯

If you know someone that might need this, share it with him! I would really appreciate that! πŸ₯°

Besides that, if you want to get notified about future content on my blog, you can sign up for my newsletter!

The whole project is available at my public repository. πŸ”—

See you next time! πŸ€“

5 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments