08.slowing-down-mobs

Making a turret that slows down mobs

In the previous lessons, we learned:

  1. How to detect mobs entering a turret’s range.
  2. How to make the turret turn to face the mob and shoot at it.
  3. How to make the turret handle multiple enemies.
  4. How to create new kinds of turrets derived from the same base.

In this lesson and the next, we’ll build upon these techniques to create a turret that slows down mobs and another that fires homing missiles.

Then, we will place the turrets in a Tower Defense level, with enemies walking along a path.

We will provide the enemies and the code that makes them walk along the path, as explaining these is outside the scope of this series.

If you want to learn how they work, please open them and see if you can understand them. We included many comments to help you make sense of the code.

For now, let’s create the turret that slows down mobs.

Creating an inherited scene

Like scripts, we can extend scenes in Godot. The inherited scene receives all the changes to nodes and properties in the parent scene.

We will use this scene inheritance to change the look of our slow-down turret.

Find the scene file TowerDefense/Turret.tscn, right-click on it, and select New Inherited Scene.

A new scene will open with some nodes greyed out. Save it as TowerDefense/TurretSlowingDown.tscn. We also want to rename the root node to keep things consistent.

Any change in Turret.tscn will also affect TurretSlowingDown.tscn. Also, you can add nodes and change properties in TurretSlowingDown.tscn, and they will override the default values from Turret.tscn.

Note: Contrary to code, which only uses text, scenes are complex beasts, so scene inheritance has some limitations. Changing the structure of the parent scene can cause losses in the inherited scene. We recommend ensuring that the parent scene is complete before creating inherited scenes.

We can now change the look and script of our new turret.

Let’s begin with the texture. Locate the image tower_area.png, and assign it to the Sprite.

Finally, let’s extend the script. Create a new script file in the TowerDefense/ directory and call it TurretSlowingDown.gd. Assign it to the TurretSlowingDown node.

We’re done with the setup! Let’s write our code.

Coding the slowing mechanic

Open the TurretSlowingDown.gd script and replace its content with this line:

extends "Turret.gd"

Here’s what we want to change in this script:

  1. We don’t want the turret to shoot rockets.
  2. We don’t want it to rotate towards targets.
  3. When a mob is in range, its movement speed should decrease. We also want to change its color.
  4. When a mob leaves the turret’s range, we should restore its movement speed and color.

First, we override the _on_Timer_timeout() function and make it do nothing. That way, the turret does not shoot anymore.

func _on_Timer_timeout() -> void:
    # We don't want to shoot, so we override the function to do nothing.
    return

Similarly, in the _ready() function, we turn off physics processing so that the turret does not turn.

func _ready() -> void:
    set_physics_process(false)

We then store our slow-down factor and tint as constants. That way, we put a name on the script’s two important values.

# The amount by which we divide the speed of mobs in range of the turret.
const SPEED_DIVIDER := 2.0
# We use this color to tint the mobs in range.
const COLOR := Color(0.4, 0.6, 1.0)

Defining a color

We’re using a color value for the first time. Like Vector2, the Color is a type built into the Godot engine.

It represents a color using three values: an amount of red, green, and blue.

Each value ranges from 0.0 to 1.0. For example, pure red is Color(1.0, 0.0, 0.0), while pure green is Color(0.0, 1.0, 0.0).

You can right-click on the term Color in the script editor and select Pick Color.

This will open a color picker in which you can preview the tone that you chose or select a new one.

Calling code from the parent’s script

We can now use those values to slow down and change the color of mobs that enter. Let’s start with _on_body_entered():

func _on_body_entered(body: PhysicsBody2D) -> void:
    pass

By overriding the function, we removed the instructions from Turret.gd. We could copy over what we had in the parent script:

func _on_body_entered(body: PhysicsBody2D) -> void:
    targets.append(body)
    select_target()

However, that would duplicate code. If we wanted to change it later, we’d have to remember to update two different scripts.

Instead, we want to run the code of the parent function.

Luckily, most programming languages have a way to call the parent of the function we’re overriding.

In GDScript, the syntax is to write a dot before the function’s name.

func _on_body_entered(body: PhysicsBody2D) -> void:
    # Call the function _on_body_entered() from Turret.gd
    ._on_body_entered(body)

Notice the dot. It’s essential! It tells Godot to call the function in the parent script.

The function will call itself infinitely without the dot, and the program will freeze.

This principle is called “super.” We say that we call “the super function.” Other languages often actually use a function or keyword named super instead of a dot.

With that done, we can extend the function with our new functionality.

func _on_body_entered(body: PhysicsBody2D) -> void:
    # ...
    # We reduce the mob's speed and change its tint.
    body.speed /= SPEED_DIVIDER
    body.modulate = COLOR

The last line assigns a color to the modulate value of the body.

You can change this value manually by selecting any 2D node and going to the Visibility -> Modulate property in the Inspector. Try changing the modulate value of the turret and notice it tints its sprite.

If you leave your mouse a second over the color, it will give you the red, green, and blue color values.

Let’s complete our code by restoring the mobs’ speed and color in _on_body_exited().

func _on_body_exited(body: PhysicsBody2D) -> void:
    ._on_body_exited(body)
    # We restore the mob's original speed and remove the tint.
    body.speed *= SPEED_DIVIDER
    body.modulate = Color(1.0, 1.0, 1.0)

The default modulate value that restores the color is white, or Color(1.0, 1.0, 1.0).

Our turret is complete. You can test it by opening TowerDefense.tscn. Drag and drop the TurretSlowingDown.tscn scene file to place the turret on the map.

Run the scene to see how the turret slows down mobs around it.

In the next lesson, we’ll code our final turret: one that shoots homing missiles.

The code

Here’s the final code for TurretSlowingDown.gd.

extends "Turret.gd"

# The amount by which we divide the speed of mobs in range of the turret.
const SPEED_DIVIDER := 2.0
# We use this color to tint the mobs in range.
const COLOR := Color(0.4, 0.6, 1.0)


func _ready() -> void:
    set_physics_process(false)


func _on_Timer_timeout() -> void:
    # We don't want to shoot, so we override the function to do nothing.
    return


func _on_body_entered(body: PhysicsBody2D) -> void:
    # Call the function _on_body_entered() from Turret.gd
    ._on_body_entered(body)
    # We reduce the mob's speed and change its tint.
    body.speed /= SPEED_DIVIDER
    body.modulate = COLOR


func _on_body_exited(body: PhysicsBody2D) -> void:
    ._on_body_exited(body)
    # We restore the mob's original speed and remove the tint.
    body.speed *= SPEED_DIVIDER
    body.modulate = Color(1.0, 1.0, 1.0)