In this lesson, we’ll make our bat chase the robot instead of the mouse cursor.
It will also make the follow mechanic a little more flexible.
In the previous lesson, we hard-coded a distance of 500
pixels to follow the mouse.
In this lesson, we’ll use an
instead.When the robot enters the area, we’ll keep track of it in a variable.
If the robot exits the area, we’ll reuse a trick from the tower defense
series: set the target variable to null
.
The logic for the bat is simple: if there is a target, the bat moves
toward it. Otherwise, if the target is null
, the bat moves
up to the ceiling.
Add an
named AggroArea to the bat scene with a as a child.Use a circular shape and set the radius. The circle’s radius determines when the bat detects the robot.
As in the DisappearingPlatform we made before, set the AggroArea’s collision mask to only detect the robot’s layer.
In the Bat script, we add the target
variable,
which we’ll use to store the robot and a reference to the we just made. Add these lines at the top:
extends KinematicBody2D
# Will store the robot when it enters the Area2D.
var target: KinematicBody2D
# The Area2D that detects the player.
onready var aggro_area := $AggroArea
Let’s look at the new logic in the _physics_process()
function.
func _physics_process(delta: float) -> void:
var direction := Vector2.UP
if target:
= to_local(target.global_position).normalized()
direction
var desired_velocity := max_speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity
= move_and_slide(velocity) velocity
You may notice we actually need fewer lines of code than before. Only the direction calculations changed.
You’ll want to replace the first four lines in your existing
_physics_process()
function.
func _physics_process(delta: float) -> void:
var direction := Vector2.UP
if target:
= to_local(target.global_position).normalized()
direction # ...
If there’s a target, the direction
changes to the
target’s position.
The function to_local()
is present in all nodes that
extend , like our area.
We use to_local()
to convert the robot’s global position
into an offset from the bat.
The function to_local()
returns a value so we can normalize it and get a
direction vector.
The rest of the _physics_process()
function is
unchanged!
Before, we used the mouse cursor’s position. Now, we track the robot.
But there’s still a missing piece in this puzzle. We need to set the
target
variable. For this, we’ll use signals.
We’ll use two signals from the
we made:body_entered
body_exited
These emit every time the area detects a physics body is entering or exiting the collision shape.
Connect them in the _ready()
function.
func _ready() -> void:
connect("body_entered", self, "_on_player_entered")
aggro_area.connect("body_exited", self, "_on_player_exited") aggro_area.
All that’s left is to create the functions.
func _on_player_entered(robot: KinematicBody2D) -> void:
# Sets the target to the robot when it enters the Area2D.
= robot
target
func _on_player_exited(robot: KinematicBody2D) -> void:
# When the robot exits the Area2D, the target is out of range.
= null target
And we’re done! The bat will now chase the robot when it’s close!
But notice how the bat will detect the robot even if the robot is behind an obstacle.
You can try to hide below a platform place between the bat and the robot. The bat will still move toward the character.
In the next lesson, we’ll explore how we can have the robot hide from the bat to make avoiding it easier and more enjoyable.
Here’s the current code for the bat so far.
extends KinematicBody2D
# Will store the robot when it enters the Area2D.
var target: KinematicBody2D
# The Area2D that detects the player.
onready var aggro_area := $AggroArea
export var max_speed := 300.0
export var drag_factor := 0.1
var velocity := Vector2.ZERO
func _ready() -> void:
connect("body_entered", self, "_on_player_entered")
aggro_area.connect("body_exited", self, "_on_player_exited")
aggro_area.
func _physics_process(delta: float) -> void:
var direction := Vector2.UP
if target:
= to_local(target.global_position).normalized()
direction
var desired_velocity := max_speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity
= move_and_slide(velocity)
velocity
func _on_player_entered(robot: KinematicBody2D) -> void:
# Sets the target to the robot when it enters the Area2D.
= robot
target
func _on_player_exited(robot: KinematicBody2D) -> void:
# When the robot exits the Area2D, the target is out of range.
= null target