How to get the impulse vector at collision of two RigidBody2D's

Godot Version

Godot 4.2.1

Question

In my game, I want enemies to take damage scaled by how hard they were hit, ie the impulse vector that the weapon applied to the enemy. Both the enemies and the weapons are RigidBody2D’s.

I thought the value I needed would be the return of get_contact_impulse(), based on the method’s desscription. I wrote this code in the enemy’s script:

func _integrate_forces(state):
	for i in range(state.get_contact_count()):
		var weapon = state.get_contact_collider_object(i)
		if weapon is Weapon:
			var damage = clamp(remap(state.get_contact_impulse(i).length(), damage_min_speed, damage_max_speed, 0, weapon.damage), 0, weapon.damage)
			$HealthComponent.take_damage(damage, weapon.faction)

However, state.get_contact_impulse(i) returns a zero vector. How do I get the impulse vector of the collision?

I believe this is this bug: PhysicsDirectBodyState3D.get_contact_impulse() returns zero vector on first frame of collision · Issue #73541 · godotengine/godot · GitHub
Good news is it appears it’ll be fixed in 4.3
Edit: sorry, you said 2d, not 3d… from that issue link, though, it looks like the bug affects both

2 Likes

is there a workaround while I’m waiting for 4.3?

I found a workaround.

In _physics_process of enemy:

func _physics_process(delta):
	_prev_velocity = _this_velocity

in _integrate_forces of enemy:

func _integrate_forces(state):
	_this_velocity = linear_velocity
	
	if _take_damage:
		var impulse = mass * (linear_velocity - _prev_velocity)
		var damage_taken = clamp(remap(impulse.length(), damage_min_speed, damage_max_speed, 0, _take_damage_amount), 0, _take_damage_amount)
		health_component.take_damage(damage_taken, _take_damage_faction)
		
		_take_damage = false

By storing the previous velocity, I can subtract the current velocity from the previous velocity to get the acceleration, then multiply by mass to get force. This must be done in _integrate_forces, because linear_velocity will not have updated in physics_process or on collision.

Side note, I moved the detection of collision to the weapon, which calls a take_damage function of enemy, which sets _take_damage, _take_damage_amount and _take_damage_faction, each of which are member variables. This allows _integrate_forces to know the damage information and when to take damage. This is pretty inelegant, I wish there was a signal for when physics has finished calculating so I could just use await physics_processed in take_damage.

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