Creating a Homing Projectile in Unity
Today’s core coding challenge will be a little math and physics heavy, but fear not! As always, we’ll be taking this one step at a time and breaking everything down as we go along.
Objective: Create a homing projectile that automatically targets the nearest enemy and homes in on it until it reaches impact.
Set up Global Variables
You will need two sets of global variables: The first set is for the targeting logic, and the second is for the movement physics.
Note: This script will go attached to the projectile/missile object.
- The target variable will be of type Transform because all objects have a transform and we’ll be using that to our advantage to find the specific objects we want to target.
- targets will be a GameObject array because this will be used to catch every object in the scene that has the enemy tag, which is how we specify the target(this will be shown later in the article).
- The Rigidbody2D is the Rigidbody component attached to the projectile object. Rigidbodies enable the use of physics in unity, and we will be using physics for the projectile movement logic. This variable is a SerializeField because we need to assign the projectile object’s Rigidbody in the Inspector. To do that, simply drag and drop the object with the attached Rigidbody into the Inspector.
- _distance which helps us determine the distance our projectile needs to traverse to get to the _closestTarget which is the object it will impact.
- _closestTarget is using Unity’s Mathf class, which is a collection of common math functions, some of which include trigonometric. Here we are using Infinity because we are searching for objects that can be in any of the infinite points of our scene, of course, we’ll be narrowing that down shortly.
- _speed: The speed at which the object will move in a linear trajectory.
- _rotationSpeed The speed at which the object’s pivot point will rotate in the direction of a given target. The picture below demonstrates better visuals.
Note: The previous values for speed have very high numbers, as you can see, this is neccesary for the values to cooperate with each other and generate enough force to make the object move accordingly. Otherwise, the projectile object can get stuck orbiting around the target rather than actually hitting it. Feel free to play with the values to see what works for you, but if you run into an issue with akward movements, it’s likely these values.
Finding the Closest Target
Now that all the variables are set up, it’s time to work on finding the closest target! This first void method is all about finding the closest target and specify what is getting targetted.
- Use your targets variable(the GameObject array), and set it to find specifically the objects with the “Enemy” tag on them. It can be any tag your target object has, it will work all the same.
Note: This method imploes that you have assigned tags to your objects. Tags are assigned in the Inspector by selecting the object and going to the top section.
2. Here we use a foreach loop because it will run for each enemy object found in our targets game object array.
- The _distance variable is set to be the enemy’s transform position, subtracted from the current projectile’s position.
- sqrMagnitude is actually allowing us to use the Pythagorean theorim(yes you’re actually using that outside of school!).
Let me break this down a bit further…
So, imagine that the target you want to hit is at position B in the following triangle, and you are at position C. To calculate the distance between the two points, you need to solve for the long side, or hypotenuse.
Unity is such a nifty tool, that all you had to do was use the simple sqrMagnitude!
2. For the final part of the foreach, we’ll be adding an if-statement to check if the _distance is less than the _closerDistance. If that condition is met, we assign our Target, which is the transform variable, to that of the enemy’s transform.
Note: Remember, an object’s transform carries all of its positional information. This is why this works.
Creating a Firing Method
Creating the firing method is mostly comprised of using Unity’s in-engine physics.
- In the Start, get the Rigidbody Component. You can also go ahead and add the findClosestEnemy function here as well.
Note: When getting a component, it’s always good to check if its null to avoid unncesary errors and issues.
2. Create a void method to fire the projectile. The first thing we’ll add to this method is the velocity of the projectile.
In physics, we recognize Velocity as a vector that contains both the direction and speed of an object in a given moment. So, this is exactly what will be specified here.
3. Next, we add an if statement to check if the Target array is not empty or not null.
- If this condition is met, we have a direction vector that takes care of substracting the Target’s position and the Rigidbody’s position(our projectile). This will make the distance between the object decrease causing it to start closing in.
- The direction vector is normalized. When we normalize a vector, it essentially means that we want the vector to keep the same direction — we want to keep it steady and unchanged. This will also make the vector into a unit vector, meaning its length is now 1.0(this does not affect the distance traveled).
4. Lastly, the method needs to actually set up the proper movement that the projectile will carry while traveling the required distance. Without setting this properly, as aforementioned, the projectile can end up wandering aimlessly or orbiting around targets rather than hitting them. Let’s take a look at this step by step.
- The roationValue is using Unity’s Vector3.Cross. The cross product of two vectors results in a third vector that is perpendicular to the two input vectors. Perpendicular means that its result will be a 90-degree line that spreads from the other two vectors.
- The angularVelocity of the HomingProjectileRB ,otherwise known as the projectile’s Rigidbody, is part of Unity’s in-engine physics system. This is the velocity vector of a Rigidbody and it’s measured in radians per second. We’ll be using the previously established rotation variables to manipulate this further.
- To close the if-statement, we set the velocity one last time. This needs to be both outside and inside the if-statement, because the projectile will move when fired regardless of whether there are targets or not. We need the if-statement block to modify that movement so that it moves properly toward the target when a target is found.
5. Add the firing method to FixedUpdate. We use FixedUpdate whenever our methods depend on unity in-engine physics.
Optional Out of Bounds Parameters
The final method you see within the firing method is just a deadZone parameter I set so that the projectile object is destroyed if it goes out of bounds. This only happens when there are no targets and the projectile is fired anyways, which means it would move indefinitely across the scene until destroyed. You can set your own parameters or use other methods to get rid of the object if you like. These are my parameters.
The Result
This has been part of a series of coding challenges from GameDevHQ! For the next, and final challenge from this set, we’ll be taking a look at a boss fight code with multiple phases!
Special thanks to Hiren Namera for helping figure out the kind of logic I should go with for this challenge! I love breaking down coding logic and look forward to learning more!