Adding proximity detection so enemies can ram player
In this article we’ll tackle making a game object follow another game object if they are within a predetermined distance of each other.
As it turned out, moving the enemy towards the player was not very hard, however rotating the enemy game object to look at the player was quite the challenge.
Modifying our Player script
There isn’t much we need to do in our player script so we’ll get that out of the way.
Essentially, we just need to make we tell all of the enemies on screen when the player is died to avoid null reference errors when the enemies are trying to check the distance to a player that no longer exists.
To do this, we create an array on player death in our LoseLife() method that gets all existing enemies, stores them in array, then we go through all of them in a foreach statement and call the new public method we will add to the enemy script shortly, PlayerDied();
Modifying our Enemy script
We’re going to need some variables that let us know when the player is nearby, what direction the player is in, the distance we define as “nearby”, how fast we want to move towards them, if the player is already dead, and last a way to disable the enemy from firing when in ramming mode.
We only disable firing for the beam laser type enemy as it is to overpowered.
Connecting up with the changes we made in our Player script, we add the PlayerDied() method which will set our private variable, _playerDied to true on player death.
Next we’ll add the CheckPlayerDistance() method.
Here I want to know a couple of things.
First is the player within our specified range, _withinPlayerTrackingDistance, and if they are, is the enemy above or below the player.
When the enemy is above the player, I want it to track and try to ram the player.
When the enemy is below the player, I want it to go back to its normal movement behavior.
Unity makes the distance comparison quite easy with the Vector3.Distance method.
We simply give it the enemy object and the player object positions and it spits out the distance.
We then compare that distance to our _withinPlayerTrackingDistance variable.
If the player’s position.y (vertical) is less than the enemy position.y, _playerNearby is set to true.
There is an issue with our enemy beam laser firing type in that it is really powerful that while it tracks the player it is pounding the player with a constant beam.
Since that is not desired, I destroy the laser beam game object here if it exists.
If the player is above the enemy, _playerNearby is set to false, we reset the enemy rotation to face directly down, and make sure that if I’ve disable it’s firing due to being the beam laser type that we reactivate the firing functionality.
Also, if the player is not in the prescribed distance we do the same, because the enemy may be mid tracking when the player runs away and we want to reset at that time as well.
We also add a RamPlayer() method.
Some simple if then checks will tell us which direction we want our enemy to move in the new Vector3() variable we create, _towardsPlayerDirection.
This will be used for moving the enemy towards the player, but not rotating towards the player.
When it comes to rotating the player, I fell down and scraped my knee.
Why something so simple is such a headache in Unity, I don’t know.
It needs its own Vector3.distance equivalent in my humble opinion, where Unity handles the trigonometry behind the scenes.
I don’t know Trig, so that’s an issue.
Luckily, the great support @ GameDevHQ pointed me in the right direction and then held my hand all the way down it.
Basically, quaternions are hard.
//Rotate towards playerVector3 direction = _playerGO.transform.position - transform.position;Quaternion targetRotation = Quaternion.LookRotation(Vector3.forward, direction);direction.Normalize();float rot_z = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;transform.rotation = Quaternion.Euler(0f, 0f, rot_z + 90);
Basically, copy the code above, it does maths, and you do trial and error with the “90” value by changing it in 90 degree increments until the object turns the way it is supposed to.
I wish I could break this down for me and you.
I’m sure there are better ways to do this functionality and I will just have to wait until I learn them 😊
Lastly, we use transform.translate at the end of this method and move the enemy towards the player.
The Update() method brings it altogether.
We make sure the player is alive and if so, we call CheckPlayerDistance();
We check if the player is nearby AND alive AND this enemy is not dying.
We add the check if this enemy is dying because the explosion animation that plays would continue to still the track the player.
This way, the enemy dies, the explosion triggers, and it continues on its path as you would expect momentum to carry it.
Anyways, if ALL of that is right, we disable enemy firing and the enemy attempts to ram the player.
If not ALL of that is right, we move the enemy as normal.
If we haven’t disabled enemy firing, we fire the appropriate weapon as usual.
That’s it, now we have an enemy that can detect player proximity and try to ram him!