How to tell how fast a RigidBody2D is going when it collides?

Godot Version

v4.4.1.stable.official [49a5bc7b6]

Question

I have a RigidBody2D ball bouncing around a scene and would like to play a sound effect when it strikes a StaticBody2D wall. I’d like to adjust how loud the sound is based on how hard the hit is.

I’ve tried listening to the RigidBody2D’s body_entered signal, but that only tells you that there was a hit, not how hard the hit was. I also tried overriding the _integrate_forces of the RigidBody2D. I thought getting the contact_impulse would give me the info I want, but the magnitude of the impulse isn’t a good representation of how hard the hit was. I’m getting small values for hard hits, and much bigger values when my ball is just gently rolling against the wall.

Is there any way to tell how hard my ball hits the wall?

func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
	for i in state.get_contact_count():
		var col_obj = state.get_contact_collider_object(i)
		var impulse:Vector2 = state.get_contact_impulse(i)
		
		#print("impulse.length() ", impulse.length())
		hit_wall.emit(impulse.length())
#		hit_wall.emit(linear_velocity.length())

I think what you are looking for is state.get_contact_local_velocity_at_position(idx)
So maybe:

func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
	for i in state.get_contact_count():
		var col_obj = state.get_contact_collider_object(i)
		var hit_speed:Vector2 = state.get_contact_local_velocity_at_position(i)

		hit_wall.emit(hit_speed.length())

It returns the velocity before the collision.

But it can only access it within the _integrate_forces.
So I don’t think you can get that information within the body_entered signal unfortunately.

1 Like

That works. Doing it from within _integrate_forces is fine. I found I needed to take the dot product with the normal to get a better collision velocity estimate.

func _integrate_forces(state: PhysicsDirectBodyState2D) -> void:
	var max_vel:float = 0
	for i in state.get_contact_count():
		var col_obj = state.get_contact_collider_object(i)
		var local_vel:Vector2 = state.get_contact_local_velocity_at_position(i)
		var local_norm:Vector2 = state.get_contact_local_normal(i)
		
		max_vel = max(max_vel, -local_norm.dot(local_vel))

	hit_wall.emit(max_vel)
1 Like