Welcome back to another exciting day of Unity development at day 20! Can you believe it? We’re 1/5 of the way to 100 days! I’m hoping that the momentum will keep me going forward until I ship something!
Now looking back at day 19, we created a health system that we can now use, however we encountered a problem.
For some reason, our collider only hits the enemies once even if we go through the attack animation multiple times.
Today we’ll be solving that problem. So, let’s get to it!
Fixing Our Mesh Collider
The problem lays with the Mesh Collider that we created for our knight on Day 12. Remember this?
That standing mesh collider, is literally what we’re colliding with every time the Knight attacks us.
The problem is that Mesh Colliders can’t follow animations of the model, as a result when the knight first touches us, the mesh might touch us, but afterwards it we, the player, don’t get pushed back by the collider, the mesh will never touch us again.
Here’s a picture to demonstrate the problem:
While the knight model itself might be touching us, our Mesh Collider is static and doesn’t move.
This isn’t good! We want to damage our player when the knight’s fist contacts the player! Not this static mesh collider!
Some changes are going to be needed to fix this problem.
- We still need our Mesh collider. This prevents us from walking through our enemy. Right now, we have a problem where we can walk over the knight, but we’ll fix this now.
- We’re going to add a Box Collider to the hands of our Knight. This way, we’ll know for sure when our player gets punched.
Fixing the Mesh Collider for the Knight
Right now, we can’t move our mesh, because it’s in the parent body. According to this post about incorrect mesh positioning, we will make a new empty object and add the mesh collider there.
This works for us, because we only need the mesh for collision detection and for our raycasting for shooting.
Let’s do it!
Originally, we put the Mesh Collider in our knightprefab game object. Let’s remove that now.
Select knightprefab, right click, and select Create Empty, to make a new Game Object. Let’s rename this object to Collider.
Select Collider and add a new Mesh Collider component to it. For the Mesh, select body to get our Knight model back.
You might have to move Collider around yourself to get the collider to match our knight, however, at the end you should have something like this:
Now with this, we can’t just walk over the knight.
Adding new Colliders for our Fists!
Now that we have solved the Mesh Collider problem, we’re going to fix our original problem with dealing damage when the enemy attacks us.
To do this, we’re going to add box colliders, just to the fists of our knights. Specifically, in knightprefab -> hips -> spine -> chest -> L_shoulder/R_shoulder -> all the way down to L_Wrist/R_Wrist.
For each box collider, I made the scale of the box to be 0.25 to fit the size of the hand.
Now that we have this, we need to attach a script to each of our hand models that can detect the collision. Scripts from parent objects can’t detect collision for it’s children.
It’s also important that our original script: EnemyAttack, stays where it is, because we need access to the animator component that’s located in the parent.
To solve our problem, we’re going to move the collision part of our code from EnemyAttack to a new script called FistCollider.
FistCollider will deal with the collision of the knight fists and we’ll get the results in our EnemyAttack script.
Here’s our FistCollider script:
using UnityEngine;
public class FistCollider : MonoBehaviour
{
private GameObject _player;
private bool _collidedWithPlayer;
void Start()
{
_player = GameObject.FindGameObjectWithTag("Player");
_collidedWithPlayer = false;
}
void OnCollisionEnter(Collision other)
{
if (other.gameObject == _player)
{
_collidedWithPlayer = true;
}
print("enter collided with _player");
}
void OnCollisionExit(Collision other)
{
if (other.gameObject == _player)
{
_collidedWithPlayer = false;
}
print("exit collided with _player");
}
public bool IsCollidingWithPlayer()
{
return _collidedWithPlayer;
}
}
Here’s the flow of the code:
- Like our EnemyAttack script, we want access to our player game object and we want to check if we collided with the enemy.
- In Start() we make sure to instantiate both of our variables.
- In OnCollisionEnter() and OnCollisionExit(), if whatever we collided with is the player than we would set our boolean flag. Once again this is exactly the same as
- In IsCollidingWithPlayer() we would give our bool _collidedWithPlayerto whoever calls it. Note that this function is public so other script can have access to this. We’re going to use it later.
Let’s attach the script to both L_Wrist and R_Wrist.
Now that we moved part of our collision code from EnemyAttack to FistCollider, let’s use FistCollider in EnemyAttack. Here’s the code:
using UnityEngine;
public class EnemyAttack : MonoBehaviour
{
public FistCollider LeftFist;
public FistCollider RightFist;
private Animator _animator;
private GameObject _player;
void Awake()
{
_player = GameObject.FindGameObjectWithTag("Player");
_animator = GetComponent<Animator>();
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject == _player)
{
_animator.SetBool("IsNearPlayer", true);
}
print("enter trigger with _player");
}
void OnTriggerExit(Collider other)
{
if (other.gameObject == _player)
{
_animator.SetBool("IsNearPlayer", false);
}
print("exit trigger with _player");
}
private void Attack()
{
if (LeftFist.IsCollidingWithPlayer() || RightFist.IsCollidingWithPlayer())
{
_player.GetComponent<PlayerHealth>().TakeDamage(10);
}
}
}
Here’s the changes in our code:
- First, we create new public variables: LeftArm and RightArm that are the FistCollider script that we just created.
- We cleaned up a lot of the code, specifically the Collision part and anything that had to do with it.
- In Attack() we use our new FistCollider to make collisions with the player and then when the attack event from our animation gets fired, we check if the player gets hit. If they do (meaning the knights fist collided with the player), the player will take damage.
With our script done, the final thing that we need to do is to make sure that we attach the FirstCollider object to our player.
We can directly attach our game object into the spot and the script will be automatically selected.
When you’re done, we should have something like this:
Conclusion
Phew that was a lot of work!
In these past 2 days, we created the health system and we re-visited some of the existing problems with the enemy knight’s mesh collider such as walking over them and not accurately colliding with the player.
We also separated out the code with attack collision work while still making our original collision script work as normal.
Now that we finally have health for our player, tomorrow, the next thing we’ll do is start working on an end game state for when the player’s health reaches 0.
Until then, have a nice evening, I’m going to sleep!
Source: Day 20
Visit the 100 Days of Unity VR Development main page.
Visit our Homepage