Game-level collision response

May 3, 2009 at 9:26 PM
I've been trying to do a better job of responding to collisions between objects with appropriate sound effects and damage results.

To facilitate this, I made a few small changes to my local copy of Farseer. Since this subject has come up here a few times, the changes might be generally useful.

In the Body class, I added a delegate to be called whenever an impulse is applied to a body as a result of a collision:

        public delegate void CollisionImpulseEventHandler(Geom geometry1, Geom geometry2, ref Vector2 impulse, ref Vector2 worldOffset);

and the corresponding field:

        public CollisionImpulseEventHandler OnCollisionImpulse;

and the following modification to PreStepImpulse() in Arbiter.cs:

                GeometryB.body.ApplyImpulseAtWorldOffset(ref _impulse, ref _r2);
                if (GeometryB.body.OnCollisionImpulse != null)
                    GeometryB.body.OnCollisionImpulse(GeometryB, GeometryA, ref _impulse, ref _r2);

                Vector2.Multiply(ref _impulse, -1, out _impulse);
                GeometryA.body.ApplyImpulseAtWorldOffset(ref _impulse, ref _r1);
                if (GeometryA.body.OnCollisionImpulse != null)
                    GeometryA.body.OnCollisionImpulse(GeometryA, GeometryB, ref _impulse, ref _r2);

This gives me:
 - what geometry of the body was collided with
 - what geometry collided with it
 - what impulse was applied in response
 - where the impulse was applied

May 3, 2009 at 11:02 PM
Oops - just noticed a typo in the last bit of code. It should be:

                GeometryB.body.ApplyImpulseAtWorldOffset(ref _impulse, ref _r2);
                if (GeometryB.body.OnCollisionImpulse != null)
                    GeometryB.body.OnCollisionImpulse(GeometryB, GeometryA, ref _impulse, ref _r2);

                Vector2.Multiply(ref _impulse, -1, out _impulse);
                GeometryA.body.ApplyImpulseAtWorldOffset(ref _impulse, ref _r1);
                if (GeometryA.body.OnCollisionImpulse != null)
                    GeometryA.body.OnCollisionImpulse(GeometryA, GeometryB, ref _impulse, ref _r1);

May 4, 2009 at 12:07 AM
Damn, it's more complicated than I thought - there are extra contact-based impulses applied in ApplyImpulse()...
Coordinator
May 4, 2009 at 12:10 AM
Thanks ladron.

Since the geometry class handles all the collision stuff, it might be better to put it into the geometry class. Imagine a case where a body has no geometry attached, the event can still be subscribed on the body class, but the expected behavior would only happen when a geometry is attached.

Just out of curiosity, how practically does this differ from OnCollision? Besides the fact that the impulse and impulse world offset is included in the event callback.
May 4, 2009 at 12:31 AM
I guess it seems more relevant to me at the body level, since that is where impulses are applied. Even though the geometries are being checked individually, it is really the bodies that are colliding. To be honest, it would probably be more convenient from my perspective if OnCollision was at the body level - when I have bodies with multiple geoms, I always end up setting the same event handler on all of them. I see your point about a body with no geometry, but you could say the same of a non-colliding geom. It's really just a matter of convenience for me, though - it would work either way.

Regarding the difference from OnCollision - I see OnCollision as a "should I collide" check, while this is more of a "this collision happened, with this resulting impulse". Calculating the resulting impulses accurately on your own in an OnCollision callback is non-trivial, since you basically would have to duplicate what the Arbiter ends up doing.

I'm not sure if this exact solution is the best, but something like it would be really nice to have in the Engine. It is a really common problem that game developers need to solve.

My current project is a little demolition derby game where this issue is particularly relevant...
Coordinator
May 4, 2009 at 12:57 AM
The OnCollision event is indeed called before an arbiter is created. This makes it possible for the users to cancel an update, but is really meant to fire when a collision occurs.

I can see your point in both the collision event being on the body and why the OnCollisionImpulse event should exist. However, there is not much I can do about the event on the body. It's a matter of the lesser of two evils.

Why would you calculate the impulse by yourself? If it is for damage detection, you have access to the list of contacts and they have a Separation property that contains the amount of penetration. The distance into the geometry from the edge.
If you have a geom that is 100x100, a seperation value of -50 would mean the two geometries have collided half way into the 100x100 geometry.
May 4, 2009 at 2:13 AM
I absolutely don't want to calculate the impulse by myself - hence having the physics engine tell me when it is applying one in response to a collision.

I've tried to use Separation for calculating damage, but I haven't been able to make it work very well. One of the biggest reasons, I think, is because of the tolerance caused by the engine's "AllowedPenetration" constant - which results in situations where there is "separation" without real resulting force going on in the engine. I've tried to account for that, but without success thus far. Even if I had been able to make it work, it is still an ad hoc calculation I am making to try to reproduce what the engine is doing.

What is really needed is a value which indicates how much "stress" the body is taking from collisions. The impulses the engine is applying as a response seem like a good place to start, and this method given me my best results so far.

The bottom line is that in the game context (as opposed to the physics engine context) you don't care about these details. You just want to know that an object was hit, and how hard. The "how hard" is the hard part :-)
Coordinator
May 4, 2009 at 12:34 PM
I can understand why you don't want to calculate the impulse by yourself, it is the engines job and it does already do it. However, the impulse calculated is based off many things and if you use it for damage detection, you might get unexpected results.

The impulse calculated is the amount that needs to be applied to separate the two geometries, and have an opposite reaction.
If you use the code you provided, that not such a big deal, but if you hook into the ApplyImpulse() method, you will run into trouble.
The impulse there is also affected by e.i. the restitution coefficient. That would mean the higher bounce you want, the higher damage you will detect. That is not expected behavior since a higher restitution coefficient does not give an impact with higher force, but is the coefficient of how much the geometry should bounce back.

Anyway, your problem with the separation amount is easily fixed. Cast it to an integer.
The amount of separation comes directly from the narrow phase collider and we are aware that some floating point problems can happen in that part of the engine. The separation value can be translated to pixels and you can't have ½ a pixel ;)

Example on how to use it as damage detection:

private bool OnCollision(Geom geomA, Geom geomB, ContactList contactList)
{
    //GeomA's size divided by two
    float maxDamage = (128 / 2f) * -1;
    float maxPenetration = 0;

    //The first contact is always the deepest (most penetration)
    if (contactList.Count > 0)
        maxPenetration = contactList[0].Separation;
    
    //Damage to apply, ranging from 0 to 1
    float damage = maxPenetration / maxDamage;

    return true;
}

Beware that this code is not tested.