Welcome back to day 28!
Yesterday, we started our next task on creating multiple new types of enemies and made a new bandit enemy.
Today, we’re going to create our last enemy, a slower enemy with more health that won’t get pushed back as far.
Today is going to be very similar to yesterday, however we’re going to try and create a new animator controller for our enemy. We’ve seen this before in the Survival Shooter, which I once again blissfully ignored, however we’re re-visiting this topic today.
Let’s get started!
Step 1: Acquire new asset
Let’s return to our favorite place in the world! The candy store Unity Asset Store!
Just like yesterday, we’re going to look for an asset that’s mecanim ready.
If you’re not aware of the glory that is the mecanim system, you can visit Day 11 where we first used it.
Step 1.1: Get Zombie Asset
Browsing around the asset store for another enemy to use and I found a nice barbarian mage to use for our tank enemy!
It’s called Zombie01:
Download and import the barbarian into our game. It’ll be in Assets > Zombie_0_1.
Step 2: Setup the Zombie
Yesterday, we created our Bandit enemy that we re-use our existing Knight Animator Controller, however for our Zombie I want him (it?) to have different move set and animations.
Today, we’re going to do things differently!
Step 2.0: Set Wave 1 SpawnManager to Spawn -1 Enemies
If you have set our Wave 1 to spawn -1 enemies already then you don’t have to do anything.
However, if not, go to our SpawnManager and make this change:
Step 2.1: Creating an Animator Override Controller
To achieve the goal of creating an enemy with new animations (but the same states), we’re going to use the Animator Override Controller.
An Animator Override Controller allows us to give it an Animator Controller for us to override and put our own animation clips in. This is very handy for us if we want to create an enemy that has the same states from another Animator Controller, but you want to use different animations for each state.
Let’s first create the animator.
- Go to Assets > Animator and right click and create a new Animator Override Controller. Let’s call it Zombie Animator Controller.
- Set Knight Animator Controller to be our Controller in our Zombie Animator Controller
Now you’ll see something like this:
See how we have the same 4 states from our Knight Animator Controller?
With this we can add our clips in, but which clips?
For this, let’s just re-use the existing ones that came along with the Knight Barbarian.
In Assets > Zombie_0_1 > Animations, we’ll find Zombie_0_1, if we expand the model, we’ll see these animation clips!
As you can see we have 8 animation clips attached:
If you want to see what each of the animation does, click on the model and you’ll see the clips that are attached and what they each look like:
Unfortunately, because these animations share very similar names, I’m going to have to rename them.
The 4 we’ll need are attack01, death, idle0, and run. Click on each of them and hit Ctrl + D to duplicate them.
Rename each of them to be ZombieAttack, ZombieDeath, ZombieIdle, and ZombieRun.
Move these into our Animation folder.
Here’s what we have:
Back to our Zombie Animator Controller, let’s add our clips in and we should have this:
Step 2.2: Attaching the Animator Controller
Now that we have created our Animator controller, let’s create our zombie to use it!
- In Assets > Zombie_0_1 > Animations drag the Zombie_0_1 model into our game’s hierarchy.
- IAdd a new Animator component and inside for the Animator Controller add our Zombie Animator Controller.
Here’s what we should have now:
Note: The Transform position could be anything. I moved it closer to the player, but in the end, it’s not going to matter, because we’re going to spawn it in our present locations.
Step 2.3: Attaching the Nav Mesh Agent and the 3 scripts: EnemyHealth, EnemyAttack, and EnemyMovement
At this point, everything that we’re going to do is going to be the exact same as the day before.
Select the Barbarian game object and add a Nav Mesh Agent Component, Enemy Health, EnemyAttack, and EnemyMovement script to it.
Step 2.4: Setting up the Scripts and Nav Mesh Agent
Now that we have attached our script, we need to set them up.
Nav Mesh Agent
For our Nav Mesh Agent component, we want to adjust the speed of how fast it moves.
Let’s set the Speed to be 2. Our Knight’s speed is 3 and our Bandit’s speed is 3.25
For our Enemy Health we want to set:
- Health: 15
- Hit Sfx Clips > Size: 4
- Set the 4 new Audio Clips to be Male_Hurt_01 – Male_Hurt_04
- Hit Sound Delta: 5
For the EnemyMovement scripts, we want to set:
- Walking Clips > Size: 4
- Set the 4 new Audio Clips to be Footstep01 – Footstep04
- Walking Delay:7
- Knock Back Force: 9
We’re going to do some re-work with our EnemyAttack script, technically speaking, we should use sound effects that are not punching sounds, however, because we’re only practicing, let’s just use the same sound effects we have been using.
- Attack Sfx Clips > Size: 3
- Set the 3 new Audio Clips to be Punch_01 – Punch_03
- Leave the Fist Colliders alone for now
Now if we were to play the game, our Zombie will start chasing after us, but he won’t attack us yet, we haven’t set up our triggers and colliders yet!
Step 2.5: Adding 3 Colliders: Trigger Collider, Mesh Collider, and Fist Colliders
At this point, we’ll be doing nearly the exact same thing as we done yesterday.
We’re going to add:
- A Capsule Collider to be used as a trigger to tell our zombie to enter attack mode if he’s near the player.
- 2 Box Collider that will be attached to the zombie’s weapon to tell when we damage the player.
- A Mesh Collider to be used for detecting when the player shoots the zombie.
Adding the Capsule Collider
Select Zombie in the hierarchy and then add the Capsule Collider component.
We’ll make it a trigger, set the Y to be 1, and then expand Radius to be 1.5.
When you’re done, we should have something like this:
Note: the radius is the outer sphere that you see.
To do a re-cap, whenever our player enters the circle, in our EnemyAttack script, we’ll hit OnTriggerEnter() which is used to tell the bandit that it’s in range to be able to hit the player.
Adding the 2 Box Colliders
Next up, we need to attach colliders to our weapon.
The process won’t be too different compared to working with fists.
In Zombie > Bip001 > Bip001 Pelvis > Bip001 Spine > Bip001 Spine1 > Bip001 Neck Bip001 L Clavicle > Bip001 L UpperArm > Bip001 L Forearm > Bip001 L Hand > Zombie_0_1_002
We want to attach a box collider. We’re just going to make it as big as our weapon.
We need to make some changes with the collider. Specifically, the Z value for Center and Size.
- Set the Z value for Center to be 0.6
- Set the Z value for Size to be 0.2
The reason why we’re doing this is because we don’t want our weapon to block our gun raycasting when we try to shoot the enemy.
Next, we’ll attach our FistCollider script to our game object.
At this point, we really should call it a weapon collider, but we’ll keep it the same for simplicity.
When we’re done, we should have something like this:
We’ll do the exact same thing for the right weapon as well.
Adding the Mesh Collider
Next up is our Mesh Collider component.
The same as yesterday, the purpose of the Mesh Collider is for 2 reasons, it allows us to:
- shoot the enemy
- get pushed by the enemy
For the zombie, this is easy.
All we need to do is add a Mesh Collider component to Zombie > Zombie_0_1.
And for the Mesh, attach Zombie_0_1
When you’re done, you’ll have something like this:
We’re almost there, we still need to make our game object shootable so we can hit him.
Step 2.6: Set Zombie Layer to be Shootable
Straightforward, just like our Bandit, we did the day before, we want to go to our Zombie and change his layer to be Shootable.
Step 2.7: Attach the Fist Colliders to EnemyAttack
We attached our First Collider script to the Zombie’s weapons. It’s time to attach both of them to our EnemyAttack Script so we can detect when the weapon makes contact with the player in Attack().
In EnemyAttack set:
- Left Fist: Zombie_0_1_002
- Right Fist: Zombie_0_1_1
With this, we can take damage now.
Except for two problems:
- If we don’t move and let the enemy attack us, he just stops attacking us at one point!
- We don’t take damage when our enemy attacks!
Step 2.7: Allowing Enemies to Continuously Attack Us
Fixing the enemy not attacking problem turned out to be straightforward.
If we were to look at the Animator of our zombie when he attacks us, you’ll notice something interesting.
The zombie is in the right state attack state, but he only attacks us once.
One possible area to look at next is the animation clip: ZombieAttack.
Looking at it, the problem becomes apparent. Our Loop Time is not checked, which means that our animation won’t loop!
Check it and we’ll solve our first problem.
Step 2.8: Creating an Event in our Animation to Call Attack()
The next problem is that our player character doesn’t seem to notice when they get attacked.
If we were to do some print debugging, if we were to add a print statement inside our EnemyAttack script at Attack(), we’ll notice that we never call Attack()!
As you might recall, we call Attack() by setting an event in our animation! Our Bandit was using the same knight attack animation so we never had to run into the problem, however, our Zombie uses a new animation!
Let’s fix this:
- Select our Zombie game object
- In the Animation Tab (Window > Animation), select our ZombieAttack clip.
- Experiment around with what’s a good frame to add our event, but I chose frame 21
- After creating our event, set the function to run to be Attack()
When we’re done, this is what we’ll have:
With this added in, the Zombie can now damage us!
Step 3: Create a prefab
Now that we finished our Zombie enemy, the last thing we need to do is to add it to our Prefabs folder for our SpawnManager in the future!
We can just call our new prefab Zombie.
There we go! Now we have 2 more enemies that we can spawn in the game!
Here’s what we have now:
With all our new enemies, the next thing to work on is setting our SpawnManager to create new waves our enemy.
We’ll set that up next time! Until then!