Box2D, I have conquered you!!! (sort of)

Posted on April 26, 2012 | 6 minute read

[caption id="attachment_48002” align="alignleft” width="225” caption="Look at that face…I don’t think Keeda is as excited about this endeavour as I am."]Keeda[/caption]

I have been chipping away at Box2D for what feels like forever. In reality it’s been almost 3 weeks since my last Box2D/JS related blog post. I’ve been huddled away juggling paid work, this, and X-Files, trying to figure this thing out. A few things contributes to the hindrance of progress in my efforts:

  • I’m using a JavaScript Box2D port that’s been ported again for the Impact engine to be turned into a plugin. Syntax has changed, not everything matches the other Box2D versions 100%, and there’s next to no documentation

  • My JavaScript skills are still average at best. I have never worked with Box2D or physics before (except for what rudimentary physics come with the Impact engine)

Challenge 1: Box2D installed, need to recode all movement

Box2D completely overwrites Impact movement and collisions. This meant that when I installed the Box2D plugin I had to recode everything using Box2D movement and scrap any existing collision and movement code.

Challenge 2: Box2D version discrepancies

I soon learned that the plugin I was using was an older version of Box2D. I upgraded to a newer version. Unfortunately this broke several things, including the debug drawer and my rule to check if the player is standing on the ground. This rule was being used in my jump trigger. As a result I’ve disabled jump completely until I can figure out how to rewrite the standing check to work with the new version.

The debug drawer took ages to fix, but I finally found a useful post on the Impact forum that explained how to rectify the prolem.

Challenge 3: Box2D cannot resize bodies

This was the start of the big problem. In Impact resizing bodies on the fly is easy. You simply change the size.x and size.y values. Box2D, it turns out, cannot resize bodies on the fly. Even something as small as implementing crouch suddenly became a problem. What I originally intended to do was destroy the player body and recreate it with different dimensions when the player presses crouch, then destroy that body and recreate it with the original dimensions when the player uncrouches.

Rob, thankfully, came to the rescue with an email explaining a better way to do this. Instead of destroying and recreating bodies he suggested creating two bodies for the player - one for the top half and the other for the bottom half. Then when the player presses crouch I could disable/filter out collision on the top half of the body (at the same time changing the animation to the crouched version of the player’s character). All the player would see is the crouching animation, but in the background what would really be happening is the top body collision being toggled on and off.

So emailing back and forth with Rob, who gave me awesome and detailed advice, I finally got the two bodies working. At first I was going blind due to the debug drawer issue above, which made checking body positions difficult. Finally when the debug drawer was fixed I could see exactly where the invisible bodies were and adjusted them as necessary to be in the correct position, connected by a distance joint (as well as modifying the damping and such to make them sit on top of each other correctly).

Challenge 4: Collision filtering

I knew the theory behind how collision filtering works in Box2D (on a somewhat rudimentary level). In practice, I couldn’t find much documentation for JavaScript specific syntax. No matter what I tried, I kept running into errors. Finally a person much more knowledgeable than myself in these matters - E - sat down with me at lunch to look at the problem. And totally saved the day. He helped me figure out how to create filters and set them for individual shapes.

When I got home tonight I sat down to review what I’d learned and actually implement the collision filtering for crouch. And voila! We have working crouch using two bodies connected with a distance joint and collision filtering toggle on the top body.

The code looks like this. Keep in mind that this is still kind of thrown together and messy. This may not be the best way to write this, but it (technically) works for now (but not completely. See issues below the code):

In my controller class I created a new function that defines two (so far) collision filters:

    setCollisionFilters: function() {
        // Default collision
        this.collideDefault = new b2.FilterData();
        this.collideDefault.categoryBits = 1;
        this.collideDefault.maskBits = 65535;

        // Collide with nothing		
        this.collideNone = new b2.FilterData();
        this.collideNone.categoryBits = 2;
        this.collideNone.maskBits = 65534;
    }

In box2d/entity.js I have an if statement that creates the two bodies and a shape for each of those bodies if the entity being initiated is the player entity:

    createPlayerBody: function() {
        var bottomBodyDef = new b2.BodyDef();
        bottomBodyDef.position.Set(
            (this.pos.x + this.size.x / 2) * b2.SCALE,
            (this.pos.y + this.size.y / 2) * b2.SCALE
        );

        bottomBodyDef.type = b2.Body.b2_dynamicBody;
        this.bottomBody = ig.world.CreateBody(bottomBodyDef);

        var topBodyDef = new b2.BodyDef();
        topBodyDef.position.Set(
            (this.pos.x + this.size.x / 2) * b2.SCALE,
            (this.pos.y + this.size.y / 2) * b2.SCALE - 4.5
        );

        topBodyDef.type = b2.Body.b2_dynamicBody;
        this.topBody = ig.world.CreateBody(topBodyDef);

        var bottomShapeDef = new b2.PolygonShape();
        bottomShapeDef.SetAsBox(
            45 / 2 * b2.SCALE,
            45 / 2 * b2.SCALE
        );

        var topShapeDef = new b2.PolygonShape();
        topShapeDef.SetAsBox(
            45 / 2 * b2.SCALE,
            45 / 2 * b2.SCALE
        );

        this.bottomBody.CreateFixture2(bottomShapeDef, 1);
        this.topShape = this.topBody.CreateFixture2(topShapeDef, 1);
                
                // Create distance joint
        var jointDef = new b2.DistanceJointDef();
        jointDef.Initialize(this.topBody, this.bottomBody, topBodyDef.position, topBodyDef.position);
        jointDef.collideConnected = true;
        jointDef.frequencyHz = 100;
        jointDef.dampingRatio = 1;
        var joint = ig.world.CreateJoint(jointDef);
    },

Then in entities/player.js I set the collision filter for the top body when the player is crouching:

            // Crouch
            if (ig.input.state('crouch')) {
                this.offset.y = 0;
                this.currentAnim = this.anims.defaultCrouchIdle;
                this.topShape.SetFilterData(ig.game.playerController.collideNone);
            } 

            // Uncrouch
            else if (!ig.input.state('crouch')) {
                this.offset.y = 45;
                this.maxVel.x = 300;	
                this.topShape.SetFilterData(ig.game.playerController.collideDefault);
            } 

So now

[caption id="attachment_47991” align="aligncenter” width="255” caption="When not pressing “S” to crouch, the player can’t get under the ledge."]Box2D colliding with body.[/caption]

[caption id="attachment_47992” align="aligncenter” width="257” caption="Pressing “S” to crouch. Top box collision filter is changed to ‘collideNone’, allowing top body to pass under the ledge body. The animation behind the debug view is updated to crouched state (which you can’t properly see here behind the debug stuff)"]Crouching Box2D collision filtering in JavaScript[/caption]

[caption id="attachment_47993” align="aligncenter” width="234” caption="Problem! This is what happens when you uncrouch under the ledge. BAD."]Collision filtering problem when crouching - Box2D JavaScript[/caption]

As you can see, there are still things to work out. For example, when I release the crouch key under a ledge the player should remain crouched until they emerge from under that ledge. I also still have to figure out standing, which will be related to figuring out the ledge detection thing. So next is being able to tell when the bottom of the player is colliding with the ground and the top of the player is colliding with another body. Tough, but getting there.

UPDATE: I’ve now set the rotation of the two shapes to fixed, which stops the above from happening and allows the player to keep moving through under the ledge. However, this is still dodgy and behaves weirdly sometimes. I think the final and “proper” solution will be to keep the player in a crouched state if they’re still under a ledge.

Thanks a lot to Rob and E for all the awesome advice. I’d probably still be stuck here ripping my hair out if it wasn’t for them. Did I mention how lucky I am to have friends who know what they’re doing and are willing to share a bit of their giant brains?




Categories:game dev dev games
comments powered by Disqus