Shattering terrain and more futzing with Box2D

I went ahead and applied the shattering animation I wrote yesterday to terrain destruction to see how it looks. Answer: pretty nice!

I also finally fixed a number of outstanding bugs having to do with body destruction in Box2D. One had to do with tiles occasionally recreating themselves in the player’s space, causing him to get nudged around or even fall through the terrain entirely. That one was a simple error from when I changed the tile coordinate system to use the upper-left corner of tiles as their canonical position, rather than their center, which is how Box2D’s bodies work. Simple fix, difficult to track down.

The other was more complex, and involved a total rewrite of how I was answering the classic “am I standing on the ground” question. You might recall that I tried to solve this with sensors earlier in the project, but even after patching Box2D in an attempt to make it more reliably notify me of collision / separation events, I was still seeing incorrect behavior on occasion. Today, I noticed that when shooting out the tile the character was standing on, Box2D was only sending an OnSeparation event about half the time. This meant that even though the character was falling through the air, he could still jump, and this state wouldn’t be reset until he landed. Not cool.

So while I still have collision / separation handlers on my character, I no longer count on Box2D to tell me when the ground beneath my feet vanishes or is recreated. Instead, I follow the naive (but plenty performant) approach and examine all the contacts on the character body.

protected void UpdateStanding() {
    bool isStanding = false;
    bool isTouchingCeiling = false;

    var contactEdge = _body.ContactList;
    while ( contactEdge != null ) {
        if ( contactEdge.Contact.IsTouching() && contactEdge.Other.GetUserData().IsTerrain ) {
            FixedArray2<Vector2> points;
            Vector2 normal;
            contactEdge.Contact.GetWorldManifold(out normal, out points);
            if ( normal.Y < -.8 ) {
                isStanding = true;
            } else if (normal.Y > .8) {
                isTouchingCeiling = true;
            }
        }
        contactEdge = contactEdge.Next;
    }

    IsStanding = isStanding;
    IsTouchingCeiling = isTouchingCeiling;
}

Then I also call this method whenever the terrain changes to make sure that the character knows he’s no longer standing when the ground underneath his feet disappears. I also reverted my patch to Box2D, so I’m not doing duplicate work in these cases. It seems to work perfectly so far.

Leave a Reply