Goal
I'm trying to create a 2D horizontal space shooter in Godot. I've used a CharacterBody2D
node for the player, a RigidBody2D
node for the asteroids that the player has to dodge and a plain Node for the main scene. My goal is to lower the player's HP when an asteroid collides against the player.
Idea
Given that I'd like to not make the asteroid disappear after the collision, I've chosen to use a RigidBody2D
for the asteroid after I found out that there's a signal that detects if the RigidBody2D
collides with another PhysicsBody2D
: it's name is body_entered()
. According to the documentation, the conditions to make this signal work is that the property contact_monitor
has to be set to true
and that the property max_contacts_reported
has to be set high enough to detect all collisions. And so I did it (I set max_contacts_reported
to 10
, just to be sure that at least 1 collision would be detected). Then I connected the signal to the same RigidBody2D
and I put a print statement to see if the signal would work. Then, I set the Player's layer to 1 and its mask to 2, and viceversa with the Asteroid's layer and mask.
The Problem
When I ran the code, collisions between the player and the asteroids happened, but no output has ever appeared. That makes me think that the signal itself is not working, but I can't figure out what's the issue.
I set the property contact_monitor
to true
and max_contacts_reported
to 10
from the inspector of the RigidBody2D (aka the asteroid)
I expected that the RigidBody2D's signal _on_body_entered(body)
would print a statement in the console output whenever a collision between the player and an asteroid happens. However, this doesn't happens, despite collisions actually happen (the player is able to stop the movement of the asteroids by blocking their path and can rotate them by touching them in certain angles)!
I found on StackOverflow an answer about the body_entered()
signal's using of a RigidBody2D, but I already followed its instructions with no success. Here's the link: text
I'll paste the code of the three nodes below. Thank you in advance!
CharacterBody2D' code
extends CharacterBody2D
@export var speed = 400
@export var health = 100
var screen_size
func _ready():
screen_size = get_viewport_rect().size
#This is an idea of the function that will lower the player's health.
func hit(value: float):
health -= value
print("+++This is your new health: " + str(health) + "+++")
#Register the inputs of the user and converts them in a 2 dimensional vector. Then, it multiplies the vector with the player's speed and it register this result as the velocity of the player. At the end, it clamps the player position inside the viewport.
func _physics_process(_delta):
var input_direction = Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = input_direction * speed
move_and_slide()
position = position.clamp(Vector2.ZERO, screen_size)
RigidBody2D's code
extends RigidBody2D
@export var min_speed = -200
@export var max_speed = -500
var speed
var screen_size
var start_pos
# Called when the node enters the scene tree for the first time.
func _ready():
screen_size = get_viewport_rect().size
speed = Vector2(randi_range(min_speed, max_speed), 0) #Chooses a random speed between the min_speed and the max_speed. The values are negative because the asteroids will move from right to left.
start_pos = Vector2(screen_size.x + 15, (randi_range(1, screen_size.y - 1))) #Chooses a random Y position outside of the viewport, so that any asteroids will suddenly appear in the game window.
position = start_pos
show()
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _integrate_forces(_delta):
linear_velocity = speed
#When the asteroid leaves the viewport, it will destroy itself. This signal actually works!
func _on_visible_on_screen_notifier_2d_screen_exited():
queue_free()
#This is the infamous signal that isn't working! No idea about why it doesn't work...
func _on_body_entered(body):
print("The signal is working!")
Main Node's code
extends Node
#I store the asteroid scene inside this PackedScene (the setting is only visible from the inspector of the Main Node)
@export var asteroid_scene: PackedScene
# Called when the node enters the scene tree for the first time.
func _ready():
$AsteroidTimer.start() #This timer is not "one shot"
#Every time that the timer goes in timeout, an asteroid scene will be instantiated from the PackedScene and will become a child of the Main Node.
func _on_asteroid_timer_timeout():
var asteroid = asteroid_scene.instantiate()
add_child(asteroid)