This is the fourth installment of a tutorial series about creating a simple tower defense game. It adds a mortar tower that launches shells that detonate on impact.

This tutorial is made with Unity 2018.4.4f1.

Add the mortar prefab to the factory's array, so it becomes possible to place mortar towers on the board. At this point they don't do anything yet though.

Next, create a prefab for the mortar tower. You can do that by duplicating the laser tower prefab and replacing its tower component. Then get rid of the tower and laser beam objects. Rename the turret to mortar, move it down so it sits on top of the base, give it a slightly gray color, and hook it up. Again, we can keep the mortar's collider fixed, in this case by using a separate object with just the collider superimposed on the mortar's default orientation. I set its range to 3.5 and shots-per-second to 1.

Placing a mortar tower currently fails, because we don't have a prefab for it yet. Begin by creating a minimal MortarTower type . Mortars have a fire rate, for which we can use a shots-per-second configuration field. Besides that we need a reference to the mortar so that we can aim it.

Now Game has to keep track of what kind of tower should be toggled. We'll simply associate each tower type with a number. The laser tower is 1, which is also the default, while the mortar tower is 2. Pressing the number keys will select the corresponding tower type.

That introduces a new possibility: a tower gets toggled while one already exists, but they are of different types. Currently that just removes the existing tower, but it makes more sense that it gets replaced with the new type, so let's do that instead. As that keeps the tile occupied pathfinding isn't needed when this happens.

It makes sense to return the most specific type, so ideally the new Get method's return type should be Tower . But the private Get method used to instantiate the prefab returns GameTileContent . We could either perform a cast here, or make the private Get method generic. Let's do the latter.

Next, adjust GameTileContentFactory so it can produce a tower of a desired type. We'll do that with a tower array and adding an alternative public Get method that has a TowerType parameter. We can use assertions to verify that the array is set up correctly. The other public Get method is now only for non-tower tile content.

As we'll create a class for each tower type, add an abstract getter property to Tower to indicate its type. This works the same as the shape behavior type from the Object Management series.

To make it possible to select which kind of tower gets placed on the board we will introduce a TowerType enumeration, just like GameTileContentType . We'll support the existing laser type and the mortar type that we'll create later.

Adjust the duplicate class so it becomes LaserTower that extends Tower and uses the functionality of its base class, getting rid of the duplicate code.

Acquiring and tracking a target is functionality that any tower could use, so we'll put that in an abstract base class for towers. We'll simply use Tower for that, but first duplicate it for later use as the concrete LaserTower . Then remove all code specific to the laser from Tower . A tower might not track a specific target, so also remove the target field and change AcquireTarget to use an output parameter and TrackTarget to use a reference parameter. Then remove the target visualization from OnDrawGizmosSelected , but keep the targeting range because that applies to all towers.

Laser aren't the only kind of weaponry that we could mount on a tower. In this tutorial we'll add a second tower type that lobs shells that explode on impact and damage all nearby enemies. To make that possible we have to support more than one type of tower.

We don't track targets in between launches, but we have to properly align the mortar when firing. We can use the horizontal launch direction vector to horizontally rotate the mortar by using Quaternion.LookRotation . We also have to incorporate the launch angle, by using `tantheta` for the Y component of the direction vector. That works because the horizontal direction has a length of 1, thus `tantheta=sintheta`.

And we're not firing every frame. Keep track of the launch progress just like the spawn progress for enemies and acquire a random target when it's time to launch in GameUpdate . But there might be no target available at that time. In that case we keep the launch progress, but don't let it accumulate further. In fact, to prevent an infinite loop we should set it to slightly less than 1.

Now that our trajectory calculation is correct we can get rid of the relative test targets. Instead, Launch should be provided with a target point.

However, due to floating-point precision a target very close to the maximum range might fail. So we should add a tiny amount to the range when calculating the required speed. Also, the enemy collider radius effectively extends the maximum tower range. We set it to 0.125 subject to at most doubling due to enemy scale, so increase the effective range by a further 0.25, to something like 0.25001.

We only need to figure out the required speed when the mortar awakens, or when we adjust its range while in play mode. So keep track of it with a field and calculate it in Awake and OnValidate .

At maximum range, `r=0` so there is only one solution for `tantheta`, which is a low trajectory. This means that we know the required launch speed `s=sqrt(g(y+sqrt(x^2+y^2)))`.

We can avoid this situation by using a high enough launch speed. But if it becomes too high then nearby targets would require very high trajectories and flight times to hit, so we want to keep the speed as low as possible. Our launch speed should be just enough to hit a target at maximum range.

Their trajectories are now complete, but the other two have disappeared. That happened because the launch speed is now insufficient to reach those points. There are no solutions for `tantheta` in those cases, which means that we end up with the square root of a negative number, leading to not-a-number values which cause our lines to disappear. We can detect that by checking whether `r` is negative.

If we want to reach the nearest two points within a second then we'd have to reduce the launch speed. Let's set it to 4.

The two farthest points can be reached in less than a second, so we see their entire trajectories and the lines continue a bit below the ground. The other two points require larger launch angles, which leads to longer trajectories that take more than a second to traverse.

There are two possible launch angles because it's possible to aim either low or high. A low trajectory is faster as it's closer to a straight line to the target. But a high trajectory is more visually interesting so we'll go for that. This means that we only need to use the largest solution, `tantheta=(s^2+sqrt(s^4-g(gx^2+2ys^2)))/(gx)`. Calculate that and also `costheta` and `sintheta`, because we need those to derive the launch velocity vector. We have to convert `tantheta` to a radian angle for that, via Mathf.Atan . Let's initially use a fixed launch speed of 5.

We launch the shell such that its flight time `t` is exactly long enough that it reaches its target. As the horizontal displacement is simplest, we can express the time using `t=d_x/v_x`. At the destination `d_x=x`, thus `t=x/(scostheta)`. This means that `y=xtantheta-(gx^2)/(2s^2cos^2theta)`.

Velocity `v` defines distance per second, so multiplying a velocity and a duration `t` gives you a distance `d=vt`. When acceleration `a` is involved velocity isn't constant. Acceleration is velocity change per second, thus distance per squared second. The velocity at any given time is `v=at`. In our case we have constant acceleration `a=-g`, so we can halve that to get the average velocity and multiply that with the time to find the displacement `d=(at^2)/2` caused by gravity.

The displacement `d` of the shell is aligned with the targeting triangle and can be described with two components. The horizontal displacement is straightforward `d_x=v_xt` where `t` is the time since launch. The vertical component is similar but also subject to negative acceleration due to gravity, so `d_y=v_yt-(g t^2)/2`.

The next step is to figure out the angle at which the shell must be launched. We have to derive that from the physics of the shell's trajectory. We won't consider drag, wind, or any other kind of interference, only the launch velocity `v` and gravity `g = 9.81`.

Now the `x` of the targeting triangle is equal to the length of the 2D vector pointing from the base of the tower to the target point. Normalizing that vector also gives us an XZ direction vector that we can use to align the triangle. We can show that by drawing the bottom of the triangle with a white line, derived from the direction and `x`.

In general the target can be anywhere within range, so the Z dimension also plays a role. However, the targeting triangle remains 2D, it just gets rotated around the Y axis. To illustrate this we'll add a relative offset vector parameter to Launch and invoke it with four XZ offsets: `[[3],[0]]`, `[[0],[1]]`,`[[1],[1]]`, and `[[3],[1]]`. The the target point becomes equal to the launch point plus that offset, with its Y coordinate then set to zero.

Using this line we can define a right triangle. Its top point sits at the mortar's position. Relative to the mortar that's `[[0],[0]]`. The point below at the base of the tower is `[[0],[y]]` and the point at the target is `[[x],[y]]`, where `x` is 3 and `y` is negative mortar's vertical position. We have to keep track of these two values.

Add a GameUpdate method to MortarTower that always invokes a Launch method. Instead of launching an actual shell, for now we'll visualize the math involved. The launch point is the mortar's world position, which is a little above the ground. Place the target point three units further along the X axis, and set its Y component to zero as we always aim at the ground. Then show the points by drawing a yellow line between them, by invoking Debug.DrawLine . The line will be visible in the scene view for one frame, which is enough because we draw a new line every frame.

To aim a mortar you have to both point it toward the target horizontally and then adjust its vertical orientation so the shell lands at the correct distance. We begin with the first step, initially using fixed relative points instead of moving targets to make it easy to verify that our calculations are correct.

A mortar works by firing a projectile at an angle, so it gets lobbed over obstacles and hits its target from above. Typically, shells are used that detonate on impact or while they are still above their target. To keep it simple we'll always aim at the ground, so shells will detonate once their elevation has been reduced to zero.

Shells