coding

Check the Filter

Box2D allows filtering of collision contacts into groups. In my case I require a boundary at center-screen to limit game player movement, but need a third piece to cross the center barrier without being stopped. Box2D bodies in a group tagged ‘-1’ will not have their vectors changed when a collision is detected.

Without making changes to a playerPiece class, I can add this grouping information to a ‘ball’ class to give it free movement across the board.

This example is gathered from the Box2D documentation and the testbed files in Cocos2D, and assumes you have a b2World and b2ContactListener already initialized.

Firstly, in the main GamePlay.mm file, a b2ContactFilter class needs to be added:

#pragma mark ContactFilter

class myContactFilter : public b2ContactFilter
{
	// There are more callbacks than this one, see manual
	bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB);
};

// add a method to test the collision data for filter settings
bool myContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB)
{
	b2Filter filter1 = fixtureA->GetFilterData();
	b2Filter filter2 = fixtureB->GetFilterData();

	// test to let pass or calculate collision results
	if(filter1.groupIndex == filter2.groupIndex) {
		return false;
	} else {
		return true;
	}
}

..then add the ContactFilter to the GamePlay.mm init function:


-(id) init
{
	if( (self=[super init])) {
        [...] // add b2World and groundbox

// start b2World
world = new b2World(gravity, doSleep);

// init a ContactFilter class
world->SetContactFilter(new myContactFilter);

       }
} 

Next in the main GamePlay.mm class, we add the Box2D edge line at screen center:

-(id) init
{
	if( (self=[super init])) {
        [...] // add b2World and groundbox
        
        // start b2World
        world = new b2World(gravity, doSleep);

       // init a ContactFilter class
        world->SetContactFilter(new myContactFilter);

                // center bar
		b2PolygonShape polygon;
		polygon.SetAsEdge(b2Vec2(0,(screenSize.height / 2) / PTM_RATIO),
b2Vec2(320/PTM_RATIO,(screenSize.height / 2) / PTM_RATIO)); 
// calculates horizontal and vertical screen centers
		
		b2FixtureDef boxShapeDef;
		boxShapeDef.shape = &polygon;
		boxShapeDef.density = 0.0f; 
//zero density makes the object 'static', behaves like an obstacle

// add the contact filter group with an index of -1. 
//Category and mask bits are other ways to match collision groups
of colliding and non-colliding Box2D bodies.
		boxShapeDef.filter.groupIndex = -1;
		//boxShapeDef.filter.categoryBits = 0x0004;
		//boxShapeDef.filter.maskBits = 0xFFFF;
		
		b2BodyDef boxBodyDef;
		
		b2Body* body3 = world->CreateBody(&boxBodyDef);
		body3->CreateFixture(&boxShapeDef);
}
return self;
}

Lastly in my external Ball.mm class, I add similar filter information to its init function:

// initialize the Box2D body in Ball.mm external class:
- (id)initWithPos:(CGPoint)points {
	if(self = [super init]) {	
                [self setBodyDef:new b2BodyDef];
		[self bodyDef]->position.Set(points.x/PTM_RATIO, points.y/PTM_RATIO); 
                //PTM_RATIO converts Box2D units(m) into Cocos2D units(px)
		[self bodyDef]->linearDamping = 0.2f;
		
		[self setShapeDef:new b2CircleShape];
		[self shapeDef]->m_radius = 22.0f/PTM_RATIO;
		
		[self setFixtureDef:new b2FixtureDef];
		[self fixtureDef]->shape = shapeDef;
		[self fixtureDef]->friction = 0.06f;
		[self fixtureDef]->density = 0.8f;
		[self fixtureDef]->restitution = 0.4f;//bounciness
		
// filter for passing middle boundary
		self.fixtureDef->filter.groupIndex = -1;
		self.fixtureDef->filter.categoryBits = 0x0004;
		self.fixtureDef->filter.maskBits = 0xFFFF;
       }
return self;
}

Now if the Box2D world is built correctly, bodies without the group index of -1 will be blocked while the Ball body passes through without stopping.

The Manifold Ways to Listen

Box2D is quickly evolving. It seems within a range of four months the physics engine’s contact listener has been radically changed (and improved).

C++, Obj-C and Box2d challenged me as a newcomer to synthesize user tutorials and outdated documentation with up-to-date examples. With the help of a few Cocos2D forum participants, time and plain bullish determination to understand, I’ve been successful and would like to share this info with other newbies.

ContactListener

A contact listener isn’t essential to watch Box2d physics simulations in action. You can set the shape’s body definitions and go. But the contact listener is essential for game logic, because it provides location of contact and pointers to the colliding physics bodies.

With that information you can also access any user data embedded in the physics body object. Often a cocos2d node is embedded, making it easy to update the onscreen graphic image during the simulation. Since Box2d is actually a mathmatical process without graphics, the visible shapes which aid debugging are usually made invisible for the actual application release.

The two principle methods of the b2ContactListener are beginContact and endContact. These are most useful for common game logic.
The listener also provides a preSolve and postSolve function. Those could be useful for altering properties of physics bodies before or after each step – making them sensors, changing collision filtering settings, and so on.

Manifolds

Embedded throughout the b2Contact classes are references to a manifold. This is simply a holder for the two objects being evaluated for contact. There’s a local-coordinate manifold and a world-coordinates manifold.
If you view the Box2d Testbed’s ‘presolve’ method, you’ll see that ‘manifold’ and ‘oldManifold’ are used to retrieve position and vector normals from colliding physics fixtures.

Older versions of Box2d examples might have used Add, Remove, and Persist functions for contacts. Manifolds simplify the code needed to collect and evaluate these contact points.

Example b2ContactListener code

My example is based on the Box2d testbed, included with the cocos2d 0.8.1 release.

In my main Game.mm class (Box2d is C++ so XCode requires the extra ‘m’), I’ve declared an actual class file:
class MyContactListener : public b2ContactListener
{ ...

A limit to the number of contacts is then stated. This is important because contacts are reported each frame. So two bodies colliding may signal many contacts in a very short time.

class MyContactListener : public b2ContactListener
{  int32 m_pointCount;
ContactPoint m_points[k_maxContactPoints];
...

Next, public methods are declared. Game logic and other game piece classes may need access to these methods.

class MyContactListener : public b2ContactListener
    {
        int32 m_pointCount;
        ContactPoint m_points[k_maxContactPoints];
        public:
        void BeginContact(b2Contact* contact)
            {
            }
    };

Note the use of C++ syntax in the methods declarations ( e.g. void rather than Obj-C -(void) ).

The b2ContactListener compares the fixtures of physics bodies, calling these fixtureA and fixtureB. We need to state these as struct in the file. Scroll to the top of the Game.mm file, outside of the @implementation and type:

const int32 k_maxContactPoints = 2048;
struct ContactPoint
    {
        b2Fixture* fixtureA;
        b2Fixture* fixtureB;
        b2Vec2 normal;
        b2Vec2 position;
        b2PointState state;
    };

These provide the constant value for k_maxContactPoints (2048) and state the fixture and physics vector variables, used in the contactListener.

Return to the BeginContact method and type:

public:
void BeginContact(b2Contact* contact)
    {
        b2Fixture* fixtureA = contact->GetFixtureA();
        b2Fixture* fixtureB = contact->GetFixtureB();
        ...

Now we’ve created local variables for the two colliding physics bodies. This will be updated every time step, so the values of fixtureA and fixtureB might change rapidly. Box2d fixtures contain the essential values for calculation, e.g. density (affecting mass), friction and rectitude (elasticity).

Any game logic you require is now entered here; methods to change color, call an animation loop, trigger a sound effect, alter the appearance or value of one or both of the colliding bodies, etc.

Hint: customize the XCode command bar to easily view your project classes and methods: View/Customize Toolbar then add Class Browser. The Box2D source is very well commented so it takes only a moment to find and understand these functions while coding.

To test our listener, we’ll add one of the b2Contact methods.
Let’s use the ‘isSolid’ function. This tests a contact to see if it’s real, and not a sensor, disabled, or no longer in contact. We’ll use it to trigger a console message:

public:
void BeginContact(b2Contact* contact)
    {
        b2Fixture* fixtureA = contact->GetFixtureA();
        b2Fixture* fixtureB = contact->GetFixtureB();
        if (contact->IsSolid()) {
            NSLog(@"Contact is solid");
        }
    }

Since there are possibly many contacts per second, it’s wise to add your own check to ignore subsequent contacts for a period of time, perhaps one second. This avoids triggering your game logic too rapidly.

Next add your EndContact function. We’ll write a simple message to the console.

void EndContact(b2Contact* contact)
    {
        NSLog(@"end contact");
    }

Complete class

#pragma mark Contact Listener

class MyContactListener : public b2ContactListener
{
    int32 m_pointCount;
    ContactPoint m_points[k_maxContactPoints];

    public:
    void BeginContact(b2Contact* contact)
        {
            b2Fixture* fixtureA = contact->GetFixtureA();
            b2Fixture* fixtureB = contact->GetFixtureB();
            if (contact->IsSolid()) {
               NSLog(@"Contact is solid");
             }
        }

    void EndContact(b2Contact* contact)
        {
            NSLog(@"end contact");
        }

    void PreSolve(b2Contact* contact, const b2Manifold* oldManifold)
        {
           const b2Manifold* manifold = contact->GetManifold();
        }

    void PostSolve(b2Contact* contact)
        {
            const b2ContactImpulse* impulse;
        }
};

Hint:’#pragma mark’ is simply a bookmark to that part of the page, which then appears in a dropdown menu above the code window in XCode.

Lastly, after creating your Box2d physics ‘world’, you need to add the contact listener to the world:
world->SetContactListener(new MyContactListener);

Remember, Box2d requires an environment to be initialized and a groundBox added in order to work.

Good luck!

…and Now for Something Completely 2Different

“we are now entering the 2D zone”

I have a few 2D games planned for after completion of my 3D work. Of course to do anything properly requires time, and a new idea has taken hold and isn’t letting go. So I’ve started a second project to work on concurrently with my 3D game effort. That 3D game is in fact going very well. I’ve not hit any insurmountable roadblocks and progress is reasonably quick.

However I do want to have an app in the Apple store, so I chose an idea which would let me work in Cocos2D directly in XCode, be relatively straightforward to do, encourage creation and exploration of interesting artwork, and would allow me to submit a completed game while I develop the more complex 3D effort.

Cocos2D for iPhone provides a framework suitable for game organization but also directly uses the iPhone’s OpenGL framework for display. My particular 2D game ideas don’t necessarily require the 3D graphics acceleration and could be accomplished with the Quartz2D framework, but the experience gained would be valuable for more complicated efforts. In any case it’s difficult to go wrong with having the extra graphics performance at hand for visual effects.

Another attractive aspect of using the 2D engine is the immediate access to antialiased graphics. On the iPhone 3D geometries are aliased, and I wanted a warmer, smoother look to the game. I could have used 2D geometric planes in Shiva but it seems a bit of overkill to use that software for this application.

The irony is Cocos2D does exactly this. Sprites are textured onto 2D planes in the OpenGL 3D space, and the GL camera is set to an orthographic (2D) projection.

Yet Cocos2D provides direct access to Objective-C commands so in this sense it can be simpler and certainly a more direct development process than working with the Shiva interface. it also has pre-built classes for managing menus, particle effects, and scenes.

For my more complex 3D game, Shiva for me is the better choice as I can then concentrate on scripting, behavior and artwork in a more complex working environment.

Cocos2D for iphone is still in beta at version 0.82. This version also uses Box2D as its physics engine, which I prefer for its potentially complex results moment to moment.

Yet both Cocos2D and Box2D are in rapid development, so following tutorials requires an eye on their current APIs. Both are open-source, and Box 2D is well-organized along OOP principles. So along with the predictive text, it’s pretty easy to just open up the class files and find out what the current functions do while studying older tutorials.

As an example, Cocos tutorials using code older than version 0.8 might refer to add: as the command to add sprite children to display containers. This has been deprecated by the addChild: command:

ball = [Sprite spriteWithFile:@”ball.png”];
[ball setPosition:CGPointMake(160,240)];
[self addChild:ball z:10];

This places a 2D artwork on-screen at x160 y240, with a z-depth of 10 (0 being the lowest ‘level’ where the background artwork is placed. Actually it’s similar of course to Actionscript 3 so users of that software will know how sprites are collected and displayed by Cocos2D.

Likewise Box2D tutorials may have a few ‘gotchas’. ‘Shapes’ are used to compute the actual physics and are associated with the sprite created in Cocos2D, shown above. The deprecated command for doing this is:

body->CreateShape(&ballShapeDef);

..and the new command in Box2D must be:

body->CreateFixture(&ballShapeDef);

After a few hours’ work studying these frameworks, coding and debugging, I already have a game board with active player and game pieces.

Links:

http://iphonedev.net/2009/07/12/cocos2d-example-box2d
http://www.emanueleferonato.com/2009/05/18/understanding-how-box2d-manages-boundaries/
http://johnehartzog.com/2009/07/using-box2d-physics-engine-with-cocos2d-iphone/

When it works it’s…

Made progress today in Shiva and XCode.

Stonetrip Shiva is a 3D game authoring package. I like the concept because as a solo developer with an artist’s proclivity, time not spent learning how to write a game coding framework is time spent creating the art and scripting for the game…which means I get it out sooner.

I’m learning how to script a virtual joystick for the iPhone in Shiva, based on a very generous example from a member called “dtr1au”. on the Stonetrip developer forums. This example worked on my device, an iPod Touch. Yet despite carefully integrating the idea into my game, it would. not. run.

Gradually I narrowed down the possibilities, and wrote a simple function to display touch data n my device.
It turns out that multitouch and normal ‘single’ touch controls conflict with each other in Shiva. So you can press a button which triggers an ‘action’, or press a label symbol of a button which is controlled by code, and they will work correctly if pressed separately. Because in this case the chicken did come before the egg.
But if you try to press both à la multitouch, it wont work – one cancels out the other.

So to avoid input conflicts on the iphone running Shiva, it’s better to have game controls all working from code under multitouch mode.

To manage which touch mode the user is working in, I confirm the screen (main, level or in-game) then switch between single or multi-touch as required. This saves me coding screens that require only a single input – start, play, choose level – and gives me more time to devote to the actual game controls.

So now, I’ve verified that three touches are feeding x and y screen information.

But the actual virtual joystick does. not. work.

Hmm..

Over several hours over several days I compared the code with existing examples, traced through my code step-by-step, left it, came back to it, and check it again.

Guess what?

It still. didn’t. work.

But I did discover something important this evening. I’d deleted the original HUD left and right button controls to replace it with the single virtual joystick style currently in vogue. But had not removed ‘initHUD’ function ‘actions’ pointing toward those particular buttons I’d deleted.

So while everything else in the game was working, including multitouch, these phantom actions where somehow blocking input from reaching the virtual joystick. Out these actions went, deleted, vaporized. Guess what happened next?

It. Worked. First. Time.

I’m going to go curl into a ball, look at complicated OOP code I’d written for a CD-ROM game commercially released a few years ago, and suck my thumb. Should be comforting.