Creating Your First Unity Game – Rainy Knifes Part 5: Animating The Player With Code

Table of Contents

Creating Your First Unity Game – Rainy Knifes Part 5: Animating The Player With Code

Reading Time: 9 minutes
Level: Beginner
Version: 2020.3.1 LTS

Help Others Learn Game Development

In part 4 of this tutorial series we created the Knife obstacle, made it a Prefab and we started spawning the Knifes in the game.

In this part of the tutorial, we are going to animate the Player through code and show some neat code tricks.

Freezing The Rotation Of The Rigidbody

Currently the Knifes are falling from the sky and pilling up in our game. This of course will be fixed soon because it’s not how our game is going to be played.

But we can’t help but notice one thing that happens with the Player while he is trying to move in the pile of Knifes:

While the Player is trying to move through the Knifes he falls down.

I am going to illustrate this issue even better by using a ground game object and making the Player fall on it:

As you can see the Player fell off the edge of the ground object.

This is something that you won’t expect in a video game except if this is a part of your gameplay, but normally these things don’t happen.

Now, even though we could have skipped this part because when we make the Knifes disappear when they fall on the ground we will not have this issue, but still I want to explain it as we will use it later in our development.

The reason why the Player falls down is because we didn’t freeze the rotation of his Rigidbody 2D component.

In order to do that, click on the Player game object, and under his Rigidbody 2D component find the Constraints option:

Img 1
From there, click on the drop down list for the Constraints option and check the check box to freeze the Z rotation:
 
Img 2
This will not allow the Rigidbody 2D component of the Player to mess with its rotation.
 
So now, when the Player falls down on the ground again, he will be standing still:
 

Getting A Reference To The Player's Animator Component

We created the Player’s animations in the first part of this tutorial series and we set up the animation transitions and we created a bool parameter that we will use as the condition for the animation transition.
 
If you remember, when we created the Animator Controller in part 1 we said that we will use him in the code to animate the Player.
 
Well the Animator component will use the Animator Controller and we will instructor the Animator component how to animate the Player with the help of the Animator Controller.
 

Inside the PlayerMovement script above the Awake function declare the following:

				
					private Animator anim;
				
			
Now, there are two ways how we can get the Animator component that is attached on the Player game object.
 
The first way is to use the GetComponent function, and we can add that line of code in the Awake right below where we got a reference to the SpriteRenderer component:
 
				
					private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }
				
			
Another way to get the Animator component is to make the Animator variable a SerializeField:
 
				
					[SerializeField]
    private Animator anim;
				
			
And now we can drag the Animator component from the Player game object directly into the anim empty field in the Inspector tab:
 

We can also drag the Player game object in the anim field for the script, and because the Player game object has an Animator component attached on it, the script will know to look for it and even though we technically dragged the Player game object, the Animator component will be the one that will be added in the anim field:

Either way you chose the outcome will be the same.

Just remember, if you choose to get a reference to the Animator component by using the GetComponent line of code, remove the SerializeField from the Animator variable declaration:

				
					private Animator anim;
				
			

I am going to use the GetComponent approach so I will remove the SerializeField from the Animator variable declaration and in the Awake I will get a reference to the Animator:

				
					private void Awake()
    {
        sr = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }
				
			

Animating The Player In Our Code

Now that we have a reference to the Animator component, we can create a function to animate the Player’s movement.

But the question is how can we know if the Player is moving or not?

For that, we can use the moveX variable.

If you remember, we are storing the Input.GetAxisRaw we get from the user in the moveX variable, and we said that when the user presses the Left Arrow or the A key the value returned will be -1 and when the user presses the Right Arrow or the D key the value returned will be 1.

We can use that information to know if the Player is moving or not.

I am going to create a new function that will Animate the Player and put the code for the animation inside:

				
					// function for handling the animation
    void HandleAnimation()
    {
        if (moveX != 0)
            anim.SetBool("Walk", true);
        else
            anim.SetBool("Walk", false);
    }
				
			

Inside the HandleAnimation function we are testing if moveX is not equal to 0, that means the value of moveX is either -1 or 1 which means that the Player is moving.

To animate the movement, we use the SetBool function, passing the name of the parameter and the value of the parameter.

Why SetBool function?
 

If you remember, when we set up the animation for the Player in part 1 of this tutorial series, we created a parameter that will handle the transitions between the Idle and the Walk animation, that parameter is a bool and the name of that parameter is Walk:

And because that parameter is a bool parameter, we call the function SetBool and we pass the name of the parameter which is Walk, this is what you see on line 5 in the code above.
 
The true value that we set, is the condition for the transition that will go from Idle animation to the Walk animation:
 
Img 4

And if the value of moveX is equal to 0(zero), then we call the same function e.g. SetBool, pass the name of the Walk parameter but this time we set the value to false which you can see on lines 6 and 7 of the code above.

We set the value to false, because that is the condition for the transition that will go from Walk back to the Idle animation:

Img 5

Before we test out our code we need to do two things.

First, call the HandleAnimation function inside the Update function:

				
					private void Update()
    {
        HandleMovement();
        HandleFacingDirection();
        HandleAnimation();
    }
				
			

Second, because the Knifes are going to fall from the sky we are going to turn off the Knife Spawner game object by unchecking the check box in the Inspector tab:

Img 6
This check box controls if a specific game object is active or not active in the Scene. If we uncheck it, then the game object will not be active in the Scene meaning the game object will not be active in the game at all.
 
Because of that, any script that is attached on that game object will not run and the code inside will not be executed. This is one of the most important things to understand and learn in Unity.
 
Now, we can run the game and test the Player’s animations:
 
As you can see the Player is being animated when he is moving, but we do have one issue.
 
The animations look like they are running late, and you can notice that when I move the Player game object, you can see him moving in the game, but he looks like he is sliding on the ground and after a few moments the Walk animation starts playing.
 
Rewatch the video above to see that part as I left it there on purpose to demonstrate the issue.
 

Animation Transition Settings

The problem with the animations running late lies in the Settings set up of the transitions of the animations.

To fix this, select any of the transitions, and in the Inspector tab locate the Settings option:

Img 7

As you can see the Transition Duration option is set to 0.25 which is why the transition takes time to finish and that’s why the animations are running late.

To fix this issue we are going to set the value of Transition Duration to 0 and we are also going to uncheck the Has Exit Time option because we don’t want the transitions to have an exit time which also delays the transition between the animations.

This is how you should set up the options in the Settings for BOTH transitions:

Img 8

Again, make sure the settings for BOTH transitions are like the ones you see in the image above, and now we can test the Player’s animations again:

Now the animations are running smoothly and they don’t have a delay when they transition between them.

The TagManager Script

Now one thing that I don’t like about the animation code we wrote is the fact that we are “hard coding” the names of the parameters.
 
Specifically on lines 5 and 7 we are using Walk to call the name of the paramater:
 
				
					// function for handling the animation
    void HandleAnimation()
    {
        if (moveX != 0)
            anim.SetBool("Walk", true);
        else
            anim.SetBool("Walk", false);
    }
				
			
Now you are wondering how else can we call the Walk parameter except by typing out its name, and that is true, and it will work for a small game like this.
 
Because we only need to call the Walk parameter in these two lines of code.
 
But what what if we need to call the Walk parameter on hundred more places in our code, or even more, that will expose us to errors very quickly.
 
Because it can happen that somewhere in our code instead of typing Walk we type walk, or we type Wal, or Wall, and so on.
 
If we make a typo like that our code will not function or if we make a mistake when it comes to capitalization, again our code will not work.
 
A better approach is to have a static class that will hold all the names we need to “hard code” in our game.
 
If you want to refresh your knowledge about what is a static class, you can do that in the lecture about static classes.
 
Inside the Scripts folder Right Click -> Create -> Folder and give it a name Game Manager Scripts.
 
Inside of that folder Right Click -> Create -> C# Script and give it a name TagManager.
 
The TagManager is going to be a static class which means the variables we declare inside are going to be static variables.
 
Let’s add a static string that will represent the Walk parameter name:
 
				
					public static class TagManager
{

    public static string WALK_ANIMATION_PARAMETER = "Walk";
    
}
				
			
I like to capitalize the names of variables that I add in the TagManager script and I always denote for what will the variable be used e.g. WALK_ANIMATION_PARAMETER.
 
When I see that name I know that I will use this variable as a parameter for an animation and the name of that parameter is Walk.
 
So now we can change the animation code to use the TagManager:
				
					// function for handling the animation
    void HandleAnimation()
    {
        if (moveX != 0)
            anim.SetBool(TagManager.WALK_ANIMATION_PARAMETER, true);
        else
            anim.SetBool(TagManager.WALK_ANIMATION_PARAMETER, false);
    }
				
			
This will not change the outcome of our game and if you test this out you will see no difference, except that our code is much cleaner.
 
If we make a mistake and something is not working, we only need to go inside the TagManager script to fix the issue if for example we typed walk instead of Walk.
 
Right away you can see how that approach is a lot easier than having to go through hundreds of lines of code to fix the typo we made.
 
While we are at it, we can also modify the movement code because in the Input.GetAxisRaw we are also hard coding the parameters.
 
Inside the TagManager add another string:
 
				
					public static class TagManager
{

    public static string WALK_ANIMATION_PARAMETER = "Walk";

    public static string HORIZONTAL_AXIS = "Horizontal";

}
				
			

Inside the HandleMovement function modify the line of code where we get the Input from the user:

				
					moveX = Input.GetAxisRaw(TagManager.HORIZONTAL_AXIS);
				
			

Again, if we test this out, we will not see any change. Everything will run the same as before, but our code is much cleaner.

Where To Go From Here

In this lecture we learned how to use the Animator component to animate the game objects from code.

We also learned more about the options we have for animation transitions and options we have in the Rigidbody 2D component.

In the next lecture titled Detecting Collision In Code And Restarting The Game After The Player Dies we are going to add some finishing touches and wrap up our game.
 

Leave a Comment