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?
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.