In this lesson, we’ll add another hazard for the player to deal with: the terrifying cave bat.
We’ll add features as we move through the lessons as we did with the robot character. In the next three lessons, we’ll progressively enhance the bat’s functionality:
If the robot is near the bat, the bat will chase it away unless the player hides. Otherwise, the bat will return to the ceiling of the cave.
The bat will be a
too, just like the robot. This is because we want the bat to interact with other physics bodies like walls and the player.Rather than take input from the keyboard like the robot, we’ll
control the bat’s movement with calculations in the
_physics_process()
function. Its movement will depend on
what it can see and if the robot is near.
Let’s get creating!
Create a new scene with a
as the root and name it Bat.Find the BatSkin scene inside the BatSkin/
directory and create an instance of the scene.
Like the robot’s skin, the BatSkin will automatically choose
the right animation depending on what the bat is doing. You’re
encouraged to look around the BatSkin.tscn
scene to see how
it works, but we created this to save some time, so we won’t go over it
in detail.
Our CollsionShape2D
. Please give it a circular shape.
Save the scene in the SideScroller/
directory, attach a
script to the Bat node, and we’ll get coding.
We’ll return to basic steering logic for the bat. Let’s look at the
_physics_process()
before breaking it down. You may
recognize much of the logic from one of the earlier series: To Space
and Beyond.
func _physics_process(delta: float) -> void:
# We start by calculating the bat's movement direction.
var direction := Vector2.UP
var relative_cursor_position := get_local_mouse_position()
# If the distance to the mouse is less than 500 pixels,
if relative_cursor_position.length() < 500:
# We convert the mouse's local position into a direction vector by
# normalizing the position.
= relative_cursor_position.normalized() direction
The bat is always in one of two states:
The direction
starts facing upwards by default. We get
the mouse cursor position relative to the bat’s position using
get_local_mouse_position()
. If the length is less than
500
pixels, the bat is close enough to move toward the
cursor.
Below, the yellow line shows the cursor’s position relative to the bat, and the pink shows the mouse cursor’s global position (relative to the game world’s origin).
The normalized()
function shortens the
relative_cursor_position
to a length of 1
. We
do this because the vector would scale the velocity
too
much without that, and our bat would fly around very fast!
It’s time for steering!
At the top of the script, add the necessary properties:
export var max_speed := 450.0
export var drag_factor := 0.1
var velocity := Vector2.ZERO
And add this in the _physics_process()
function. This is
code you’ve seen before, but here is a reminder.
func _physics_process(delta: float) -> void:
# ...
var desired_velocity := max_speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity
= move_and_slide(velocity, Vector2.DOWN) velocity
The desired_velocity
is the most efficient velocity to
reach the target. It’s either straight upwards at maximum speed or in
the direction of the mouse cursor if it’s near.
The steering_vector
is the difference between the
desired_velocity
and the current velocity
this
frame.
If we added steering_vector
to velocity
,
the bat would instantly move toward the desired position. Instead, we
use the drag_factor
value to change the bat’s direction
gradually.
Because the drag_factor
is very low, the bat is slow to
turn and accelerate. Increasing the drag_factor
will make
the bat more reactive.
We often use low values for enemies to give the player enough time to escape or avoid them.
You can now add the bat to the Level scene to play around with it yourself. Make sure to draw a ceiling with the
, so it has a place to rest!We’ve made the first steps to make the enemy, but there’s much to do! In the next lesson, we’ll make the bat target the player instead of the mouse.
Here’s the current code for the bat so far.
extends KinematicBody2D
export var max_speed := 450.0
export var drag_factor := 0.1
var velocity := Vector2.ZERO
func _physics_process(delta: float) -> void:
# We start by calculating the bat's movement direction.
var direction := Vector2.UP
var relative_cursor_position := get_local_mouse_position()
# If the distance to the mouse is less than 500 pixels,
if relative_cursor_position.length() < 500:
# We convert the mouse's local position into a direction vector by
# normalizing the position.
= relative_cursor_position.normalized()
direction
var desired_velocity := max_speed * direction
var steering_vector := desired_velocity - velocity
+= steering_vector * drag_factor
velocity
= move_and_slide(velocity, Vector2.DOWN) velocity