Force of an impact. (Solution)

Jun 13, 2009 at 11:15 PM
Edited Jun 13, 2009 at 11:16 PM

Hi i posted about this problem a bit back an have noted that a few people have also asked about this problem.
I have found a solution that works for me and want to post it the help any others out.
Please tell me what you think of my solution and if you think there is any way of inproving it.

Hope this helps.

 

    public bool PlayerColEvent(Geom geom1, Geom geom2, ContactList list)
        {
            Vector2 position = list[0].Normal; //get the normal of the impact

            float angle = (float)Math.Atan2(position.Y, position.X); //calculate the angle of the normal

            Vector2 force = Vector2.Zero; //stores the force of the impact
            if (angle < 0) //detect the angle
                force = new Vector2((float)(Math.Cos(angle) * geom1.Body.LinearVelocity.X),(float)Math.Sin(MathHelper.TwoPi + angle) * geom1.Body.LinearVelocity.Y);
            else
                force = new Vector2((float)(Math.Cos(angle) * geom1.Body.LinearVelocity.X),(float)Math.Sin(MathHelper.TwoPi - angle) * geom1.Body.LinearVelocity.Y);
            //apply the angle to the velocity to work out the impact vel
            //now to get the force of the collison just do force.Length()
            //you now have the impact force on collison

            return true;
        }

Jul 18, 2009 at 3:59 AM
Edited Jul 18, 2009 at 4:03 AM

I solved this a different way.

I saw in the PreStepImpulse method of the Artbiter class that the force of collisions are calculated on each contact in the ContactList to work out what impulses are to be applied for collision response - so rather than re-calculate this I decided to leverage this.

I added a new event in the Geom class, which gets called AFTER the impulses are calculated.  The result is the ContactList bounceVelocity contains a value relating to the force (I also had to make the bounceVelocity public in ContactList) - more my purposes bounceVelocity is a suitable approximation to force, as I just wanted to adjust audio pitch on a soundeffect based on the force of the collision.

So, my changes were :

1. Modify bounceVelocity in Contact.cs from internal to public

public float bounceVelocity;

2. Modify Geom.cs to include new delegate

public delegate bool CollisionEventHandlerWithBounceVelocity(Geom geometry1, Geom geometry2, ContactList contactList);
...
public CollisionEventHandlerWithBounceVelocity OnCollisionWithBounceVelocity;

3. Modify ApplyImpulses in PhysicsSimulator.cs to call new delegate

private void ApplyImpulses(float dt)
        {
            float inverseDt = 1f / dt;

            for (int i = 0; i < jointList.Count; i++)
            {
                if (!jointList[i].Enabled || jointList[i].IsDisposed)
                    continue;

                jointList[i].PreStep(inverseDt);
            }

            for (int i = 0; i < arbiterList.Count; i++)
            {
                if (!arbiterList[i].GeometryA.CollisionResponseEnabled || !arbiterList[i].GeometryB.CollisionResponseEnabled)
                    continue;

                arbiterList[i].PreStepImpulse(inverseDt);

                // -- Added OnCollisionWithBounceVelocity 
                if (arbiterList[i].GeometryA.OnCollisionWithBounceVelocity != null)
                    if (arbiterList[i].ContactList.Count > 0)
                        arbiterList[i].GeometryA.OnCollisionWithBounceVelocity(arbiterList[i].GeometryA, arbiterList[i].GeometryB, arbiterList[i].ContactList);

                if (arbiterList[i].GeometryB.OnCollisionWithBounceVelocity != null)
                    if (arbiterList[i].ContactList.Count > 0)
                        arbiterList[i].GeometryB.OnCollisionWithBounceVelocity(arbiterList[i].GeometryB, arbiterList[i].GeometryA, arbiterList[i].ContactList);
                // -- End OnCollisionWithBounceVelocity 

            }
	}
4. My game now can handle the OnCollisionWithBounceVelocity event like thus :
private bool CollisionHandler(Geom geom1, Geom geom2, ContactList contactList)
        {
            if (geom1 == this.Geom || geom2 == this.Geom)
                if (Body.Enabled && Body.Moves)
                {
                    // Adjust pitch to be proportional to size of force of collision                    
                    for (int i=  0; i< contactList.Count; i++)
                    {
                        if (contactList[i].bounceVelocity != 0)
                        {
                         	_bounce1SoundInstance.Pitch = Math.Abs(contactList[i].bounceVelocity) / 1000;
                                _bounce1SoundInstance.Play();
                        }                        
                    }
                }
            return true;
        }

The end result is quite effective - my test case is a grenade polygon, which on collision has multiple contacts in the ContactList, each with varying degrees of bounceVelocity - so the sound of the grenade bouncing, rolling, etc is fairly realistic.

Jul 20, 2009 at 10:35 AM

I haven't looked into this, but isn't there a way to retrieve global force vector applied to geometry/body in OnCollision? Store overall force calculated during collision calculation and provide some public getter 'LastAppliedForce' in Body. This can be pretty usefull for sound effects (like mentioned) or even for damage calculation.

Coordinator
Jul 20, 2009 at 3:08 PM

@MikeSheen:

A thing you should be aware of is that bounceVelocity takes restitution into account. This means that if you have a ball with restitution of 1 it will have a higher bounce velocity than if a lower restitution is used.

@Dusho:

Not a global force vector - we simply don't need it in the engine. You can retrieve the local force vector at any offset on the body. There are some things to consider tho:

Force is two things: the velocity of movement along a straight line (linear velocity) and velocity around the rotating axis (angular velocity). Imagine a box that collides with another box head on. The force of impact will be the speed at which the box hit the other box. But if the box was rotating just as it hit the other box, the force of impact will be larger. If you measure the linear velocity at the center of the body, it will be lower than if you measured it at the contact point.

Anyway. When two geometries collide, you can measure the force of impact by checking the velocity on the contact points between the two geometries. In the case of what MikeSheen wrote, he also takes restitution into account. It is simply "velocity * restitution" where restitution goes from 0 to 1 in normal cases. (beware that velocity in this case points along the normal of the geometry (because it is calculated in the collision response). That means the velocity is pointing away from the geometry.