How can I slow down a Farseer body?

Topics: User Forum
Nov 24, 2013 at 11:08 AM
I have two bodies. A ball and a rock. If the ball touches the rock, the rock should get removed and the ball should slow down a little because it has touched the rock. The rock gets removed when the ball is touching it, but the ball is not slowing down. I set the rock's friction to 0.6f to slow down the ball, but the ball is not getting slower when it touches the rock. Why is friction not working here?

What is wrong? Why is the ball not getting slower when it touches the rock? How can I solve the problem?

You can see it in this video: Video
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    World world;
    Body Ball, Rock;
    Texture2D BallSprite, RockSprite;
    List<Vertices> list = new List<Vertices>();
    Vector2 _origin;
    float _scale;
    bool removeRock = false;
    bool DrawRock = true;

    private static float _displayUnitsToSimUnitsRatio = 100f;
    public static Vector2 ToDisplayUnits(Vector2 simUnits)
    {
        return simUnits * _displayUnitsToSimUnitsRatio;
    }

    public bool Rock_OnCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
    {
        if (removeRock == false)
        {
          if (fixtureB.CollisionCategories == Category.Cat15)
          {
            //remove the rock body if the ball touches it
            removeRock = true;
            DrawRock = false;
          }
        }
        return true;             
    }      

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        TargetElapsedTime = TimeSpan.FromTicks(333333);
        InactiveSleepTime = TimeSpan.FromSeconds(1);
        graphics.IsFullScreen = true;
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);        
        if (world == null)
        {
            world = new World(new Vector2(0, 1));
        }
        else
        {
            world.Clear();
        }

        BallSprite = Content.Load<Texture2D>("ballbig");
        RockSprite = Content.Load<Texture2D>("rocksprite");
        //create the ball
        Ball = BodyFactory.CreateCircle(world, 0.50f, 1.0f);
        Ball.BodyType = BodyType.Dynamic;
        Ball.Position = new Vector2(graphics.GraphicsDevice.Viewport.Width / 200f, graphics.GraphicsDevice.Viewport.Height / 200f-4f);
        Ball.Restitution = 0.5f;
        Ball.LinearVelocity = new Vector2(0, 2.0f);
        Ball.CollisionCategories = Category.Cat15;

        //create the rock
        uint[] data = new uint[RockSprite.Width * RockSprite.Height];
        RockSprite.GetData(data);
        Vertices textureVertices = PolygonTools.CreatePolygon(data, RockSprite.Width, false);
        Vector2 centroid = -textureVertices.GetCentroid();
        textureVertices.Translate(ref centroid);
        _origin = -centroid;
        textureVertices = SimplifyTools.ReduceByDistance(textureVertices, 4f);
        list = BayazitDecomposer.ConvexPartition(textureVertices);
        _scale = 1f;
        Vector2 vertScale = new Vector2(ConvertUnits.ToSimUnits(1)) * _scale;
        foreach (Vertices vertices in list)
        {
            vertices.Scale(ref vertScale);
        }

        Rock = BodyFactory.CreateCompoundPolygon(world, list, 1.0f, BodyType.Static);
        Rock.IsSensor = true;
        Rock.Position = new Vector2(4.00f, 2.50f);
        Rock.Rotation = 0f;
        Rock.Friction = 0.6f;
        Rock.CollisionCategories = Category.Cat4;
        Rock.OnCollision += Rock_OnCollision;       
    }

    protected override void Update(GameTime gameTime)
    {
        float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
        world.Step(Math.Min(elapsed, (1f / 60f)));

        if (removeRock)
        {
            world.RemoveBody(Rock);
            removeRock = false;
        }
        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
          spriteBatch.Draw(BallSprite, ConvertUnits.ToDisplayUnits(Ball.Position), null, Color.White, Ball.Rotation, new Vector2(BallSprite.Width / 2.0f, BallSprite.Height / 2.0f), 1f, SpriteEffects.None, 0f);
          if (DrawRock == true)
        {
          spriteBatch.Draw(RockSprite, ConvertUnits.ToDisplayUnits(Rock.Position), null, Color.White, Rock.Rotation, _origin, _scale, SpriteEffects.None, 0f);
        }
        spriteBatch.End();
        base.Draw(gameTime);
    }
}
Nov 26, 2013 at 11:03 AM
Edited Nov 26, 2013 at 11:04 AM
What happens if you change the restitution of the rock? I think that may be more relevant than it's friction.

Also are the collision categories setup so they actually collide?
Nov 26, 2013 at 1:47 PM
Rock.Restitution = 0.2f;
I added restitution to the rock but nothing happens when they collide. The ball doesn't get slower. The bodies collide because the rock is getting removed when the ball touches it.
Nov 26, 2013 at 4:56 PM
I solved the problem like this:
 public bool Rock_OnCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
    {
        if (removeRock == false)
        {
          if (fixtureB.CollisionCategories == Category.Cat15)
          {
            //remove the rock body if the ball touches it
            Ball.LinearVelocity = new Vector2(Ball.LinearVelocity.X * 0.6f, Ball.LinearVelocity.Y * 0.6f);
            removeRock = true;
            DrawRock = false;
          }
        }
        return true;             
    }