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 if we, the players, 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<sup> -> </sup>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 its 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, then we would set our boolean flag. Once again, this is exactly the same as - In
IsCollidingWithPlayer()
, we would give our bool _collidedWithPlayer
to whoever calls it. Note that this function is public
so other scripts 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!
Day 19 | 100 Days of VR | Day 21
Home
CodeProject
The post Day 20 Fixing Game Object Collider in Unity appeared first on Coding Chronicles.