Proper Documentation and 4.0 Compatible

Oct 14, 2010 at 11:40 PM

I am getting so frustrated.  I was doing just fine with v2.1, had everything figured out for the most part.  Objects were colliding and bouncing and everyone was happy.  How with 3.0 I cannot figure out much.  I have spent the last three evenings reading and re-reading the Box2D manual but it is not at all helpful.  Is there some real documentation and a nice little tutorial on getting a  couple of sprites on the screen and get them colliding etc?  I don't understand the difference between a Body and a Fixture.Body.  I finally got a red sprite to drop from the top to the bottom with gravity but I can't get it to land on the blue sprite below it.  It just passes through.  At this point I guessed I must need a fixture or something on them.  Fumbled around with this for a bit and ended up with 4 bodies in the World object I created but nothing collides.  Still don't understand this whole meters vs pixels thing.  Passed in the texture.width / 100 and texture.height / 100 and got an exception error because it thought the value was less than 0.

Any quick tutorial on this would be helpful.

 

Thanks,

 

Adam

Coordinator
Oct 14, 2010 at 11:46 PM

We are a free project that is driven by contributions and the work of volunteers. If anyone writes a tutorial, I'll be more than happy to link it/post it to the wiki.

As for a simple sample on how to make things collide; try out the HelloWorld sample. FPE 3.1 will be compatible with XNA 4.0.

Oct 15, 2010 at 5:08 AM
Edited Oct 15, 2010 at 6:04 AM
CannedGames wrote:

 Still don't understand this whole meters vs pixels thing.  Passed in the texture.width / 100 and texture.height / 100 and got an exception error because it thought the value was less than 0.

Well, I can tell you this for now: change your 100 to 100.0f so it's a float. That's why it's returning 0. I had this problem myself when first learning. :)

There is no difference in a Body and a Fixture.Body. A fixture is just an object that also contains your body.

 

I know how you feel. I had farseer 2.x working so easily with no problem. Quit that project and came back several months later and 3.0 was out, and it felt so impossible to figure out. Hardly any documentation at all and box2d's manual doesn't transfer all that well. I'm not the best at farseer by far, but maybe I can whip up a little example soon and post it.

 

edit: Got bored and frustrated w/not really making any progress on my own game, so I made a tutorial to demonstrate very basic farseer collision. If anyone wants, I can find some place to zip up the whole solution+textures and upload it somewhere.

 

 

using System;
using FarseerPhysics.Factories;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using FarseerPhysics.Dynamics;

// This tutorial will demonstrate how to make a dynamic body
// fall due to gravity and land on a static body.

namespace FarseerCollisionDemo
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        // Farseer needs a physics world. The vector passed into its constructor is your gravity value. I just used 9.8
        // since that's what gravity actually is. Not even sure if that ends up scaling well in farseer. It all depends
        // on what you want the feel of your game to be.
        World physicsWorld = new World(new Vector2(0.0f, 9.8f));

        // Textures to load in our two images.
        Texture2D platformTexture, boxTexture;

        // Farseer uses fixtures, which contain a reference to a Body inside of it. Not going to explain this much more since
        // I don't really know it all that well.
        Fixture platformFixture, boxFixture;

        // Our initial positions for our two textures. Essentially our draw positions.
        Vector2 platformPosition = Vector2.Zero;
        Vector2 boxPosition = Vector2.Zero;

        // This will be our conversion factor. This converts the units to "meters". Don't take the term meters too seriously.
        // It is just some arbitrary scalar value so you can convert your units to be small enough to work well with farseer.
        // It can be whatever you want, just so long as it'll make your units between .1 and 10 or something like that. For
        // example: the box texture in this tutorial has a width of 64. That's what we want it to be when we're drawing it,
        // but that's way too big for farseer. Dividing it by 100 will make it 6.4, which makes it fit our range perfectly.
        // Mainly, whenever you are dealing with your Body inside farseer, you want to convert it to this range by dividing
        // by your scalar. Whenever you want to draw it to the screen, you need to convert it back by multiplying by 100.
        const float pixelsPerMeter = 100.0f;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            graphics.PreferredBackBufferWidth = 1280;
            graphics.PreferredBackBufferHeight = 720;
            graphics.ApplyChanges();

            base.Initialize();
        }

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

            // TODO: use this.Content to load your game content here

            // Setup the box.
            boxTexture = Content.Load<Texture2D>("box");

            // Just centering its position on the screen and making it start high up so it'll fall down at the beginning.
            boxPosition.X = (1280 / 2) - (boxTexture.Width / 2);
            boxPosition.Y = 100;

            // Use a fixture factory to create a rectangle that will represent your body. Since this texture is 64x64, we want to create a 64x64
            // rectangle around it. But remember, since we're dealing with farseer, we need to use our pixelsPerMeter value to scale it down to
            // 6.4x6.4. We also need to pass in our physics world we instantiated above so the fixture factory can add the fixture to the world,
            // mainly. We then set its position to be it's draw position scaled by our value, pixelsPerMeter. Next we set its body type to be
            // dynamic, since this will be a moving object.
            boxFixture = FixtureFactory.CreateRectangle(physicsWorld, boxTexture.Width / pixelsPerMeter, boxTexture.Height / pixelsPerMeter, 1);
            boxFixture.Body.Position = new Vector2(boxPosition.X / pixelsPerMeter, boxPosition.Y / pixelsPerMeter);
            boxFixture.Body.BodyType = BodyType.Dynamic;


            // Setup the platform.
            platformTexture = Content.Load<Texture2D>("platform");
            platformPosition.X = (1280/2) - (platformTexture.Width / 2);
            platformPosition.Y = 600;

            // Same as the above fixture factory, except this body type is set to static, which means it won't be moving.
            platformFixture = FixtureFactory.CreateRectangle(physicsWorld, platformTexture.Width / pixelsPerMeter, platformTexture.Height / pixelsPerMeter, 1);
            platformFixture.Body.Position = new Vector2(platformPosition.X / pixelsPerMeter, platformPosition.Y / pixelsPerMeter);
            platformFixture.Body.BodyType = BodyType.Static;
        }

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

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // Gather basic input.
            GamePadState currentState = GamePad.GetState(PlayerIndex.One);

            // In this example, let's just make it jump straight up. Very basic, but no
            // need for it to be anything else for the purposes of this tutorial.
            if(currentState.IsButtonDown(Buttons.A))
            {
                boxFixture.Body.ApplyForce(new Vector2(0.0f, -10.0f));
            }

            // TODO: Add your update logic here
            // Now let's update our draw position to be whatever our values ended up being in farseer
            // after our gravity/forces were applied. Remember, since we're moving from farseer units to
            // drawing units, we need to multiply it by our pixelsPerMeter value again. Otherwise, our
            // position would be off by a factor of 100 or whatever.
            boxPosition.X = boxFixture.Body.Position.X * pixelsPerMeter;
            boxPosition.Y = boxFixture.Body.Position.Y * pixelsPerMeter;

            // Advance the physics world. If you don't do this, you won't see anything happen with your farseer bodies.
            physicsWorld.Step(Math.Min((float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f, (1f / 30f)));

            base.Update(gameTime);
        }

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

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(boxTexture, boxPosition, Color.White);
            spriteBatch.Draw(platformTexture, platformPosition, Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

 

Oct 16, 2010 at 9:59 PM

Thank you for that simple example.  That's all I needed.  I was making two mistakes.  I was creating a body using the world.CreateBody method and then creating a fixture and trying to create a fixture using its shape.  I just now realized that the Fixture also creates the body that it attaches itself to.

My second mistake was the pixels to meters conversion.  I was converting meters to pixels, but I wasn't converting back.

Again, thank you for your assistance.  I realize this is an open source project but a straight forward Hello World all inside the Game1 file is probably all most people need to get them over the initial hurdles.

 

Cheers!

Oct 18, 2010 at 2:10 AM
Edited Oct 18, 2010 at 3:41 PM

alkier, you don't have code to have the debug view working with your example, do you? I tried to do it myself and it didn't even end up right-side up.

EDIT: I would appreciate that zip you mentioned, too, because my sprites are "colliding," but they're not visually touching each other, so I think I screwed up my alignment somewhere along the line.

Oct 20, 2010 at 5:36 AM

Debug view was insanely hard for me to get working in my current project. The only way I got it working was with the help of jrommann in this thread http://farseerphysics.codeplex.com/Thread/View.aspx?ThreadId=229605 along with RC's camera/input class floating around here. That thread will most likely tell you what you need to know to get debug view working.

If you want me to send you the zip file, give me your email and I'll email it to you. Too lazy to upload it to somewhere like rapidfire or whatever. :D

Oct 20, 2010 at 5:39 AM
If you could email it to me at achrisfling (at) gmail (dot) com, I'd really appreciate it. I can tell after spending a couple days on it that it's just going to be a nightmare without some example stuff.
Oct 21, 2010 at 7:32 PM

So I'm really close now - my debug view lines up perfectly when I create a polygon fixture like so...

 

	    platformTexture = Content.Load<Texture2D>(@"TestContent\TestRectangle");
            platformPosition.X = (Global.Variables.Width / 2);
            platformPosition.Y = 500;

            Rectangle rect2 = platformTexture.Bounds;
            Vertices verts2 = new Vertices();
            verts2.Add(PositionToPhysicsWorld(new Vector2(rect2.Left, rect2.Top)));
            verts2.Add(PositionToPhysicsWorld(new Vector2(rect2.Right, rect2.Top)));
            verts2.Add(PositionToPhysicsWorld(new Vector2(rect2.Right, rect2.Bottom)));
            verts2.Add(PositionToPhysicsWorld(new Vector2(rect2.Left, rect2.Bottom)));
            
            // farseer platform stuff
            platformFixture = FixtureFactory.CreatePolygon(physics, verts2, 1.0f);
            platformFixture.Body.Position = new Vector2(platformPosition.X / Global.Variables.PixelsPerMeter, platformPosition.Y / Global.Variables.PixelsPerMeter);
            platformFixture.Body.BodyType = BodyType.Static;

But, if I create a rectangle like so...

            // setup box
            boxTexture = Content.Load<Texture2D>(@"TestContent\TestSquare");
            boxPosition.X = (Global.Variables.Width / 2);
            boxPosition.Y = 100;

            // farseer box stuff
            boxFixture = FixtureFactory.CreateRectangle(physics, boxTexture.Width / Global.Variables.PixelsPerMeter, boxTexture.Height / Global.Variables.PixelsPerMeter, 1.0f);
            boxFixture.Body.Position = PositionToPhysicsWorld(boxPosition);
            boxFixture.Body.BodyType = BodyType.Dynamic;
            boxFixture.Restitution = 0.5f;
It's like the top left corner of the texture gets attached to the middle of the Farseer rectangle. Should I have to worry about offsetting my texture? I thought I could just translate the Farseer position directly to the game world position with a scalar.

 

Developer
Oct 21, 2010 at 8:51 PM

I hate to stomp on your progress but XNA 4 makes using SpriteBatch with any camera easy. That means you can quite easily work at the scale you want (should be in meters).

            float zoom = speed.Length() + 1f;
            Matrix proj = Matrix.CreateOrthographicOffCenter(-zoom * GraphicsDevice.Viewport.AspectRatio, zoom * GraphicsDevice.Viewport.AspectRatio, zoom, -zoom, 0, 1);
            Matrix view = Matrix.CreateTranslation(new Vector3(-bike.Position - new Vector2(1, 0), 0));


            effect.World = Matrix.Identity;
            effect.View = view;
            effect.Projection = proj;

            effect.TextureEnabled = true;
            effect.VertexColorEnabled = true;

            spriteBatch.Begin(0, null, null, null, null, effect);

            bike.Draw(spriteBatch);

            spriteBatch.End();

            debugView.DrawSegment(new Vector2(-25, 0), new Vector2(25, 0), Color.Red);
            debugView.DrawSegment(new Vector2(0, -25), new Vector2(0, 25), Color.Green);

            debugView.RenderDebugData(ref proj, ref view);

Above is a quick example.

First I create an Orthographic projection. I used the off center version because I was offsetting my view at one point. Notice how I use zoom and aspect ratio to create a properly sized projection viewport. Please note that since Farseer 3.x uses MKS you should try to keep your viewport size from 5x5 to 100x100 meters with the width being adjusted for aspect ratio.

Second I create a translation matrix for my view matrix. This allows me to track my bike's position. In this example I also offset the bike a little.

Third I set up my effect. Here I am simply using BasicEffect.

Now call Begin and pass the stuff you need (I didn't need anything other then default options so null works great). Be sure to pass the effect.

Now draw using SpriteBatch as normal. Just remember you are already in physics space so to speak. So no position conversion is needed. You will have to scale your texture down to meters. For me something around 0.001 seems to work great. Also remember to always set the origin of your texture to the center of the texture. One final note is if you see nothing at first make layerDepth negative. Sometimes the way the viewport is created all positive layerDepths will be clipped. This is always true with CreateOrthographic() and is possible with the off center version.

Now I draw a few line segments to show myself the origin of the physics world.

Then simply call RenderDebugData with the projection and view matrices and everything should line up, assuming you got your texture scaling right. I recommend adding a scaling value per object as some textures may not be drawn to scale and need a special scaling vector. Using a global scaling vector means you may have to change your textures to make them all the same scale.

 

Tips -

Always make sure your textures have the Generate Mipmaps set to true.

If your using Reach profile also set Resize To Power of Two to true.

 

I hope all this makes sense and works for you.

Oct 21, 2010 at 11:16 PM

The reason I haven't been using xna 4.0 is because I'm not able to deploy to xbox with it. Has this changed, or is it going to change anytime soon?

Oct 21, 2010 at 11:35 PM

That's an extremely important requirement for me as well, but because I'm not releasing until next year I assumed that it would be an option by then. Also, thank you guys for your help, I now have debug view and sprite drawing working.

Developer
Oct 22, 2010 at 1:19 AM

XNA 4.0 should have deploy ability now. Although I'm not sure if you can submit games to XBLA yet.

Oct 27, 2010 at 10:10 AM

Hello alkier, could you please send me the zip to gudome (at) hotmail.com ?