Multiple Bodies OnSeperation

Topics: User Forum
Mar 18, 2013 at 1:08 AM
Hello,

I was wondering if there is a way to detect multiple bodies on sepration, right now in my code i can detect only one body at a time.

Let me explain what i am trying to do, I have BodyA which is "AI_Car" and other bodies labeled "Player", "Car", and "AI_Car." BodyA has a circle fixture attached labeled "Collision Radius" and this fixture is a sensor.

What I want to do with the AI_Car's Collision Radius is detect all other bodies colliding with it, and when all of them have sperated from this collision radius.

I can get the OnCollision event handler to work with all the bodies, but for the OnSepration event handler I can only get it to work with one body at a time.

Does anyone know what i can do?

Thank you for any advice.
Mar 18, 2013 at 1:56 AM
It might be easiest just to check which bodies are in contact with your collision sensor each Update, instead of relying on the collide/separate events. Checkout the method I posted here for help on how to do that. http://farseerphysics.codeplex.com/discussions/435983

Hope that helps,
Mike
Mar 18, 2013 at 2:22 AM
Thanks for the reply but i would have to admit that i am a newbie to this physics engine.

could you please explain how i could use this code
 private List<Body> GetBodiesInContactWithBody(Body body)
{
            var bodiesInContact = new List<Body>();
            var c = body.ContactList;
            while (c != null && c.Next != null)
            {
                if (c.Contact.IsTouching())
                {
                     bodiesInContact.Add(c.Other);
                     // if the above doesn't work ( haven't tried this code) try the below:
                     // if (c.Contact.FixtureA.Body == body)
                     //     bodiesInContact.Add(c.Contact.FixtureB.Body);// FixtureA is the body we're getting contacts for, so add fixtureb
                     // else 
                     //     bodiesInContact.Add(c.Contact.FixtureA.Body);
                     
                } 
                c = c.Next;
            }
            return bodiesInContact;
}
could you explain how isTouching works and how to use it when its not touching also do i use this like Oncollision event handler like set my Body +=GetBodiesInContactWithBody()?

Thanks for the help
Mar 18, 2013 at 2:54 AM
Sure. In your main game loop (Update() if you're using XNA), you can check to see if any bodies are in contact with your collision sensor (BodyA) using the code above. So, based on your original post, I think you want something like this:
private bool hasCollided = false;
public void Update() // whatever your main game loop is called
{
       // check collisions
       var collisions = GetBodiesInContactWithBody(AI_Car);
       if (collisions.Count <= 1) // I'm assuming you have a "ground" body that your cars are driving on, set this to 0 instead of 1 if that is not the case
       {
           // AI_Car is not in contact with anything besides the ground, see if it was colliding
           if (hasCollided)
           {
               // was colliding, now separated from all colliding bodies
               HandleOnSeparation();
               hasCollided = false;
           }
       }
       else
       {
             // in a collision
            hasCollided = true;
       }
Hope that clears it up for you,
Mike
Mar 19, 2013 at 12:55 AM
Thank you for the help this is working great for the bodies, but now i have another problem.

My problem is that i am trying to see if there are other Bodies colliding with BodyA's Car Fixture which is a circle that is a sensor to detect when an object is in the AI cars radius. i hope that makes sense.

With this code i am only able to detect the whole body as a collision and not specific fixtures.

does anyone know what i need to do?

i want BodyA's fixture to detect other bodies in the contact list with the code provided here or something similar.

Thank you for the help
Mar 19, 2013 at 3:52 PM
If you look at the GetBodiesInContactWithBody code, you can see that c.Contact has FixtureA and FixtureB properties. Sounds like you want to only add the body to the bodiesInContact return value if FixtureA or FixtureB is your collision sensor fixture. Something like this might work:
 private List<Body> GetBodiesInContactWithFixture(Fixture sensor)
{
            var bodiesInContact = new List<Body>();
            var c = sensor.Body.ContactList;
            while (c != null && c.Next != null)
            {
                if (c.Contact.IsTouching())
                {
                     if (c.Contact.FixtureA.FixtureId == sensor.FixtureId)
                          bodiesInContact.Add(c.Contact.FixtureB.Body);
                     else if (c.Contact.FixtureB.FixtureId == sensor.FixtureId)
                          bodiesInContact.Add(c.Contact.FixtureA.Body);
                     
                } 
                c = c.Next;
            }
            return bodiesInContact;
}
Good luck,
Mike.
Mar 19, 2013 at 5:57 PM
Thank you very much Mike, you have been very helpful.
Mar 19, 2013 at 6:28 PM
i ran into another problem, i am using debugview and i can see the size of my shapes on the screen, but this collision code shows the objects colliding a little further apart from eachother.

Would anyone know why this is happening?

Thanks for all the help.
Mar 19, 2013 at 7:20 PM
Probably just a difference in scale between what your modeling and what your drawing. Remember, the physics world (bodies, fixtures, collisions) and screen world (sprites, textures, drawing) are completely separate. If the bodies react to a collision, it's because they collided. If it doesn't look like they collided, then it's probably not being drawn correctly. I suspect there's a difference in scale between the model and drawing. I'm not sure if the debug view was intended to be pixel perfect -- draw with sprites and just tweak the scale until the physics world and the screen world align.

Hopefully that makes sense,
Mike.
Mar 22, 2013 at 1:51 AM
First of all Mike, Thanks for all the help you were right once again lol.

I fixed my drawing problem as far as i can tell, now the collision look a lot more precise.

Once again i have another problem, now i noticed that i want to detect fixtures detecting fixures only, because using this code i noticed even the whole body is being checked when all i need to check is a fixture from that body colliding with another fixture.

i have edited the GetBodiesInContactWithFixture() to try and only use the fixture to fixture contact but it doesnt work the way i need it to. I have also tried switching the bodiesInContact to a fixture list but i dont know how to return the value at the end of the function

Also i compair BodyId because i use a static list which hold the bodies and i have edited the BodyId to match the list so i can handle calls from any function.
        private List<Body> GetBodiesInContactWithBody(Fixture sensor)
        {
            var bodiesInContact = new List<Body>();
            var c = sensor.Body.ContactList;
            while (c != null && c.Next != null)
            {
                if (c.Contact.IsTouching())
                {
                    if (c.Contact.FixtureA.UserData == "Collision_Radius"
                        && c.Contact.FixtureB.UserData == "Car_Body"
                        | c.Contact.FixtureB.UserData == "AI_Car_Body"
                        | c.Contact.FixtureB.UserData == "Player"
                        && c.Contact.FixtureA.Body.BodyId != c.Contact.FixtureB.Body.BodyId)
                    {
                        bodiesInContact.Add(c.Contact.FixtureB.Body);
                    }
                    if (c.Contact.FixtureB.UserData == "Collision_Radius"
                            && c.Contact.FixtureA.UserData == "Car_Body"
                       | c.Contact.FixtureA.UserData == "AI_Car_Body"
                       | c.Contact.FixtureA.UserData == "Player"
                       && c.Contact.FixtureB.Body.BodyId != c.Contact.FixtureA.Body.BodyId)
                    {
                        bodiesInContact.Add(c.Contact.FixtureA.Body);
                    }
                }
                c = c.Next;
            }
            return bodiesInContact;
        }
Just when you think you have solved your problems another one arises.

Can anyone help me out with this one?


Thank for the help.
Mar 22, 2013 at 7:57 PM
Hi WaZz86. You're welcome. Glad I've been able to help you so far. As far as I know, Fixture does not have a contact list so you'll still need to loop over each fixture in the body until you find the fixture you're looking for. But once you've found it, you can exit (no need to keep looping). The other issues you're having seem to be more c# related than Farseer specifically (the if statements in your code; returning a Fixture list). Anyway, I think I understand what you're looking for, so here you go :) But I think if you continue to have problems of this nature, you might be better served reading up on c# because I don't think they're related to the physics engine specifically.
private string[] contactFixtures = new string [] {"Car_Body", "AI_Car_Body", "Player"};

private List<Fixture> GetFixturesInContactWithCollisionRadius(Fixture collisionRadius)
        {
            Debug.Assert(collisionRadius.UserData == "Collision_Radius");
            var fixturesInContact = new List<Fixture>();
            var c = collisionRadius.Body.ContactList;
            while (c != null && c.Next != null)
            {
                if (c.Contact.IsTouching() == false || 
                    c.Contact.FixtureA.Body.BodyId == c.Contact.FixtureB.Body.BodyId ||
                    (c.Contact.FixtureA.UserData != "Collision_Radius" && c.Contact.FixtureB.UserData != "Collision_Radius"))
                    continue;
                      
                    if (c.Contact.FixtureA.UserData == "Collision_Radius" &&
                        contactFixtures.Contains((string)c.Contact.FixtureB.UserData))
                    {
                        fixturesInContact.Add(c.Contact.FixtureB);
                    }
                    else if (c.Contact.FixtureB.UserData == "Collision_Radius" &&
                      contactFixtures.Contains((string)c.Contact.FixtureA.UserData)))
                    {
                        fixturesInContact.Add(c.Contact.FixtureA);
                    }
                }
                c = c.Next;
            }
            return fixturesInContact ;
        }
Cheers,
Mike.