Slopes & ramps in a side scrolling game

Apr 20, 2012 at 1:29 AM


I'm messing around with farseer (nice work to all involved btw) and have been trying to get a simple sidescrolling game going.  It's something with an actual setting, people walking, barrels, crates, the regular game stuff.  I'm having with the actual figures to use though and was wondering if anyone could share their settings or give advice about what to do.


The main problem right now is with inclines and how to prevent a character from sliding down an incline that's less then, say, 50 degrees.  There are a few mentions in various threads but none of them seem to work all the time.  I'm trying to get the angle / normal to the character center from a contact/collision so that I can say "if one of your collisions is pointing upwards, don't fall down the slope" but non of the implementations I've tried return anything useful.  They seem to detect, return different answers at different times.


In the video below you can see how sometimes the character registers a collision, allowing it to jump, and sometimes it doesn't.  (The first line in the upper right is the result of GetWorldManifold which doesn't seem to correspond to any one direction, the second that turns red is body.ContactList.Contact.IsTouching();)

http://youtu.be/JwbwYBi9fOk

The character setup is as follows.

 

avatar = new Item();                
avatar.tex = _humansSpriteOutline;

                // Polygonal bodies need different parameters 
                // than the (perfect) circles made above...

                // Make new body, pass in center...
                // (Remember, CreateBody position parameter
                // is in meters.)
                avatar.body = BodyFactory.CreateBody(_world, avatar_position);
                avatar.body.BodyType = BodyType.Dynamic;

                // Make rectangle...
                Vertices verts = PolygonTools.CreateRectangle(0.5f, 0.5f, new Vector2(0, -0.25f), 0f);
                // Make ellipse (errors)
                //Vertices verts   = PolygonTools.CreateEllipse(0.5f, 1f, 12);
                // Rounded rect (errors)
                //Vertices verts  = PolygonTools.CreateRoundedRectangle(1.0f, 1f, 0.1f, 0.1f, 24);
                // Diamond attempt... (errors)
                //Vertices verts = PolygonTools.CreateLine(new Vector2(0, 0.5f), new Vector2(0.25f, 0) );           
                PolygonShape rect_shape = new PolygonShape(verts, 1f);
                avatar.body.CreateFixture(rect_shape);

                // Make circles...         
                CircleShape circle1 = new CircleShape(0.5f, 1f);
                circle1.Position = new Vector2(0f, 0.5f);
                avatar.body.CreateFixture(circle1);
                
                // Make jump test line...
                //EdgeShape edge1 = new EdgeShape(new Vector2(0.0f, 0.0f), new Vector2(0.0f, 5.2f));
                //edge1.p
                //avatar.body.CreateFixture(edge1);                

                // So it doesn't tip over unless we want 
                // it to...
                avatar.body.FixedRotation = true;
                
                // Give it  friction
                avatar.body.Friction = 0.6f;

                // Dampen movement... 
                //avatar.body.AngularDamping = 2f;
                avatar.body.LinearDamping = 1f;
                // (This can can limit max velocity, but 
                // for per pixel control, this is done 
                // in movement code)

                // Mass ( considered in weight / kg )
                avatar.body.Mass = 88;

                // Bounce...
                //avatar.body.Restitution = 0.0f;
                // (0 by default)

                // Not used so we can render them differently...
                list_item.Add(avatar);

 


All three ground blocks are made like so:

            Item ground2 = new Item();
            ground2.tex = _groundSpriteLarge;
            Vector2 ground2Position = (_screenCenter / MeterInPixels) + new Vector2(-2.5f, 4f);
            ground2.body = BodyFactory.CreateRectangle(_world, 2048f / MeterInPixels, 64f / MeterInPixels, 1f, ground2Position);
            ground2.body.IsStatic = true;
            ground2.body.Restitution = 0.0f;
            ground2.body.Friction = 0.1f;
            ground2.body.Rotation = MathHelper.ToRadians(32);
            list_item.Add(ground2);

What's really weird is that, as you can see, I get positives on the middle ramp when there are only two ground pieces, but it returns the red false bool when I remove one of the ground pieces.   For a bit I was thinking it was a problem with them overlapping, but he same thing happens with the barrels in the level.  Sometimes they connect, sometimes they don't.

Any help would be greatly appreciated.

Apr 20, 2012 at 1:44 PM

Can you not increase the friction your character exerts so that he doesn't slide down the slope?

Apr 20, 2012 at 3:38 PM
Edited Apr 20, 2012 at 3:45 PM

I did have the idea, but I get into this catch-22.  I can increase the friction so it'll stay on slopes, but then you need to increase the force applied to get him to move at all.  This brings with it unrealistic behavior such as huge air control (moon jumps), a jagged looking "jumping" down slopes instead of just walking down them and makes him unable to "push" circle shapes (the friction is so great he starts rolling up over with them).  You also lose the feeling of momentum since the character stops on a dime with higher friction (I'd like to have a Sonic the Hedgehog / Out of this World feeling of weight to him).

Last night I tried making it so that the friction only comes on when you press the key, like so...

if (!state.IsKeyDown(Keys.A) && !state.IsKeyDown(Keys.D)) {

avatar.body.Friction = 0.6f;                   

//avatar.body.LinearVelocity += new Vector2(-1f, 0);                   

//avatar.body.ApplyLinearImpulse(new Vector2(-80, 0));                   

avatar.body.ApplyForce(new Vector2(-8000, 0));  // 8000               

}               

if (!state.IsKeyDown(Keys.D))           

avatar.body.Friction = 5.0f;          

 

...but for some reason the friction becomes stuck at 5.0 and doesn't ever update.  Not sure why.

Apr 20, 2012 at 7:40 PM

Nice video :)

I think usually you can find a sweet spot for friction by tuning carefully. Everything might break, though, if you change parameters and you have to tune again... In the same way working with contact normals should in theory give you good results (although in your case it seems it doesn't).

How about you attach a circle to your character's bottom so it's the one in contact with the ground? Main body will be FixedRotation. You can attach the circle body with a revolute joint and allow it to rotate. You can even put a motor on it and just set the motor speed for moving. By playing with the circle's friction you can make it more or less "sticky". Basically you will be modelling the "shoes" of the character that can have superpowers while leaving the rest to fulfill your other more physically-correct requirements. Because a human is not supposed to climb mountains just by touching them with his big toe... Something like this: http://www.youtube.com/watch?v=oKNg0Ivq3sY

There are also other approaches depending on your needs that can produce different behaviours:

- You can use a capsule for the character that has some better responses also for going over edge shapes and not getting stuck in corners. Usually games never use rectangles. Capsules are the way to go regardless of friction problems.

- Coulomb friction can create issues with pushing other rigid bodies on slopes because the normal force is taken into account. Ignore friction completely (setting it to 0). Put a sensor on the character's bottom. Apply your own forces to make the character "stick" to the slope when Sensor.IsTouching=true. At the end of the day that's what you would have needed friction for in the first place.

I suppose you'd have to experiment with various things to see which one you like most. In my opinion it's not that easy to implement a platformer just with the off-the-shelf utilities provided by the engine, even though it might seem so :)

Apr 22, 2012 at 1:50 PM

Thanks for the replies guys.

/\ Holy crap the lighting effect on that is cool, I'd love more info on that if you have it?


I tried using both ellipses and rectangles with rounded edges, they both gave me serious errors at runtime.
"How about you attach a circle to your character's bottom..."I'd seen posts about that, but then I also saw two posts here asking if the angle or revolute joints were broken so I skipped straight to just a circle as a fixed shape (I did want a circle down there anyway so that he wouldn't do that "90% of the body over the edge" thing).  Now that I've found the testbed files I see in one example they do seem to be working but it's hard to get the right settings.  I have my acceleration at something like 50 million to make it look right.  Even then, this doesn't work if I want air control (at least, I'd have to have both the motor and an applyforce system set up).

I also thought about using the point inside shape check, but that wouldn't work for terrain built out of lines, only solid convex shapes.

A lot of my frustration stemmed from the apparent fact that contactList and the world manifold don't link to actual lists.  If you reference them outside the world step you just get the most recent entry in each, not a complete rundown on what went on in that body during the lest world step.  There's a comment that reads "      

/// Get the list of all contacts attached to this body.

/// Warning: this list changes during the time step and you may

/// miss some collisions if you don't use ContactListener

....but even if I do track that section down (inside "sensorTest") there's no way I can see of getting the whole list in the main state code.  Maybe I'm not reading it right but ideally if any contact were made during a step by a body there'd be a single "isTouching" bool flipped to true and left alone until the next world step, not the next contact.

Am I really interpretting this right?

Also, would anyone know if all tests done through the contacts class?  Is there anyway to just use straight up intersection math?