Ricochet Laser in Unity as a New Weapon Type
This might be a bit of a longer one so I’ll just get right into it.
First things first, we need to determine the logic of the laser so we can translate that into code. So let’s break down what we need it to do step by step.
- When player presses Fire, the Laser_Rico travels in the direction its own Transform.up
- The Laser_Rico collides with an enemy and searches to see if there are other enemies on screen
- If there are no other enemies on screen then Destroy itself
- else, rotate so that its Transform.up is facing the next nearest enemy
Not too bad. So let’s translate these statements into code starting with Step 1. Pretty straight forward, it would look like this:
That’s honestly Step 1 done.
Step 2 can be done using a Physics.OverlapSphere() within a collision event method such as OnCollisionEnter() or OnTriggerEnter() depending on how you set up your enemies. I’m using 3d colliders as triggers with SpriteRenderers because I didn’t feel like changing all of the colliders to 2d, but the 2d equivalent would be a Physics2d.OverlapCircle(). So, in my case it would be this:
FindNearestEnemy() is a method that I learned from a friend of mine ages ago which returns the nearest object in an array, so let’s take a look as to how it works.
Let’s start from the top and go down. First at Line 41, the method takes a Collider array and the Collider of the enemy that was hit.
Naturally, the collider of the enemy that we just hit would be the closest out of every other collider in the scene so we want to make sure that we omit it in our search. Otherwise, it will always be the Collider of the enemy that was hit.
Line 43 is where we’ll be keeping our BEST result.
Let’s go ahead and imagine if you were to compare a list of numbers manually in order to find the lowest value. Let’s say the list is [8,9,4,5,3]. As a human, how would you do this? Well, if you’re reading from left to right then you would look at 8 and say to yourself, “since I don’t have any other number to compare 8 with at the moment then 8 is the smallest so far”. So 8 is our current BEST result. Now, with 8 in our memory we would look at 9 and ask ourselves, “is 9 smaller than 8? No it’s not so 8 is still our BEST value”. Next we look at the 4 and ask ourselves the same thing, “is 4 smaller than 8? Yes it is so now the BEST value is 4.” This process would continue until you reach the end of the list, which would then give your BEST result of 3.
Lines 45–54 are the process that I described translated into code with a couple of specifics laced in there. Mainly on Line 47
This statement is what prevents the initial enemy that was hit from being returned as the nearest object.
Once the foreach loop completes then Line 55 returns our BEST result. So now if we go back to the OnTriggerEnter() script we need to implement Step 3. Which looks like this:
We’re just checking to see if nearest ended up being nothing or the enemy that we initially hit, in both cases that means that we didn’t find any other enemies so we can safely Destroy the Laser_Rico because it doesn’t have anything to do.
For Step 4, let’s take a look at how we can calculate the required angle of rotation in order for the Laser_Rico’s transform.up to be facing the next nearest enemy. Here’s a zoomed in look at the CalculateRotationAngle() method.
In our case, the target would be the nearest enemy that we got from the FindNearestEnemy() method. The math is simple enough to follow so I’ve underlined some important points that you need to be aware of.
When calculating angles, Mathf() will calculate them in Radians and since you want to convert them to Degrees for your transform.rotation, then you must convert back to Degrees by multiplying the result by the Mathf.Rad2Deg constant.
Why are we subtracting the angle by 90? That’s because we’re traveling by our transform.up, which is technically at pi/2 or at 90 degrees. Mathf.Atan2() will calculate the angle needed to rotate the laser based off of Radian 0 which is the equivalent of transform.right. So we’ll actually get something like this. Where the green line is transform.up and the red line is transform.right:
In order to rotate clockwise we need to subtract the necessary angle on the desired axis and since we know the difference between transform.right and transform.up is 90 degrees then we can simply subtract 90 from the z axis and we get this:
And to finish up Step 4 we rotate the Laser_Rico by the angle we just calculated on the z axis.
Done! The full script looks like this:
Hope that helps! ^_^b