Search Unity

How to calculate collision impulses to apply with ApplyImpulse()

Discussion in 'Physics for ECS' started by PhilSA, Jun 21, 2022.

  1. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I'm facing a situation where I need to solve collision impulses manually instead of relying on the physics engine. Imagine this scenario: a blue trigger collider moves towards a red dynamic rigidbody. We want to pretend that the blue trigger has a mass of X, and we want to calculate the impulse to apply on the red body so that it imitates a collision that would be solved by the physics engine:


    So basically, given this information:
    • collision normal
    • collision point
    • Physics mass/velocity of A
    • Physics mass/velocity of B
    What is the physically-correct way of calculating the impulse that we should apply on bodies A & B using PhysicsVelocity.ApplyImpulse() in order to imitate a dynamic rigidbody collision between the two bodies? I've tried a few things that almost work (calculate the change in momentum at the collision point, using "point velocities" and "effective masses" at that point, and use that as impulse), but the results always seem slightly off compared to an equivalent scenario solved by the physics engine.
     
    Last edited: Jun 22, 2022
    Occuros likes this.
  2. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    I don't know the answer to your question, but I do wonder whether you can get the same result as the physics engine when you are (presumably) trying to compute and apply an impulse in a single step while comparing it to what the physics engine does (again presumably) over a number of steps via solver iterations?
     
  3. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I think you're right; it probably won't be exactly the same

    However, I've done some research (talks & papers on physics engines) since yesterday and I now see that my current approach to calculating impulses isn't totally correct, which is the main reason why my results differ. I'll be working on implementing a more physically correct approach and I think the results will be sufficiently similar once that's done
     
    hippocoder and Occuros like this.
  4. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    I'd love to hear a TLDR on how you achieved this when you get it (if you don't mind sharing).
     
    Edy, Occuros and PhilSA like this.
  5. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I think I've got it now:

    Code (CSharp):
    1.  
    2. public static void SolveCollisionImpulses(
    3.     in PhysicsVelocity physicsVelA,
    4.     in PhysicsVelocity physicsVelB,
    5.     in PhysicsMass physicsMassA,
    6.     in PhysicsMass physicsMassB,
    7.     in RigidTransform transformA,
    8.     in RigidTransform transformB,
    9.     float3 collisionPoint,
    10.     float3 collisionNormalBToA,
    11.     out float3 impulseOnA,
    12.     out float3 impulseOnB)
    13. {
    14.     impulseOnA = default;
    15.     impulseOnB = default;
    16.  
    17.     Translation translationA = new Translation { Value = transformA.pos };
    18.     Translation translationB = new Translation { Value = transformB.pos };
    19.     Rotation rotationA = new Rotation { Value = transformA.rot };
    20.     Rotation rotationB = new Rotation { Value = transformB.rot };
    21.  
    22.     float3 pointVelocityA = physicsVelA.GetLinearVelocity(physicsMassA, translationA, rotationA, collisionPoint);
    23.     float3 pointVelocityB = physicsVelB.GetLinearVelocity(physicsMassB, translationB, rotationB, collisionPoint);
    24.  
    25.     float3 centerOfMassA = physicsMassA.GetCenterOfMassWorldSpace(translationA, rotationA);
    26.     float3 centerOfMassB = physicsMassA.GetCenterOfMassWorldSpace(translationB, rotationB);
    27.     float3 centerOfMassAToPoint = collisionPoint - centerOfMassA;
    28.     float3 centerOfMassBToPoint = collisionPoint - centerOfMassB;
    29.  
    30.     float3 relativeVelocityAToB = pointVelocityB - pointVelocityA;
    31.     float relativeVelocityOnNormal = math.dot(relativeVelocityAToB, collisionNormalBToA);
    32.  
    33.     float3 crossA = math.cross(centerOfMassAToPoint, collisionNormalBToA);
    34.     float3 crossB = math.cross(collisionNormalBToA, centerOfMassBToPoint);
    35.     float3 angularA = math.mul(new Math.MTransform(transformA).InverseRotation, crossA).xyz;
    36.     float3 angularB = math.mul(new Math.MTransform(transformB).InverseRotation, crossB).xyz;
    37.     float3 temp = angularA * angularA * physicsMassA.InverseInertia + angularB * angularB * physicsMassB.InverseInertia;
    38.     float invEffectiveMass = temp.x + temp.y + temp.z + (physicsMassA.InverseMass + physicsMassB.InverseMass);
    39.  
    40.     if (invEffectiveMass > 0f)
    41.     {
    42.         float effectiveMass = 1f / invEffectiveMass;
    43.  
    44.         float impulseScale = -relativeVelocityOnNormal * effectiveMass;
    45.         float3 totalImpulse = collisionNormalBToA * impulseScale;
    46.  
    47.         impulseOnA = -totalImpulse;
    48.         impulseOnB = totalImpulse;
    49.     }
    50. }
    51.  
    "SolveCollisionImpulses" takes some PhysicsVelocity and PhysicsMass as parameters, but if one of your objects doesn't have one of these components, you can just create a PhysicsVel/Mass on the fly with all the right properties, and pass them to the function. It'll return the impulses that would resolve the collision. If you need to apply the resulting impulse to an object that doesn't have a PhysicsVelocity, you can just look at what the "ApplyImpulse()" function does in the source code of Unity.Physics, and immitate that for that object

    One limitation to keep in mind with this is that it won't give physically-accurate results when there are more than just 2 bodies involved (ex: you have a seesaw with a block on one side and a character that jumps onto the other side). The impulse calculated by this between the character and the seesaw won't take into account the counter-weight of the block on the other side, so the total effect of the impulse will appear weaker than it should be. The only real way to deal with that kind of scenario is with a complete physics solver (what solves systems of rigidbodies in a typical physics engine)
     
    Last edited: Jun 30, 2022
    Luxxuor likes this.