RigidBody2D collision problem

Godot Version

v4.2.2.stable.official [15073afe3]

Question

Blocks change color and a sound is played when hit.

When I throw my RigidBody2D to the right without jumping, it registers one collision too many (3). If I do the same thing on the left, it registers 2 collisions, and this is what I want.

On the other hand, when I throw after a jump, I have 2 collisions on the right, and 3 on the left, so one too many !

Here’s the relevant code for my RigidBody2D:

func _on_body_entered(body):
    if body.modulate == Color(1,1,1):
        body.set_modulate(Color(1,0,0))
        print("Red")
    else:
        body.set_modulate(Color(1,1,1))
        print("White")
    $FlagCollisionSound.play()

Nothing is scaled, shape on the right is the same shape on the left rotated 90°.

I already tried tried putting continuous cd at “Cast Shape”, doubled default physics ticks (so 120), and achieved the same result.

It looks like your RigidBody2D might be registering multiple collisions due to overlapping shapes or rapid movement. Try implementing collision debouncing by adding a “has_collided” flag to prevent multiple detections.

for example

var has_collided = false

func _on_body_entered(body):
    if not has_collided:
        has_collided = true
        if body.modulate == Color(1,1,1):
            body.set_modulate(Color(1,0,0))
        else:
            body.set_modulate(Color(1,1,1))
        $FlagCollisionSound.play()

func _on_body_exited(body):
    has_collided = false

Hello, thanks for your answer. I tried your suggestion and achieve the same result. Here’s the full script for the object I’m throwing:

extends RigidBody2D


var has_collided = false

signal start_smoothing()


func _on_area_2d_area_entered(area):
	var player = area.get_parent()
	if player.can_retrieve_flag:
		queue_free()
		player.retrieve_flag()


func _on_tree_exited():
	start_smoothing.emit()


func _on_body_entered(body):
	if not has_collided:
		has_collided = true
		if body.modulate == Color(1,1,1):
			body.set_modulate(Color(1,0,0))
		else:
			body.set_modulate(Color(1,1,1))
		$FlagCollisionSound.play()


func _on_body_exited(body):
	has_collided = false

I think your suggestion didn’t work because the collisions happen one after another, and not at the same time.

1 Like

Try this…
I added a Timer for debouncing:

extends RigidBody2D

var has_collided = false
var collision_timer: Timer

signal start_smoothing()

func _ready():
    # Initialize the Timer
    collision_timer = Timer.new()
    collision_timer.wait_time = 0.1  # Adjust time as needed
    collision_timer.one_shot = true
    collision_timer.connect("timeout", self, "_on_collision_timer_timeout")
    add_child(collision_timer)

func _on_area_2d_area_entered(area):
    var player = area.get_parent()
    if player.can_retrieve_flag:
        queue_free()
        player.retrieve_flag()

func _on_tree_exited():
    start_smoothing.emit()

func _on_body_entered(body):
    if not has_collided:
        has_collided = true
        if body.modulate == Color(1,1,1):
            body.set_modulate(Color(1,0,0))
        else:
            body.set_modulate(Color(1,1,1))
        $FlagCollisionSound.play()
        collision_timer.start()  # Start the timer to debounce collisions

func _on_collision_timer_timeout():
    has_collided = false

It works, thanks ! But it feels kind of “hacky”, I mean, it’s a simple collision between two rectangles at a relatively normal speed, I find it kind of weird that I have to resort to timers for such a simple thing… And I still don’t understand why the collision is “normal” on one side and not the other !

I could also store the velocity at each frame and check in _body_entered if the velocity is different than 0, and in that case play the sound. But again, and maybe it’s my inexperience with gamedev in general, I find it weird that I can’t just say something as simple as “play the sound when something hits the object”.

Yeah it does feels like using a timer is a bit “hacky” for a simple collision. The different behavior on each side could be due to how the physics engine processes collisions or slight differences in object positioning.

You could try checking the velocity before playing the sound, which might feel more intuitive:

if not has_collided and linear_velocity.length() > 0:
    has_collided = true
    $FlagCollisionSound.play()

Also, consider fine-tuning collision layers, masks, and the physics settings like continuous collision detection (“CCD”). Game development sometimes requires these workarounds due to the complexities of physics engines, but with experience, you’ll find approaches that feel more natural.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.