iPhone Coding

SteamPunk Hockey is LIVE on the App Store!

Exciting news today, Apple has approved SteamPunk Hockey and it’s available on the App Store.
Click: SteamPunk Hockey

This version 1.0.0 supports twenty-three preset levels and infinite random levels thereafter; two paddle size and three disc sizes; physics-based motion and OpenFeint community Leaderboards!

Update 1.0.1 is well underway and features improvements to the disc motion and physics, improved goal action, animated discs and animated obstacles.

My sincere Thanks to all who support this idea with you encouraging words and interest. Now buy the app – own it today! 😉

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/

Sparks are flying!

Motion, check. Weapon loading, check. Collision detection, check. Smoke and sparks, check check check!

More progress this past week. The basic player mechanisms are working. Motion forwards and back, side-to-side orientation. Fire control works. I had concerns about dynamic engine performance on the iphone but so far it is very good, no complaints. I’ve added particle effects to weapon firing and hits, which is working out, and added an extremely basic reward mechanism.

All of these elements need refinement. Currently I’m sketching out the experience across the spectrum of gameplay, which helps illuminate interactions requiring improvement.

Next step is to create one level of finished artwork, and the heads-up display. Altogether this should be enough to share the game with colleagues for feedback. The artwork will involve some 3D modeling, textures, 2D graphics and basic sound effects. Once the initial 3D models are created, variations can be made. Lots of work but satisfying.

Who object.setTranslation’d my cheese?

So today is a day of victory, a day to sing about and drink mead, to spin tales told for generations to come.
Yessiree Bob, my joystick is sharing some love!

Stonetrip Shiva uses local and global axis like any 3D package. In their version of the lua script language, kLocalSpace translates of an object’s vertices around that object’s own center. Changes to the kLocalSpace shouldn’t change an object’s position relative to the whole 3D world, something like spinning a marble in one place on a table.

kGlobalSpace on the other hand is relative to the world the object is in.  Changing kGlobalSpace makes sense if you want to move things around in that world from A to B. The marble now moves across the table.

‘AI’ is code attached to an object and loosely regarded as ‘artificial intelligence’.
Following an example on the forums, the ‘Main’ AI  handles keyboard input and sends it to another AI attached to the game camera.

‘Main AI’ is useful for  overall game coordination like user input, environment variables, scoring points and so on.
When a player uses a joystick or taps the iPhone screen and moves the in-game camera, the player is actually triggering functions on the ‘Camera’ AI.

In my game’s case, the player presses side to side to rotate, and moves their finger up and down to move forwards or backwards. The problem is, the virtual joystick moves a virtual vehicle.
Direct motion backwards and forwards gives the player great control, but doesn’t emulate the real sense of steering and moving a car. Yet steering the camera like a car makes it harder to move around the 3D space and still find the target. I’m seeking a joyful tension among control, ‘realism’ and difficulty so this is crucial to balance out the ‘challenge’ vs ‘control’ to equal ‘fun’.

The side-to-side rotation is being handled in global space. The motion back and forwards first gets applied to the camera in local space, then an invisible ‘helper’ object absorbs those numbers and matches them in global space. This helper object is attached to the camera, so we have a bit of flexibility moving the camera vs moving this ‘helper’ object. I’d think a helper object is more important when you want to use dynamics on the camera, or want to move in one direction while looking in another different direction, or if there are compatibility issues with AI code on a helper object vs on a camera itself.

Understanding object.translate and object.setTranslation is important.  The first is a cumulative number relative to the last position the object was in. The latter, setTranslation, is absolute position in the 3D space. Either can be used depending on need, but the amounts and language used to calculate it may be different.

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.