Now we’ve explored the
node, we can use it in our Bat scene.After this lesson, our robot will be able to hide from the bat. As the bat flies around, the raycast will update in real-time.
We’ll alter the bat scene by:
_physics_process()
to check if the
target is in view.Let’s get going!
Add a
to the bat scene.We need to set the Cast To vector here.
We make it point to the right and set the value to match the radius of the AggroArea’s collision shape.
This will be how far the bat can “see.” The raycast can’t collide with anything beyond its range.
Be sure to include the robot’s collision layer in the Collision Mask, or the raycast won’t detect it.
Lastly, turn the ray’s Enabled property on.
In our bat script, let’s first store a reference to the raycast so we can use it in the script. Add this to the top of the script:
onready var raycast := $RayCast2D
The bat should only move toward the robot if:
Once more, we need to rewrite how we calculate the bat’s movement direction.
Please delete the line in the if target:
conditional
block at the top of _physics_process()
. We’re going to
replace it with the following.
func _physics_process(delta: float) -> void:
# You should already have the following two lines in your script.
var direction := Vector2.UP
if target:
# Robot has entered the areas and is within range, so we check if the
# raycast is unobstructed
look_at(target.global_position)
raycast.force_raycast_update()
raycast.# If the collider is the robot, then we have a direct view to it.
#
# Otherwise, it means something is blocking the bat's line of sight.
if raycast.get_collider() == target:
= to_local(target.global_position).normalized()
direction
# ...
The first thing to do is update the raycast. Recall that
target
is valid if it entered the AggroArea.
So, we know when we have a target. In that case, we can safely call
the look_at()
function, which keeps rotating to point at
the target.
As seen in the previous lesson, we call the
force_raycast_update()
function every frame to ensure
collisions are accurate.
func _physics_process(delta: float) -> void:
# ...
if target:
# Robot has entered the areas and is within range, so we check if the
# raycast is unobstructed
look_at(target.global_position)
raycast.force_raycast_update()
raycast.# ...
Then, if the target is the same as the collider, we know there’s no wall between the bat and the robot, so we move the bat toward it.
func _physics_process(delta: float) -> void:
# ...
if target:
# ...
if raycast.get_collider() == target:
= to_local(target.global_position).normalized()
direction # ...
And that’s it! If you run the scene, the player can sneak around as needed.
However, if you run the scene and play around a bit, you might notice strange behavior when the bat and robot collide.
It’s funny, but it’s not really what we expect from an enemy!
In the next lesson, we’ll deal with what happens when the bat touches the robot by adding a simple fail state.
Here’s the current code for the bat so far.
extends KinematicBody2D
export var max_speed := 300.0
export var drag_factor := 0.1
var velocity := Vector2.ZERO
var target
onready var raycast := $RayCast2D
onready var aggro_area := $AggroArea
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:
# Robot has entered the areas and is within range, so we check if the
# raycast is unobstructed
look_at(target.global_position)
raycast.force_raycast_update()
raycast.# If the collider is the robot, then we have a direct view to it.
#
# Otherwise, it means something is blocking the bat's line of sight.
if raycast.get_collider() == 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(player: KinematicBody2D) -> void:
= player
target
func _on_player_exited(player: KinematicBody2D) -> void:
= null target