1
\$\begingroup\$

I am new to Farseer Engine used with MonoGame so I decided to do some test with the API to see the results and learn.

If I understand, in Farseer the Position property of a Body object is located at it's center. Wich is unusual.

Ok, now that read all the tutorial on Farseer I wanted to test the Texture 2 Polygon algorithm. So I have my Heart shaped JPG transformed in a real Heart shape in my game ! It worked just well ! Collision were all fine. I am using Farseer Debug Draw to render physics object.

The problem is that I also tested Joints on my Heart polygons and I discovered something Strange and so annoying. First I tried to connect 2 DYNAMIC Heart Polygon with a simple Distance Joint. It worked well because I used both Hearts.WorldCenter as Anchors

The problem is that when I try to change the type of one of my Heart to Static, the centering and positionning is all messed up ! The actual position of a static heart is top corner and its WorldCenter property too ! What happen ? And how to get back to normal centers ?

Here is a picture rendering all Body.WorldCenter Vectors with a blue square. Static objects are green and Dynamic are red.enter image description here

using System.Collections.Generic;
using FarseerPhysics;
using FarseerPhysics.Common;
using FarseerPhysics.Common.Decomposition;
using FarseerPhysics.Common.TextureTools;
using FarseerPhysics.DebugView;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace FarseerTest {

/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    private Body ball;
    private Body ball2;
    private Body poly;
    private Body ground;
    private Body heart;
    private Body heart2;

    World w = new World(new Vector2(0, 9f));

    KeyboardState kbs = new KeyboardState();

    DebugViewXNA debug;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        graphics.PreferredBackBufferHeight = 700;
        graphics.PreferredBackBufferWidth = 1500;
        graphics.ApplyChanges();
        ConvertUnits.SetDisplayUnitToSimUnitRatio(32);

    }


    protected override void Initialize()
    {
        ball = BodyFactory.
            CreateCircle(w, 1, 1, new Vector2(3, 1));

        ball.BodyType = BodyType.Dynamic;
        ball.Restitution = 0.1f;
        ball.Friction = 1f;

        ball2 = BodyFactory.
            CreateCircle(w, 3, 1, new Vector2(8, 3));

        ball2.BodyType = BodyType.Dynamic;
        ball2.Restitution = 0.1f;

        poly = BodyFactory.CreatePolygon(w,
            new Vertices(new List<Vector2>(new[]
            { new Vector2(-4, 0), new Vector2(0, 4),
                new Vector2(4, 0) })), 100);

        poly.Position = new Vector2(19, 5);
        poly.BodyType = BodyType.Dynamic;
        poly.Friction = 0.3f;

        ground = BodyFactory.
            CreateRectangle(w, 500, 2, 9, new Vector2(0, 20));

        ground.BodyType = BodyType.Static;

        BodyFactory.CreateRectangle(w, 2, 500, 9,
            new Vector2(1, 0)).BodyType = BodyType.Static;

        BodyFactory.CreateRectangle(w, 2, 500, 9,
            new Vector2(ConvertUnits.ToSimUnits(
            graphics.PreferredBackBufferWidth)-1,0))
            .BodyType = BodyType.Static;

        BodyFactory.CreateRectangle(w, 500, 2, 9,
            new Vector2(0, -1)).BodyType = BodyType.Static;


        for (int i = 0; i < 35; i++)
        {
            Body b = BodyFactory.CreateCapsule(w,
                0.5f, 0.4f, 0.02f);

            b.Position = new Vector2(i, 0);
            b.BodyType = BodyType.Dynamic;
            b.GravityScale = 0.1f;
            b.Restitution = 0.6f;
        }


        //Debug view
        debug = new DebugViewXNA(w);
        debug.RemoveFlags(DebugViewFlags.Controllers);
        //debug.AppendFlags(DebugViewFlags.AABB);
        debug.DefaultShapeColor = Color.Red;
        debug.ActiveAlpha = 1;

        base.Initialize();
    }

    private Body circle;
    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        debug.LoadContent(GraphicsDevice, Content);

        Texture2D heartTex = Content.Load<Texture2D>("heart");

        heart = CreatePolygonFromTexture(heartTex,
            w, 0.1f, new Vector2(27, 5), 0.5f);

        heart.BodyType = BodyType.Static;


        heart2 = CreatePolygonFromTexture(heartTex,
            w, 0.1f, new Vector2(10, 5), 0.5f);

        heart2.BodyType = BodyType.Dynamic;

        DistanceJoint j = JointFactory.
            CreateDistanceJoint(w, heart, heart2,
            heart.WorldCenter, heart2.WorldCenter, true);

        j.Frequency = 0.8f;
        j.DampingRatio = 0.05f;
        j.CollideConnected = true;
        //j.Breakpoint = 67f;

        Body box = BodyFactory.
            CreateRectangle(w, 3, 3, 3, new Vector2(20, 5));
        box.BodyType = BodyType.Static;

        Body box2 = BodyFactory.
            CreateRectangle(w, 3, 3, 3, new Vector2(15, 5));
        box2.BodyType = BodyType.Dynamic;

        circle = BodyFactory.
            CreateCircle(w, 1, 1, Vector2.Zero);
        circle.IsSensor = true;

        DistanceJoint j2 = JointFactory.
            CreateDistanceJoint(w, box, box2,
            box.WorldCenter, box2.WorldCenter, true);

        j2.Frequency = 0.5f;
        j.DampingRatio = 0.1f;

        for (int i = 0; i < 10; i++)
        {
            Body b = CreatePolygonFromTexture(heartTex,
                w, 0.1f, new Vector2(5, 2), 0.3f);

            b.Position = new Vector2(i * 5, 2);
            b.BodyType = BodyType.Dynamic;
            b.GravityScale = 0.1f;
            b.Restitution = 0.6f;
        }

    }


    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }


    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back
                == ButtonState.Pressed
            || Keyboard.GetState().IsKeyDown(Keys.Escape))
        {
            Exit();
        }

        kbs = Keyboard.GetState();

        if(kbs.IsKeyDown(Keys.Right))
        {
            ball.ApplyForce(new Vector2(14, 0));
        }
        else if (kbs.IsKeyDown(Keys.Left))
        {
            ball.ApplyForce(new Vector2(-14, 0));
        }

        if (kbs.IsKeyDown(Keys.Up))
        {
            ball.ApplyLinearImpulse(new Vector2(0, -6));
            //heart2.ApplyLinearImpulse(new Vector2(0, -6));
        }
        else if (kbs.IsKeyDown(Keys.Down))
            ball.ApplyLinearImpulse(new Vector2(0, 6));

        circle.Position = poly.WorldCenter;
        w.Step(1f/30f);

        base.Update(gameTime);
    }


    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin();

        // spriteBatch.Draw(CreateRectangle(100, 100, Color.Red),
        // body.Position, null, Color.White, body.Rotation,
        // Vector2.Zero, 1f, SpriteEffects.None, 0f);

        var projection =
            Matrix.CreateOrthographicOffCenter(0f,
            ConvertUnits.ToSimUnits(
            graphics.GraphicsDevice.Viewport.Width),
            ConvertUnits.ToSimUnits(
            graphics.GraphicsDevice.Viewport.Height),
            0f, 0f, 1f);

        debug.RenderDebugData(ref projection);

        foreach (Body body in w.BodyList)
        {
            spriteBatch.Draw(
                CreateRectangle(10, 10, Color.Blue),
                ConvertUnits.ToDisplayUnits(body.WorldCenter),
                Color.White);
        }

        spriteBatch.End();

        base.Draw(gameTime);
    }

    public Body CreatePolygonFromTexture(Texture2D tex,
        World world, float density, Vector2 position,
        float scale, TriangulationAlgorithm algorithm
        = TriangulationAlgorithm.Bayazit)
    {
        uint[] texData = new uint[tex.Width * tex.Height];
        tex.GetData<uint>(texData);

        Vertices vertices = TextureConverter.
            DetectVertices(texData, tex.Width);

        List<Vertices> vertexList = Triangulate.
            ConvexPartition(vertices, algorithm);

        Vector2 vertScale = new
            Vector2(ConvertUnits.ToSimUnits(scale));

        foreach (Vertices vert in vertexList)
            vert.Scale(ref vertScale);

        Vector2 centroid = -vertices.GetCentroid(); 
        vertices.Translate(ref centroid); 
        //basketOrigin = -centroid;

        return BodyFactory.CreateCompoundPolygon(w,
            vertexList, density, position);
    }

    public Texture2D CreateRectangle(int width, int height, Color c)
    {
        Texture2D rectangleTexture = new
            Texture2D(this.GraphicsDevice, width, height);

        Color[] color = new Color[width * height];

        for (int i = 0; i < color.Length; i++)
        {
            color[i] = c;
        }
        rectangleTexture.SetData(color);
        return rectangleTexture;
    }
    }}

Also, you can see that my 2 Bodies box and box2 show that the center of mass of a static body Rectangle is properly calculated as shown on the picture. Thanks !

\$\endgroup\$
  • \$\begingroup\$ Please provide your source code in the question, or at least essential parts of it. \$\endgroup\$ – Khaled.K Feb 1 '16 at 20:17
  • \$\begingroup\$ Ok I will do that. \$\endgroup\$ – Marc-Antoine Jacob Feb 1 '16 at 20:18
  • \$\begingroup\$ @KhaledKhnifer done \$\endgroup\$ – Marc-Antoine Jacob Feb 1 '16 at 20:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.