In this lesson, we will take a break from altering the bat scene for a moment. Instead, we’ll look at a toy that focuses on the next mechanic we’re going to implement: making the bat “see” the robot.
We’ll be making a basic version of the bat, which looks in the direction the cursor is pointing. If we point at the robot, the bat can see it, and it looks angry. In a later lesson, we’ll combine this functionality with the movement we programmed earlier.
Looking at the hiding behavior on its own might help you understand how it works when integrated into the bat scene.
To give a bat a line of sight, we will use a
node.It casts out a ray in a straight line and stops when it detects a physics object.
This works a little differently than in the real world. Rather than using light to see, it’s like a hand that reaches out and records the first thing it touches.
This is a slow visualization to help you see what the
simulates. It casts a line to a point and records the closest physical object the line intersects.In this example, it’s the robot, which makes the bat unhappy.
Let’s see this
node in action.Open the SideScroller/BatLookToy/
folder, where you’ll
find some pre-made scenes to work on for this chapter.
Open the BatLookToyRoom.tscn
scene. It has an instance
of a simplified bat and the robot to move around.
If you press F6 to run the scene, nothing happens.
Open the BatLook.tscn
scene. It has a as the root node. The
LookDirection
scene is a helper by GDQuest to help us
visualize raycasts.
Add a
to the scene.Let’s inspect the
node’s properties in the Inspector.The Cast To property is the point the ray tries to reach relative to the raycast’s position.
We set it to Vector2(1000, 0)
to make it face right by
default. In code, we’ll rotate the raycast to follow the mouse
cursor.
A vector pointing to the right corresponds to an angle of zero degrees in Godot, making it easy to rotate it from code.
Like other objects interacting with physics bodies, the raycast has a collision mask.
It can only intersect with physical objects on the mask we give it.
In our case, we flag 1
and 2
because they’re
the layers the walls and robot are on.
We also turn on the Enabled property to activate the raycast. Otherwise, by default, it is disabled and does not detect anything. You will often keep raycasts inactive in games to help with performance.
You should see the ray point to the right in the editor.
Let’s move on to the script. When you open it, you’ll see this.
extends Sprite
var bat_textures := [
preload("bat_aware.png"),
preload("bat_hang.png")
]
onready var look_direction := $LookDirection
We need to do three things:
LookDirection
scene, so we have a
visual.First, let’s update the raycast. Add a reference toward the top.
onready var raycast := $RayCast2D
Then, add the physics code.
func _physics_process(delta: float) -> void:
# Rotate the raycast to look at the mouse cursor
look_at(get_global_mouse_position())
raycast.force_raycast_update() raycast.
Usually, a raycast checks for collisions and then reports them in the following physics frame.
The force_raycast_update()
function immediately checks
for a collision instead of waiting for the next physics frame.
This is important for an accurate reading because we want to check for a collision in this frame.
After the update, we check for a collision and either set the bat texture to angry if it hits a
, or neutral otherwise.func _physics_process(delta: float) -> void:
# ...
# We check if the ray collides with a KinematicBody2D. As the only
# KinematicBody2D we can hit is the player, we update the bat's texture to
# the mean-looking sprite.
#
# The is keyword checks for the type of the collider we get with
# get_collider()
if raycast.get_collider() is KinematicBody2D:
= bat_textures[0]
texture else:
= bat_textures[1] texture
In a larger game, we might check which
we hit. It may be another enemy rather than the player.But in this scene, the only other
is the player, so we don’t need extra checks.All that’s left is to update the LookDirection instance. Since it draws a vector, we set its vector to the collision point.
func _physics_process(delta: float) -> void:
# ...
= to_local(raycast.get_collision_point()) look_direction.vector
You’ve already learned about types in code. They’re names we use to
restrict which values are compatible with variables or functions. As
we’ve seen, types can be anything from float
, and so on.
Sometimes, it’s useful to check the type of a variable or parameter
using the is
keyword. We can also use it to check what kind
of node an object is.
We used it here because we know the robot is a
to save some additional coding.And there we go! A first look at raycasts. If you run the
BatLookToyRoomVideo.tscn
scene, you’ll be able to see the
raycast in action.
In the next chapter, we’ll use this new knowledge of raycasts to improve our flying bat.
Here’s the full code for the BatLook.gd
script.
extends Sprite
var bat_textures := [
preload("bat_aware.png"),
preload("bat_hang.png")
]
onready var look_direction := $LookDirection
onready var raycast := $RayCast2D
func _physics_process(delta: float) -> void:
# Rotate the raycast to look at the mouse cursor
look_at(get_global_mouse_position())
raycast.force_raycast_update()
raycast.# We check if the ray collides with a KinematicBody2D. As the only
# KinematicBody2D we can hit is the player, we update the bat's texture to
# the mean-looking sprite.
#
# The is keyword checks for the type of the collider we get with
# get_collider()
if raycast.get_collider() is KinematicBody2D:
= bat_textures[0]
texture else:
= bat_textures[1]
texture # Visualizes the RayCast2D.
= to_local(raycast.get_collision_point()) look_direction.vector