5
\$\begingroup\$

I'm writing a game where you can pick and throw objects and depending on the force of the impact they break.

Using the get_contact_impulse method to calculate the breaking point gives me very consistent results, but only for a single rigid body. When two rigid bodies collide, one of them reports the correct impulse, the other reports really low values, close to 0.

As an example, this is the same collision being reported by both objects:

SimpleCube_A2 collide with SimpleCube_A force 18.338762
SimpleCube_A collide with SimpleCube_A2 force 0.0180135

I believe this happens because once the second object runs its IntegratePhysics, the impact has already been solved by the first object (but this is just a guess).

I could just call a method on the second object, but since both objects will be reacting to the impact, I would have to add some bookkeeping to make sure the calls don't get recursive, nothing difficult but could be more error prone.

So I'm wondering if there's a solution where both objects can detect the "same" impulse, but without talking to each other. I don't mind having to calculate the impact myself or something like that, as long as it's reliable enough.

\$\endgroup\$
0

1 Answer 1

0
\$\begingroup\$

You could make each object compute its own impulse using linear momentum change. This solution uses Signals and tracks state variables manually. It works for linear momentum only, but you can extend it to include angular momentum too.

Add a script to a rigid body. A variable will store the object's linear velocity before updates at each physics frame:

extends RigidBody3D

var prev_velocity: Vector3

func _physics_process(_delta: float) -> void:
    prev_velocity = linear_velocity

To configure the rigid body to emit signals when colliding with another body, from the Inspector, set Contact Monitor to True and Max Contacts Reported to a value larger than 0 (I set it to 4 just in case). Depending on the object shape, you may want to enable Continuous CD too.

Now, connect the body_entered Signal of the rigid body to its own Script in a new Receiver Method. This method will be emitted automatically after the physics engine has resolved all collisions. Therefore, the rigid body will figure out what impulse was applied to turn its previous velocity into the current one:

func _on_body_entered(body: Node) -> void:
    var impulse = mass * (linear_velocity - last_velocity)
    print(name + " collide with " + body.name + " force " + str(impulse.length()))

The above means: physics engine applied a force of impulse magnitude for physics process' delta seconds to the object as a result of resolving all collision pairs, causing its linear velocity to switch from prev_velocity (previous frame) to linear_velocity (current frame).

The above snippet works when the object collides both with static bodies and other rigid bodies. RB pairs will compute impulses that will explain their final velocities.

If "strange numbers" start appearing, this is due to the fact that I didn't take angular momentum into account, so total momentum is still there in the Physics Server but wasn't computed in my custom script.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the answer, but it didn't work. I still get the same problem where only one rigid body reports a high impulse, I supose because the change in momentum for a resting object is going to be low no matter what...? unless the impact is extremely strong \$\endgroup\$
    – Luke B.
    Nov 30, 2023 at 17:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .