Adding Animations to Our Robot AI in Unity 2021
In this article, we’ll add animations of the AI character walking, running, and going idle behind cover.
The first thing we need to check is how the Rig’s Animation Type was imported for our Robot we’ll be animating.
It is mistakenly set as Generic, so we’ll set that to Humanoid.
Before I create an Animation Controller from scratch, I want to check the project for one since the Robot prefab was originally missing from the project.
Sure, enough there is a “Robot” Controller, which I’ve assigned to the Animator component of the Robot_1 game object.
I’ve also clicked the Overrides Apply button to save this to the prefab itself.
Running the game, I find that the Controller never leaves the Idle state so no animation is occurring.
Looking over the Controller itself, I can see that it is setup with the assumption that the AI should always walk before it runs and that it can enter the Cover_idle animation from the Running State.
There are also 3 parameters already included in the Controller which I will need to make use of.
For a more complete idea of what I’m working with here, I’ve previewed each animation state.
Now, I’m going to manually mess with the Animator Controller’s parameters while the game is running to see what effects they have.
Above we can see idle animation becomes the walk animation which becomes the run animation as we increase the speed value.
The Hiding parameter only takes effect when in the Running state, as expected, so the Speed parameter has to be a value high enough to enter the Running state.
And of course, our Death parameter does exactly what you would expect it to.
Controlling the Controller
We have the basic building blocks needed for our objective but we’ll need to supply the concrete that binds them.
In the case of the Robot_1 animations, we’ll need to control these animation states from our RobotAI script.
First of all, we’ll need a variable to make referencing the Animator more convenient.
We’ll also use variables to control each of the parameters we have access to.
Next we have to assign the Animator to its variable in our Start() method.
We’ll also default the values for our parameter variables.
You would think that you could just call “Animator.parameters” or “.speed” to assign the parameters but you’d be wrong.
We can use Animator.parameters to GET or READ the parameters, but we can’t set them.
This would be useful if we didn’t know ahead of time what parameters to expect so that we could just iterate through and load them into an array.
But we do, so that is not necessary.
Animation Parameters are variables that are defined within an that can be accessed and assigned values from . This is…
Per the Unity documentation shown and linked above, we can see that there are 5 methods on the Animator class we can use to modify the runtime animator controller’s parameters.
SetFloat, SetInteger, SetBool, SetTrigger, and ResetTrigger.
With these methods, we need to pass the name of the Parameter we wish to modify and the value it should be changed to.
Before I wrote a ton of code related to this approach, I added the bit of code to the Update() method and verified that it was working as desired.
Which it did.
I then removed that bit of code from the Update() method.
Thinking ahead, I knew that with object pooling, these parameters and their associate variables would need to be reset when the Robot_1 game object is reused rather than destroyed.
After some trial and error and integrating it with the another method we’ll review, SetCoverStatus(), I finally arrived at this finished method for setting the animation states.
This method is responsible for communicating with the Animator component and its parameters as well telling the NavMeshAgent to stop or start.
The SetAnimationState() method calls the WalkToRun() coroutine when appropriate.
Since the animation controller will change from walking to running based on the “Speed” parameter, this proved to be an ideal if simple approach.
It’s pretty ugly, I’m not going to lie.
I’ll probably clean this up later but right now I’m focused on the functionality.
This method is responsible for keeping track of the finite state machine that is “Cover Status”.
In retrospect, I might have went with a finite state machine based purely on the state of motion for this NPC, but this is in working order for the time being.
Actually, this FSM does focus on the state of motion but I’ve just named everything around the state of cover.
Idling is a state of motion! Or lack thereof, anyways.
The WaitToRun() coroutine is called from the SetCoverStatus() method when the AI enters the “at cover” state.
After waiting a few seconds, the AI goes back to running towards the end point.
With these new methods in play, the Start() method was ready to set the initial Animation State as shown above.
I also added a call to a newly created coroutine for testing purposes.
I’ve revised the move method so that _navMeshAgent.isStopped has been removed and by rewriting the if/then statement to fit my new overall approach to the code flow.
The Update() method remains unchanged.
I’ve included it here for clarity.
This method remains pseudo code and is still not used at this time.
Later I will implement it to direct the AI to take cover when being fired at.
This method returns a single position value that was used for testing purposes.
In the future it will determine the nearest waypoint associated with taking cover.
As I intend to add more game objects to the scene as cover, the array holding these positions and this method are on the back-burner for the moment.
The ReachedEnd() method has some code, more may be needed but I will tackle that when necessary.
The ResetValues() method has been updated to reset the Animation State and NavMeshAgent.isStopped.
OnDeath() triggers the death animation and will need to be fleshed out more as well.
This will need to integrate with hit detection which is still a ways off.
After adding the cover game objects to the scene and associated array, I’ll be tackling object pooling the AI with a spawn manager.
Still a long ways off from implementing the player’s ability to shoot and the AI’s ability to take damage.