Problem with the normal in OnCollision

Topics: Developer Forum, User Forum
Mar 21, 2010 at 12:42 PM
Edited Mar 21, 2010 at 12:43 PM

Hey, recently I've run into a little problem when using the OnCollision event. I've got the problem sorted out, but I thought I'd share this incase anyone else is having similar problems and it's actually a bug, or if I'm just looking at it entierly wrong.

GeomA and GeomB are the objects found in the world. Geom1 and Geom2 are the references sent to the OnCollision method.

The OnCollision event gets called when two objects collide, and it's called for both objects. The first object to be found is GeomA and the second is GeomB. The OnCollision is first sent to A saying that Geom1 is A and Geom2 is B, then it's sent to B saying that Geom1 is B and Geom2 is A. This meaning that Geom1 in the OnCollision method is always the object that you are looking at.

The problem is that there is also a CollisionList sent with the event, holding a normal among other things, and the normal is pointed from A to B, and when the B event is called there is no way to know that Geom1 isn't A and thus the normal sent in the method to GeomB is actually inverted.

This means that half of all the objects that get the event called on them have an inverted normal in the CollisionList! This doesn't really matter if you aren't using the normal when looking at your objects, but if you do this is a big problem.

Heres the code in the Arbiter:

 

            if (GeometryA.OnCollision != null)
                if (_newContactList.Count > 0)
                    if (!GeometryA.OnCollision(GeometryA, GeometryB, _newContactList))
                        _newContactList.Clear();

            if (GeometryB.OnCollision != null)
                if (_newContactList.Count > 0)
                    if (!GeometryB.OnCollision(GeometryB, GeometryA, _newContactList))  
                        _newContactList.Clear(); _mergedContactList.Clear();

So how can this be fixed? Well first I tried simply always sending GeomA as Geom1 and GeomB as Geom2, but it might not be a good idea because it's very handy to know that geom1 always is the object we are looking at when using the Event, and who knows if anything else uses that event already.

In the end what I did was that simply inverted the normal for the contactlist before it was sent the second time, and then inverted it back since the rest of the event needs it in it's original state. This might be a bit expensive to do, but right now it's the only thing that makes the event work for my game, if anyone has any suggestions on how to write this more efficiently please tell as right now it's very sloppy code.

 

            if (GeometryA.OnCollision != null)
                if (_newContactList.Count > 0)
                    if (!GeometryA.OnCollision(GeometryA, GeometryB, _newContactList))
                        _newContactList.Clear();

            for (int b = 0; b < _newContactList.Count; b++)
                _newContactList[b] = _newContactList[b].InvertNormal();

            if (GeometryB.OnCollision != null)
                if (_newContactList.Count > 0)
                    if (!GeometryB.OnCollision(GeometryB, GeometryA, _newContactList))  
                        _newContactList.Clear(); _mergedContactList.Clear();

            for (int b = 0; b < _newContactList.Count; b++)
                _newContactList[b] = _newContactList[b].InvertNormal();

In contact I made a simple:

 

        public Contact InvertNormal()
        {
            Normal *= -1;
            return this;
        }

 

Hope it helps! Thought it'd be good to post here atleast so that maybe it could be fixed for 3.0.