Search code examples
actionscript-3flashbox2dcollision-detection

How can I track all of my Box2D collisions in a clean, manageable manner?


I am using Box2D for the first time seriously in a medium sized Flash Game that I am working on. My current experience with Box2D is limited to creating a world, bodies and adding those bodies to the world in a functional manner.

I'm finding it easy enough to integrate Box2D into my game environment, maintaining well-written code and have completed a few tutorials that walk through dealing with collisions. The issue that I'm facing now is that my game will have many bodies, each interacting with other bodies in different ways, and I'm finding it hard to write my own b2ContactListener subclass without it getting extremely messy.

Based off a tutorial I used, I have created my own subclass of b2ContactListener and added an override of the BeginContact() method. The argument that BeginContact() receives when it is called will reference an instance of b2Contact, through which I can access two b2Fixture instances (the two instances that have collided). I am then able to access the b2Body instance associated with each of those b2Fixtures.

  • Problem: Currently I have a roundabout way of finding out what two things collided (i.e. whether they're a wall and a missile, or the player and a tree, etc) which uses GetUserData() and looks like this as an example:

    var f1Player:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Player
    var f2Player:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Player
    var f1Tree:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Tree
    var f2Tree:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Tree
    // ... continutes with all possible combinations.
    
    
    // Example of managing a collision:
    if(f1Player && f2Tree)
    {
        // Player (FixtureA) and Tree (FixtureB)
    }
    
    if(f2Player && f1Tree)
    {
        // Player (FixtureB) and Tree (FixtureA)
    }
    

    As you can see, this is going to end up extremely long and unmanageable. I also have to write each set of actions to perform twice to cater for a certain element being FixtureA or FixtureB, or vice versa (obviously in the form of a function call with the parameters swapped around rather than literally re-written).

This is clearly not the correct approach, but I haven't been able to locate resources that more thoroughly explain collision detection management.

Does anyone have experience with collision detection management using Box2D that they can share? Also, is using SetUserData( entityThatOwnsTheBody ); the correct way to be using that method?


Solution

  • I've come up with something much nicer than the original.

    Firstly, I just have my Being class (which owns a b2Body) set itself as its bodies' UserData. This class will also contain an onContact() method and look similar to the below:

    public class Being
    {
    
        private var _body:b2Body;
    
    
        public function Being()
        {
            // Define the body here.
            // ...
    
            _body.SetUserData(this);
        }
    
    
        public function onCollision(being:Being = null):void
        {
            //
        }
    
    }
    

    Then in my own b2ContactListener implementation, I simply pass the colliding Being (or null, if there is no Being assigned to the colliding b2Body's UserData) to the opposing Being's onCollision():

    override public function BeginContact(contact:b2Contact):void
    {
        var bodyA:b2Body = contact.GetFixtureA().GetBody();
        var bodyB:b2Body = contact.GetFixtureB().GetBody();
    
        var beingA:Being = bodyA.GetUserData() as Being || null;
        var beingB:Being = bodyB.GetUserData() as Being || null;
    
        beingA && beingA.onCollision(beingB);
        beingB && beingB.onCollision(beingA);
    }
    

    And finally in each of my subclasses of Being, I can easily prepare logic appropriate for a collision between other Beings of a certain type:

    class Zombie extends Being
    {
        override public function onCollision(being:Being = null):void
        {
            if(being && being is Bullet)
            {
                // Damage this Zombie and remove the bullet.
                // ...
            }
        }
    }