Farseer 2.x to 3.0 - platformer sample

Topics: User Forum
Nov 27, 2010 at 4:40 PM

I'm attempting to update this excellent little platformer sample (http://www.sgtconker.com/2010/09/article-xna-farseer-platform-physics-tutorial/) to Farseer 3.0 and encountering nothing but trouble.

I have everything updated to Farseer 3.0, but I can't get the character to work at all. Here is my current create character code:

    Fixture body, wheel;

    protected override void SetupPhysics(World physics, Vector2 position, float width, float height, float mass)
        {
            float upperBodyHeight = height - (width / 2);
            body = FixtureFactory.CreateRectangle(physics, width, upperBodyHeight, mass / 2);
            body.Body.BodyType = BodyType.Dynamic;
            //also shift it up a tiny bit to keep the new object's center correct
            body.Body.Position = position - Vector2.UnitY * (width / 4);
            body.Body.IsStatic = false;
            centerOffset = position.Y - body.Body.Position.Y; //remember the offset from the center for drawing
            

            //Now let's make sure our upperbody is always facing up.
            fixedAngleJoint = JointFactory.CreateFixedAngleJoint(physics, body.Body);
            fixedAngleJoint.CollideConnected = false;
            fixedAngleJoint.TargetAngle = 0;

            physics.AddJoint(fixedAngleJoint);

            //Create a wheel as wide as the whole object
            wheel = FixtureFactory.CreateCircle(physics, width/2, mass/2);
            wheel.Body.BodyType = BodyType.Dynamic;
            wheel.Body.IsStatic = false;
            //And position its center at the bottom of the upper body
            wheel.Body.Position = body.Body.Position + Vector2.UnitY * (upperBodyHeight / 2);
            
            //These two bodies together are width wide and height high :)
            //So let's connect them together.
            motor = JointFactory.CreateRevoluteJoint(physics, body.Body,
                wheel.Body,
                wheel.Body.Position
                //Vector2.Zero
                );
            motor.MotorEnabled = true;
            motor.MaxMotorTorque = 1000f;
            motor.MotorSpeed = 0f;
            motor.CollideConnected = false;
            
            physics.AddJoint(motor);


            wheel.Restitution = 0;
            body.Restitution = 0;
            wheel.Friction = float.MaxValue;

            wheel.IgnoreCollisionWith(body);
            body.IgnoreCollisionWith(wheel);
    }
When run, the body kind of sinks through the wheel and twitches randomly... the motor itself is entirely unresponsive to input. I have no clue what's causing it. :S

I've done some searching and found a few other threads with similar issues (http://farseerphysics.codeplex.com/Thread/View.aspx?ThreadId=228615, http://farseerphysics.codeplex.com/Thread/View.aspx?ThreadId=234624) but I haven't been able to glean any more information about what's going on.

 

Also, once I added the world.AddJoint() commands, the game would go into an endless loop in World.Solve. I hacked in a way to break out of the loop if it iterated for more than 1000 times, which allows the game to run as normal, but I'm not sure if I'm just covering up a deeper issue with what I'm doing...

               private void Solve(ref TimeStep step)
               {

                   ...

                    //my hack
                    int itrs = 0;
                    // Search all joints connect to this body.
                    for (JointEdge je = b.JointList; je != null; je = je.Next)
                    {
                        if (je.Joint.IslandFlag)
                        {
                            //it always gets stuck here and never breaks out of the loop... so I added this code to break if it runs more than 1,000 times

                            itrs++;
                            if (itrs > 1000)
                                break;
                            continue;
                        }

                        Body other = je.Other;

                        ...
               }

 

Any help would be greatly appreciated - I *KNOW* this is a fantastic engine, if I just knew what I was doing with it. :D

Coordinator
Nov 27, 2010 at 5:39 PM

I'm wondering what causes the endless loop - that is not supposed to happen.

Could you send me over your project so I can take a look at it?

Nov 27, 2010 at 9:46 PM

Hi Genbox,

Here's the project: http://ubergeekgames.com/FarseerPlatformer.zip

In it's current state it will go into the endless loop - check World.cs at line 779 for my commented-out hack that fixes it.

CompositeCharacter.SetupPhysics() is where the character is created - any help in getting that working would be fantastic!

If you need to send files or anything, my email is here: http://ubergeekgames.com/contact.html

Thanks!

Coordinator
Nov 27, 2010 at 10:41 PM

The infinite loop is happening because you add each joint to the engine twice. Once in the JointFactory and then again later. FPE 3.1 fixes this issue as it has a check if you add it more than once.

I'm investigating the other issue.

Coordinator
Nov 27, 2010 at 10:48 PM

Okay. I think I know what it is.

You are using pixels as units instead of meters. FPE 3.0 switched to the full Box2D engine that uses the MKS system. You need to decouple your physics from your drawing.

Another note: You don't need the FixedAngleJoint to make the body stop rotating. You can set Body.FixedRotation = true instead.

Nov 28, 2010 at 3:11 AM

Thanks - hadn't thought to check that. one mystery solved then. ;)

pardon my noobness, but my physics knowledge pretty much peters out after basic velocity and inertia... Could you link me to some Farseer 3.0 specific literature on this meter-pixel issue, or explain how to solve it very slowly? :)

 

Thanks again!

Nov 30, 2010 at 8:10 PM

I'll take a shot at answering. I don't think there's documentation outlining the MKS specifics, but check out my thread here for a little guidance. The last post in thread has the most useful info:

http://farseerphysics.codeplex.com/Thread/View.aspx?ThreadId=234573

The "meter-pixel" issue means that the Farseer/Box2D engine operates in terms of meters/kilograms/seconds. XNA/WPF/Silverlight operate in terms of pixels (there's no real concept of mass in XNA/WPF/Silverlight and, well, seconds are seconds). If either the X or Y component of a body's position vector changes by one whole unit in the Farseer world (e.g., the position vector changes from (10, 10) to (10, 11), the body moves one meter in the Farseer world; a body with a position vector of (10, 10) in the Farseer world is 10 meters in the positive X direction and 10 meters in the positive Y direction from the center of the Farseer world (imagine some fictional point at the center of some fictional universe and then imagine a ball that's centered 10 meters to the right and 10 meters "above" that point); if a component of a body's velocity changes by one unit in the Farseer world, the body is moving one meter/second faster in that direction.

If you translate Farseer MKS units directly to screen units (pixels), you'll likely see some undesired effects. Notably, a body moving relatively fast in the Farseer world will move slowly on your screen. Specifically, a body with a velocity of 20 meters/second (~70 kilometers/hour; about 45 mph) in the Farseer world would only move 20 pixels/second on your screen.

You'll want to scale a body's position and velocity in XNA/WPF/Silverlight by converting (scaling) the MKS units to pixels. There's no hard rule; just pick a scale that looks good and makes sense for your use case. For example, make each pixel on your screen represent 0.1 meters (a 10x scale; 10 pixels = 1 meter). For every whole unit change in a body's position in the Farseer world, move the body's image on the screen 10 pixels. At that scale, a body moving at 20 meters/second in the physics world will move 200 pixels per second on your screen.

I don't know XNA as well as I know WPF, but in WPF you can apply a scale transform to a Canvas element (same for Silverlight), and WPF sort of takes care of the scaling allowing you to draw objects to the screen using Farseer's MKS coordinates/units.