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 := $LookDirectionWe 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 := $RayCast2DThen, add the physics code.
func _physics_process(delta: float) -> void:
# Rotate the raycast to look at the mouse cursor
raycast.look_at(get_global_mouse_position())
raycast.force_raycast_update()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:
texture = bat_textures[0]
else:
texture = bat_textures[1]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:
# ...
look_direction.vector = to_local(raycast.get_collision_point())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
raycast.look_at(get_global_mouse_position())
raycast.force_raycast_update()
# 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:
texture = bat_textures[0]
else:
texture = bat_textures[1]
# Visualizes the RayCast2D.
look_direction.vector = to_local(raycast.get_collision_point())