OnCollision detection error on 2 contact points

Topics: Developer Forum
Sep 14, 2010 at 10:02 AM
Edited Sep 14, 2010 at 10:03 AM

I've got a fairly-simple rolling character setup, image below. Arrows add Torque to the wheel, space adds a force to jump. All works nicely.

[image]

I'm now working on detecting contact with ground pieces, and have pulled a load from the CharacterCollisionTestBed. By and large it works great, if I'm in contact with the ground, CharacterOnCollision is called, _collision is true and the Debug message is displayed. If I jump, CharacterOnSeparation is called, _collision is false, and the message disappears.

The trouble comes with the slope (_triangleFixture, which was originally aligned with the _rectangleFixture, I shifted it for demonstration purposes). I first noticed that jumping onto it is fine, but rolling onto it would reverse the output of OnCollison - that is, it would be false when I'm in contact with the floor and would briefly flash true as I roll over the point where I leave the triangle and roll onto the rectangle.

Moving the slope up the y axis a little (as in the diagram), it now seems that it's when the _wheel is in contact with both the rectangle and the triangle, that I get a problem.

Presumably I'm doing something wrong with OnCollision?

 

 

public class Character
    {
        public bool _collision;

        public Character(World _world, Vector2 position)
        {
            //creates body elements
            //  _wheel, _torso, joints 

            _wheel.OnCollision += CharacterOnCollision;
            _wheel.OnSeparation += CharacterOnSeparation;
        }

        public void Update(GameTime gameTime, World _world)
        {  
            HandleInput(gameTime);
        }

        private bool CharacterOnCollision(Fixture fixtureA, Fixture fixtureB, Contact contact)
        {
            _collision = true;
            return true;
        }

        private void CharacterOnSeparation(Fixture fixtureA, Fixture fixtureB)
        {
            _collision = false;
        }

 

Sep 14, 2010 at 10:18 AM

Update: playing with the TestBed CharacterCollision, I notice it happens there too - dragging the squareCharacter onto the left-hand green box and dragging it across produces the same error - 'OnSeparation fired' even though there is a collision.

Developer
Sep 14, 2010 at 2:31 PM
Edited Sep 14, 2010 at 2:33 PM

Hey,

the "problem" is OnCollision and OnSeparation fires for every fixture your character collides with. So you need to keep track of all those collisions.

I just copy pasted some code together from my solution:

public class Character
{
    private bool _onGround;
    private bool _onWall;
    private HashSet<Fixture> groundCache;
    private HashSet<Fixture> wallCache;

    public Character(World _world, Vector2 position)
    {
        //creates body elements
        //  _wheel, _torso, joints 

        _wheel.OnCollision += CharacterOnCollision;
        _wheel.OnSeparation += CharacterOnSeparation;
    }

    private bool CharacterOnCollision(Fixture fixtureA, Fixture fixtureB, Contact contact)
    {
        Vector2 down = new Vector2(.0f, 1.0f);
    
        Manifold manifold;
        contact.GetManifold(out manifold);

        float angle = Vector2.Dot(manifold.LocalNormal, down);
    
        if (angle > .99f) 
        {
            onGround = true;
            groundCache.Add(fixtureB);
        }
    
        if (angle < .15f && angle > -.15f) 
        {
            onWall = true;
            wallCache.Add(fixtureB);
        }
   
        return true;
    }

    private void CharacterOnSeparation(Fixture fixtureA, Fixture fixtureB)
    {
        if (groundCache.Contains(fixtureB)) 
        {
            groundCache.Remove(fixtureB);
            if (groundCache.Count == 0) 
            {
                onGround = false;
            }
        }
        if (wallCache.Contains(fixtureB)) 
        {
            wallCache.Remove(fixtureB);
            if (wallCache.Count == 0) 
            { 
                onWall = false; 
            }
        }
    }
}

Basically what i do is keeping track in a HashSet of all objects the character collides with. I use the angle to determine if a collision is to be treated as character vs. ground or character vs. wall collision. In OnSeparation I remove the objects from the according set and if it is empty, the character no longer collides with anything. Using a set is crucial here, otherwise you have to check for yourself that you don't add the same Fixture multiple times during OnCollision as it fires on every update.

HTH a bit.

Sep 14, 2010 at 5:12 PM

Really helpful, thanks. I've plugged it in and on the face of it, it seems to work a charm. Many thanks :)