[Solved] Polygon out of position during collision

Topics: Developer Forum
Apr 26, 2010 at 6:02 AM

I have created a simple triangle polygon and rectangle. My triangle sits below the rectangle and is static. The rectangle, using gravity falls on to the triangle. The rotation appears to be working fine. However, I keep having this problem where there is a large gap between each object when they collide. I've tried different implementations and playing with the numbers to no success. Any help would be greatly appreciated. Thanks.

Code for Settings up the Rectangle and Triangle:

// Triangle
triangleSprite = Content.Load<Texture2D>("triangle");
data = new uint[triangleSprite.Width * triangleSprite.Height];
triangleSprite.GetData(data);
verts = Vertices.CreatePolygon(data, triangleSprite.Width, triangleSprite.Height);
triangleBody = BodyFactory.Instance.CreatePolygonBody(physicsSimulator, verts, 5);
triangleBody.Position = new Vector2(400, 600);
triangleGeomtry = GeomFactory.Instance.CreatePolygonGeom(physicsSimulator, triangleBody, verts, 0);
triangleBody.IsStatic = true;

// Rectangle
myTexture = Content.Load<Texture2D>("floor_tile");
rectBody = BodyFactory.Instance.CreateRectangleBody(physicsSimulator, myTexture.Width, myTexture.Height, 1);
rectBody.Position = new Vector2(400f, 0f);
rectBody.IsStatic = false;
rectGeom = GeomFactory.Instance.CreateRectangleGeom(physicsSimulator, rectBody, 256, 256);

Code for Drawing:

// Rectangle
spriteBatch.Draw(myTexture,
    rectBody.Position,
    null,
    Color.White,
    rectBody.Rotation,
    new Vector2(myTexture.Width / 2, myTexture.Height / 2),
    1.0f,
    SpriteEffects.None,
    1f);

// Triangle
spriteBatch.Draw(triangleSprite, 
    triangleBody.Position, 
    null, 
    Color.White, 
    triangleBody.Rotation, 
    new Vector2(triangleSprite.Width/2, triangleSprite.Height/2), 
    1.0f, 
    SpriteEffects.None, 
    1f);

Apr 26, 2010 at 6:21 AM
Edited Apr 26, 2010 at 9:37 PM

After reading some additional sites, I've seen mention that it is smart to use the Debug View to see where the actual geometries are drawn. Any suggestions on how to include that in my own project without including additional projects?

Edit:

After reading through other code and forum posts, I have come to the conclusion that I'm not including my true Polygon Origin in my drawing code. I understand how to find the origin of my triangle by using the "verts.getCentroid()" function. However, I'm not sure how I go about using that in my drawing code. I've tried different things with crazy results, especially when it comes to changing my "origin" argument in the drawing routine.

If anyone could show me how to draw my polygon sprite properly, or even point me to a good resource where I can learn, I would be very appreciative. Thanks again.

Edit #2:

I solved my own problem of including the debug view into my project. I actually just created a new project within the Farseer XNA Solution and copied over my pre-existing code. Then I included the PhysicsSimulatorView object and called it's drawing routine from my Draw() function. A rough post on how to accomplish this can be seen at: http://farseerphysics.codeplex.com/Thread/View.aspx?ThreadId=16625 but you can't just take the code word-for-word. It took a little modifcation to get it working with XNA 3.1 and Farseer 2.1.

Now I see that my geometries are very displaced from the position my textures are being drawn. I'm going to try to figure this. If I figure something out I'll post it for future people who have this problem. If anyone has any suggestions in the mean time feel free to share.

Apr 26, 2010 at 9:53 PM

Solution to my problem:

Notes:

  1. The PolygonOrigin calculation must be performed before the BodyFactory or GeometryFactory touch the vertices. I'm not sure why but that affected my results.
  2. In XNA's SpriteBatch.Draw function, the rotation's origin point should be the polygonOrigin from your sprite's vertices. See code below 

Working Code (Cleaned up):

// Create Triangle Polygon w/ Texture (Sprite)
triangleSprite = Content.Load<Texture2D>("triangle"); // Load the Sprite
data = new uint[triangleSprite.Width * triangleSprite.Height]; // Array to Hold the Image's Data (Type: uint[])
triangleSprite.GetData(data); // Save the Sprite's Pixel Data to the 'data' array
verts = Vertices.CreatePolygon(data, triangleSprite.Width, triangleSprite.Height); // Calc. vert. points from Sprite (Type: Vertices)
polygonOrigin = verts.GetCentroid(); // Calculate the Center Offset. Not really sure how/why. (Type: Vector2)
triangleBody = BodyFactory.Instance.CreatePolygonBody(physicsSimulator, verts, 3);
triangleBody.Position = new Vector2(400, 600);
triangleGeomtry = GeomFactory.Instance.CreatePolygonGeom(physicsSimulator, triangleBody, verts, 0);


// Draw a Moving Triangle
spriteBatch.Draw(triangleSprite,
    triangleBody.Position,
    null,
    Color.White,
    triangleBody.Rotation,
    polygonOrigin,
    1.0f,
    SpriteEffects.None,
    1f);

Developer
Apr 26, 2010 at 10:30 PM

Since this is a physics forum I will answer the first question and then you will be able to figure out the rest on your own.

First you will need to copy

PhysicsSimulatorView.cs

and all of the DrawingSystem folder

CircleBrush.cs  DrawingHelper.cs  EllipseBrush.cs  LineBrush.cs  PolygonBrush.cs  and RectangleBrush.cs

and both the Common and Fonts folders and their contents into your Content Project.

Then you need to add

using DemoBaseXNA;
using DemoBaseXNA.DrawingSystem;

to the top of your whatever source file you do your drawing in.

Now create a new PhysicsSimulatorView in your game and init it with your PhysicsSimulator. You can do this in your Initalize() method.

Then call LoadContent() on your PhysicsSimulatorView object inside your games LoadContent method.

Now call Draw() on your PhysicsSimulatorView object with your games SpriteBatch object and be sure to call SpriteBatch.Begin() before and SpriteBatch.End() after.

Apr 26, 2010 at 10:37 PM

Thanks Matt. That is a much cleaner way to include the PhysicsSimulatorView than I was doing.

Apr 26, 2010 at 11:05 PM
Edited Apr 26, 2010 at 11:05 PM

I'm not sure if this is the proper place, but I went ahead and threw my working code into a class for others to use or learn from. Hopefully someone finds it useful.

 

/*
 *  Describes a very simple Game Object (Actor)
 *  The constructor initializes the object.
 *  Use the included draw() function during your SpriteBatch Loop to draw the object.
 */
class Actor
{
    // Physics
    public Body body;
    public Geom geometry;
    public Vertices vertices;
    public Vector2 origin;

    // Sprite
    public Texture2D sprite;
    private uint[] spriteData;

    public Actor(Texture2D texture, PhysicsSimulator physicsSimulator, float mass, float gridSize)
    {
        // Assign Variables
        sprite = texture;

        // Build Vertices from Sprite
        spriteData = new uint[sprite.Width * sprite.Height];
        sprite.GetData(spriteData);
        vertices = Vertices.CreatePolygon(spriteData, sprite.Width, sprite.Height);
        origin = vertices.GetCentroid();

        // Create Body and Geometry and Put them Into the Physics Engine
        body = BodyFactory.Instance.CreatePolygonBody(physicsSimulator, vertices, mass);
        geometry = GeomFactory.Instance.CreatePolygonGeom(physicsSimulator, body, vertices, gridSize);
    }

    public void draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(sprite,
            body.Position,
            null,
            Color.White,
            body.Rotation,
            origin,
            1.0f,
            SpriteEffects.None,
            1f);
    }
}