07.coding-a-turret-that-targets-weak-mobs

Coding a turret that targets weak mobs

We will now code a turret that aims at the weakest mob in range.

Whenever a mob enters or exits the turret’s collision shape, we want the turret to pick the one with the lowest health.

If you open the script we attached to mobs, TowerDefense/common/DraggableMob.gd, you will see that mobs have a health property.

This health property shows up in the Inspector when selecting one of the mobs in the scene.

In the scene, each mob has a different health value. When dragging a mob with low health into the tower’s range, we want the tower to target it.

Test your skills

Here’s a good exercise before moving on. Reopen Turret.gd and try to guess:

  1. Where we should bring a change.
  2. What this change should be.

Please take a moment to give it a thought.

It’s okay if you don’t find the solution. Thinking about how to code this new turret is a good exercise.

Once you’ve given it some time, you can move on.

Making the turret aim at weak enemies

Create a new script in the TowerDefense/ directory and name it TurretWeakestPicker.gd.

Assign it to the last turret, the TurretWeakestPicker node.

Open the script and replace its contents with a line to extend the Turret.gd script.

extends "Turret.gd"

The function we have to override is select_target().

In Turret.gd, the function always picks the first target. We want to change this, so the tower picks the weakest target.

If there are targets in range, we need to:

  1. Start with a default target. It will be the first, like previously.
  2. Loop over all the other potential targets.
  3. Compare each potential target’s health to the default target’s health.
  4. If the potential target’s health is lower, it replaces the default target.

Here’s the override we need for the select_target() function.

func select_target() -> void:
    if target_list:
        # We start with a default target to compare against.
        target = target_list[0]
        # We then loop over all targets in the list to compare their health
        # against the current target.
        for potential_target in target_list:
            # If the potential target has lower health, it replaces the current
            # target.
            if potential_target.health < target.health:
                target = potential_target
    # This part is the same as in Turret.gd, if there are no potential targets,
    # we unset the target.
    else:
        target = null

Because the loop runs for each item in the target_list array, we know that we always pick the weakest possible target.

You may have noticed that we check the first target in the loop, which means the first loop iteration compares the target_list[0] against itself.

This isn’t a useful operation, but avoiding this comparison would make the code a little harder to read at no benefit.

We would have to introduce an extra condition, like so.

for potential_target in target_list:
    if target != potential_target:
        # ...

It would also worsen performance, because we would have to introduce one instruction per target to avoid a single comparison.

In this case, the code is slightly simpler and faster if we keep it “dumb.”

You can run the scene and drag a couple of mobs into the turret’s range to see how it aims at the weakest one.

We included health bars to show which mob has the lowest health.

In the next lesson, we’ll build upon what we did so far to create a tower that slows down enemies in range.

The code

Here is the complete code for TurretWeakestPicker.gd.

extends "Turret.gd"


func select_target() -> void:
    if target_list:
        # We start with a default target to compare against.
        target = target_list[0]
        # We then loop over all targets in the list to compare their health
        # against the current target.
        for potential_target in target_list:
            # If the potential target has lower health, it replaces the current
            # target.
            if potential_target.health < target.health:
                target = potential_target
    # This part is the same as in Turret.gd, if there are no potential targets,
    # we unset the target.
    else:
        target = null