As previously mentioned in my last article, I created a new weapon type called a Drone Shot. This weapon type can not miss! It will find the location of the nearest enemy and track it down until it is destroyed. This weapon also does not consume ammo because it is not a laser, but a mini version of the player ship. Here is what it looks like:
I learned a lot creating this feature, especially how physics and math can be used to create a behavior. This means I have a lot to explain but I will try to be concise!
The first thing I did was create a drone_shot prefab just like the other weapons and then I got right into coding. Istarted off on my DroneShot script by finding a way to identify the closest enemy ship in relation to the drone. This was pretty straightforward for me after I did some research on finding specific game objects in the scene. Here is my method FindNearestEnemy():
The first line in this method identifies all the enemies in the scene and puts them into an array. Then we have the local variables where closestEnemy is equal to null because this gets assigned in our foreach loop, closestEnemyPosition which is set to infinity which we will get more into later, and lastly, we have ourPosition, which is the position of the drone.
A foreach loop goes through each item in an array which is our enemies. The first two lines of code in the foreach loop compute the distance between the drone and the current enemy in the enemies array which is currentEnemyDistance. If currentEnemyDistance is less than closestEnemyDistance, then this enemy in the array becomes the new closestEnemy and this distance becomes the closestEnemyDistance. This explains why closestEnemyPosition was initially set to infinity. After the foreach loop is finished, the closestEnemy is returned which can be used else where in the script.
This method is pretty intensive so it should not be called in the update method but in a coroutine. I have my drones checking for the closest enemy every 2 seconds and it works perfectly fine.
_nearestEnemy is a global variable that is defined in the coroutine and used in the other method, GoToEnemy().
I had a lot of trouble making my GoToEnemy() method work. I tried forums, Unity’s API, but I just couldn’t visualize what I had to do. Even with the solutions I did find, the rotation of the drone still wasn’t how I wanted. Then I found this video by good old “Brackeys” that uses physics and rigidbodies to create a homing missile. This is my final GoToEnemy script:
I took a lot of time after watching to actually understand what’s going on here, but I would recommend watching the video linked above because the visualization of the vectors is great.
After the coroutine is called, there is a null check to see if there is even an enemy on-screen and if there is not, the drone stays in the same place. Then we find the direction by subtracting the drone position from the _nearestEnemy position. _rb is the variable for the RigidBody2D component on the drone. Since it is a rigidBody2D, the position is a Vector2 which is why target.position is converted into a Vector 2 as well. Then the direction vector’s magnitude is set to 1 with Normalize();
Now we will find the cross product of this vector with the vector transform.up (0,1,0). transform.up is used drone will always be going “up”, or going in the direction it is facing. When these two vectors are crossed, it will create another vector whose z component will be the rotateAmount. This value will get smaller and smaller as the drone becomes closer to the enemy. This allows our rotation to be dynamic and slow down as it nears the enemy. This rotateAmount is multiplied by my _rotateSpeed variable to determine the angular velocity of the drone. Then velocity is calculated as usual.
This sounds very complicated, but if you are interested in how it works, I would recommend doing this problem with simple vectors on a piece of paper as I did and watch the video, specifically 2:40–4:19. This is where the vector visuals are.
Now my enemies need to worry about two ships!
End of GDJ 22!