OnCollision event handler problems in C# XNA with Farseer Physics
Asked Answered
Z

3

6

I have this working ok(ish) in my game at the moment, but i'm not fantastic at maths. When two primatives collide, I want them to smash up into tiny bits if the force applied to a primative was over a set threshold. My collision event handler at present looks like this.

public bool Collision(Fixture fixtureA, Fixture fixtureB, Manifold manifold) 
{ 
   Vector2 position = manifold.LocalNormal; 
   float angle = (float)Math.Atan2(position.Y, position.X); 
   Vector2 force = Vector2.Zero; 
   if (angle < 0) 
     force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi + angle) * fixtureA.Body.LinearVelocity.Y); 
   else 
     force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi - angle) * fixtureA.Body.LinearVelocity.Y); 
   double XForce = Math.Sqrt(force.X * force.X); 
   double YForce = Math.Sqrt(force.Y * force.Y); 
   double totalForce = XForce + YForce; 
   if ((Breakable) && (totalForce > BreakForce)) 
   { 
      Breakable = false; 
      Active = false; 
      BreakUp(fixtureA, fixtureB); 
   } 
   return true; 
} 

I put that in a LONG time ago when I was just playing around. This causes a bit of a problem in certain situations. For example, if a primative is stationary on the floor and another primative falls onto it from a decent height, almost always, the falling box blows up and the resting box survives. Also if two boxes are falling side by side and give each other the tinyest of touches, then both boxes blow up mid air. Hmmmmm, not really perfect that. Does anyone have any idea how to improve my collision handler? Thanks in advance.

Zoril answered 22/7, 2010 at 10:50 Comment(0)
A
6

(While this is currently the accepted answer - I would direct anyone to my other answer for a potentially superior approach.)

Your calculation of impact force is completely wrong. You need to get the relative velocity at the contact point(s) - you're getting something quite strange...

Your code looks like it's using Farseer 3.0 (you should specify, because that's more of a fork of Box2DX than Farseer 2.1). What I did in Farseer 2.1 (where you've got ContactList contacts instead of a Manifold) to get the impact velocity was:

foreach(Contact contact in contacts)
{
    Vector2 position = contact.Position;
    Vector2 v0;
    me.Body.GetVelocityAtWorldPoint(ref position, out v0);
    Vector2 v1 = new Vector2();
    if(!hit.Body.IsStatic)
        hit.Body.GetVelocityAtWorldPoint(ref position, out v1);
    v0 -= v1;

    float hitVelocity = v0.Length();
    // To then get the force, you need the mass of the two objects
}

From a brief look at the Farseer 3.0 source, it seems that Manifold has a member:

public FixedArray2<ManifoldPoint> Points;

And both Manifold and ManifoldPoint have members:

public Vector2 LocalPoint;

It should be fairly simple to modify my Farseer 2.1 code to use those instead.

Also: I recommend simply marking the two objects as needing to break, and then actually breaking them after your physics update finishes running (rather than in the collision handler).

Accouchement answered 22/7, 2010 at 11:20 Comment(5)
Hi Andrew, Thanks for your response. My BreakUp() function simply adds a load of new primatives into a list based on the position of the breaking primative (the chips if you like) they dont get added till the next physics "Step()". Similarly a box being destroyed is simply flagged to be removed in the nextstep. So in your example ... I would use LocalPoint rather than the position variable in your function correct? Also I can see this creates a hitVelocity variable but I'm unsure how this relates to the force applied to the object. I know F = MA but dont angles come into play here?Zoril
Yes use LocalPoint. My Position variable is in world space (so find the local space equivalent of GetVelocityAtWorldPoint). To find force you need the acceleration (F=MA). You can approximate acceleration by determining how much the velocity will change in that frame (difference in velocity divided by time).Accouchement
@DrLazer: Oh, and, getting the velocity at a specific point will take care of all the angle stuff for you.Accouchement
Just one last thing .... you are using "me" and "hit". My assumption is that me is the class/object/primative/whatever you are running the function on and "hit" is the object it is colliding with. Just want to be certain because I guess this part of the app is vital. Oh and is the change in velocity the difference between hitVelocity and its current velocity?Zoril
@DrLazer: Your assumption is correct. And hitVelocity is the relative velocity of the collision between the two objects at the point of collision (ie: taking into consideration rotation/angular velocity). In other words it is a good approximation of how hard the impact was - but it doesn't take into consideration mass. I was using this to determine if I should play an impact sound effect and how loud it should be. For your purposes, I now recommend my other answer: #3308512Accouchement
A
8

Ok, so my other answer is viable. But I've looked at Farseer 3.0 (the current SVN version) more closely and found that it already implements almost exactly what you are trying to do.

Look for "BreakableBody.cs". You may be able to directly use that - but otherwise you could just copy out the functionality you want.

Specifically: Instead of attaching a function to your fixture's OnCollision you want to attach one to PostSolve. It takes a ContactConstraint which you can dive into and find the impulses from the collision.

This is the implementation of that function used by BreakableBody:

private void PostSolve(ContactConstraint contactConstraint)
{
    if (!Broken)
    {
        float maxImpulse = 0.0f;
        for (int i = 0; i < contactConstraint.manifold.PointCount; ++i)
        {
            maxImpulse = Math.Max(maxImpulse,
                         contactConstraint.manifold.Points[0].NormalImpulse);
            maxImpulse = Math.Max(maxImpulse,
                         contactConstraint.manifold.Points[1].NormalImpulse);
        }

        if (maxImpulse > Strength)
        {
            // Flag the body for breaking.
            _break = true;
        }
    }
}

Apparently the impulse data in the manifolds is only set correctly in PostSolve, not in OnCollision.

Accouchement answered 22/7, 2010 at 14:20 Comment(2)
Mr Russell you are a true Gent. Thankyou kindly for your assistance, I'll ditch the code at this point and pretty much steal that functionality from there. Im now intrigued as to what setting "_break" to true will achieve ... hmmmmmmZoril
It flags the Update() function to perform the actual breaking (I had meant to link the code: farseerphysics.codeplex.com/SourceControl/changeset/view/…)Accouchement
A
6

(While this is currently the accepted answer - I would direct anyone to my other answer for a potentially superior approach.)

Your calculation of impact force is completely wrong. You need to get the relative velocity at the contact point(s) - you're getting something quite strange...

Your code looks like it's using Farseer 3.0 (you should specify, because that's more of a fork of Box2DX than Farseer 2.1). What I did in Farseer 2.1 (where you've got ContactList contacts instead of a Manifold) to get the impact velocity was:

foreach(Contact contact in contacts)
{
    Vector2 position = contact.Position;
    Vector2 v0;
    me.Body.GetVelocityAtWorldPoint(ref position, out v0);
    Vector2 v1 = new Vector2();
    if(!hit.Body.IsStatic)
        hit.Body.GetVelocityAtWorldPoint(ref position, out v1);
    v0 -= v1;

    float hitVelocity = v0.Length();
    // To then get the force, you need the mass of the two objects
}

From a brief look at the Farseer 3.0 source, it seems that Manifold has a member:

public FixedArray2<ManifoldPoint> Points;

And both Manifold and ManifoldPoint have members:

public Vector2 LocalPoint;

It should be fairly simple to modify my Farseer 2.1 code to use those instead.

Also: I recommend simply marking the two objects as needing to break, and then actually breaking them after your physics update finishes running (rather than in the collision handler).

Accouchement answered 22/7, 2010 at 11:20 Comment(5)
Hi Andrew, Thanks for your response. My BreakUp() function simply adds a load of new primatives into a list based on the position of the breaking primative (the chips if you like) they dont get added till the next physics "Step()". Similarly a box being destroyed is simply flagged to be removed in the nextstep. So in your example ... I would use LocalPoint rather than the position variable in your function correct? Also I can see this creates a hitVelocity variable but I'm unsure how this relates to the force applied to the object. I know F = MA but dont angles come into play here?Zoril
Yes use LocalPoint. My Position variable is in world space (so find the local space equivalent of GetVelocityAtWorldPoint). To find force you need the acceleration (F=MA). You can approximate acceleration by determining how much the velocity will change in that frame (difference in velocity divided by time).Accouchement
@DrLazer: Oh, and, getting the velocity at a specific point will take care of all the angle stuff for you.Accouchement
Just one last thing .... you are using "me" and "hit". My assumption is that me is the class/object/primative/whatever you are running the function on and "hit" is the object it is colliding with. Just want to be certain because I guess this part of the app is vital. Oh and is the change in velocity the difference between hitVelocity and its current velocity?Zoril
@DrLazer: Your assumption is correct. And hitVelocity is the relative velocity of the collision between the two objects at the point of collision (ie: taking into consideration rotation/angular velocity). In other words it is a good approximation of how hard the impact was - but it doesn't take into consideration mass. I was using this to determine if I should play an impact sound effect and how loud it should be. For your purposes, I now recommend my other answer: #3308512Accouchement
C
2

I haven't used XNA, but as a physics problem, why not just subtract the linear velocity of A from the linear velocity of B, and get the 'force' as the square of the resulting vector (sum the squares of the components)? That should harmonize with the kinetic energy involved according to `E=(mv^2)/2', for a very simple physical model, even if we are ignoring the masses (or, for that matter, elasticity or the distinction between energy and momentum). If the objects are moving in the same general direction at the same speed, you get a small value; if one is approaching (or, of course, departing!) at high speed, you get a large value.

Cryptoclastic answered 22/7, 2010 at 11:11 Comment(2)
This wouldn't work for, for example, a long rotating beam that "smacks" another object.Accouchement
@Andrew: Correct, it wouldn't. Just somewhat more realistic than the posted code...Cryptoclastic

© 2022 - 2024 — McMap. All rights reserved.