How To Design An Open-Source iPhone Game

Quick Summary

I love games and I’m a huge math nerd, so I made a new iPhone game based on a famous math problem called The Seven Bridges of Königsberg. I’m selling it in the App Store, but I also want to share it with everyone, so I made it open source.

Table of Contents

Members support Smashing

Wonderful, friendly people who keep this lil' site alive — and get smarter every day.

This article is the first in a series that will walk through iOS programming using this game as an example. This first article gives you an overview of the game and of iOS programming in general. We’ll look at a few specific pieces and see how the whole project fits together.

First Steps

This article uses the real game as the example. You’ll need a couple of things to start using the code:

“You must unlearn what you have learned!” Meet the brand new episode of SmashingConf San Francisco with smart front-end tricks and UX techniques. Featuring Yiying Lu, Aarron Draplin, Smashing Yoda, and many others. Tickets now on sale. April 17-18.

Before We Start

Seven Bridges is a simple game. It doesn’t handle complex physics like Angry Birds does or rich textures like Infinity Blade does. Seven Bridges has just a single player who walks over bridges and bumps into rivers. It sounds easy, but a lot goes into even a simple game like this.

Seven Bridges is written in a combination of Objective-C and Objective-C++. If you’re used to programming in scripting languages such as JavaScript, then Objective-C will come as a shock.

Programming for iOS

Getting started with JavaScript is easy. You start with a little jQuery behind a button and go from there. Many iPhone programming tutorials make it sound like writing iPhone applications is just as easy. It’s not.

Objective-C has been around for nearly 30 years. Programming with it entails learning a large ecosystem and some fundamental concepts of how it all holds together. All of the Objective-C documentation assumes that you have a strong background in both object-oriented programming and the C programming language.

The news isn’t all bad. Objective-C shows its age in some areas, but the tools are well written and the runtime environment is amazing.

The Files

You already have the code on your computer, so let’s get a better idea of what we’re looking at. You can tell what each file does by looking at its extension.

.h These files are class headers. Each of these files represents the public information available about a single class or object in the project.

.m These files are Objective-C implementation files.

.mm These files are Objective-C++ implementation files. Objective-C++ is a hybrid in which you can use parts of Objective-C and C++ in the same file. Seven Bridges uses Objective-C++ so that it can take advantage of frameworks written in C++.

.xib These files define visual views in the game where you can lay out buttons and other controls.

.png These files are individual images used in the game.

.pvr.gz These files contain large sets of images called sprite sheets. The images are combined into a single file so that they load faster and take up less space.

.plist These are XML properties files. They define the basics of how the application works and the positions of the images in each sprite sheet.

.m4a These are audio files that provide the sounds for the game.

Xcode projects also include a folder named your project.xcodeproj containing all of the project’s configuration files. Mine is named bridges2 because I messed up the project so badly that I had to start over.

Is your pattern library up to date today? Alla Kholmatova has just finished a fully fledged book on Design Systems and how to get them right. With common traps, gotchas and the lessons she learned. Hardcover, eBook. Just sayin'.

The Frameworks

In addition to the standard Objective-C libraries, Seven Bridges uses three major frameworks.

UIKit

Every iOS application starts with UIKit. This comprehensive framework from Apple provides all of the basic controls your application uses, such as windows, buttons and text fields. Most of the classes in UIKit start with the prefix UI, like UIWindow and UIButton.

The easiest way to work with UIKit is by using the visual editor provided with Xcode. Xcode makes it easy to drag and drop controls into your application. It works well for business applications, but it wouldn’t know anything about the rivers or bridges in my game. For that, I need a gaming framework and a bit of code.

Cocos2d

You could draw on the screen directly with OpenGL, but that signs you up for a lot of work in managing each pixel on the screen. Game libraries provide higher-level support for placing images, creating animations and managing your game. There are a few for iOS.

Cocos2d handles all of the animations in my game, as well as the scenes where you’re actually playing; it also handles sprites, which we’ll get to a little later. It was originally written in Python for desktop applications, so make sure to search for “Cocos2d iPhone” when you’re looking for help on the Web. Cocos2d files start with the prefix CC.

Cocos2d handles user interactions, but it can’t handle interactions with objects. I need to know when the player runs into a river, bridge or any other object. That type of collision detection is made much easier with a physics library such as Box2d.

Box2d

Box2d is a physics engine written in portable C++. It can handle complex variables like gravity and friction, but we’re only using it for collision detection. Box2d files start with the prefix b2.

My game doesn’t use the complex physics of swinging candy from Cut the Rope or of sloshing liquids from Where’s My Water? It just handles a player walking around the screen and bumping into things. Box2d tells me when those bumps happen, and my code handles the rest.

In this article, we’ll see how to use these three frameworks together to build a scene, integrate sprites, respond to user gestures and detect collisions.

Building A Scene

The playable area of the game is drawn with LevelLayer, which extends the Cocos2d class CCLayerColor. This layer handles all of the drawing in the game and the responses when the user taps on the game screen.

The game is made up of a set of images called sprites. Each item in the game’s scene, such as a bridge or a piece of a river, is made up of a separate image file.

Images used this way, instead of being created programmatically, perform much faster and can include subtleties such as shading and texture without needing a lot of code. The technique also makes delegating artwork easier because the artist doesn’t need to know anything about Objective-C.

The sprites all fit together into a single file called a sprite sheet. Cocos2d takes that image and a properties file containing coordinates and creates the individual images from it when the application runs.

Managing sprite sheets manually is a real pain, so I used TexturePacker. It costs a little money, but $25 is well worth the hours of adjusting pixel coordinates it saved me. TexturePacker can also convert a sprite sheet to PVR (the internal image format for iOS) and compress it with Gzip so that it loads even faster.

Once we’ve created the sprite sheet, we’re ready to add the sprites to a scene.

Adding the Sprites

Xcode makes it easy to drag and drop images into a view, but that won’t work for dynamic scenes like our game levels. We’ll need to write some code.

Cocos2d interprets our sprite sheet with the CCSpriteBatchNode class. We initialize the sprite sheet once for the application and then use the individual items when we build our level.

This code creates a new sprite sheet with our sprites file, initializes it with the properties file that defines the images in the sheet, and adds the sheet as a child to our current scene. You can see the full code for this in the init method of LevelLayer.mm.

Now that we have the sheet, we can use it to load individual sprites, like this:

This code snippet creates a new sprite from the sprite sheet, adds that sprite as a child of the sheet, and positions it 100 points from the left side of the screen and 100 points up from the bottom. (We’ll get to what “points” are in the next section.)

Cocos2d manages the sprite sheets with a caching mechanism it calls the sprite frame cache. We loaded the frame cache in the previous code snippet when we loaded the bridgesprites.pvr.gz sprite sheet file and the bridgesprites.plist file that describes it. Cocos2d loaded all of those images into the cache, and we can now get them by name.

All positions in Cocos2d are based on a coordinate system in which point 0,0 is in the bottom-left corner. UIKit puts the point 0,0 at the top left of the screen. There are a few places where we need to convert back and forth further in the code.

Instead of using points directly, the game uses a tile system to position the sprites.

Pixels, Points and Tiles

iOS supports five devices:

iPhone 3.5 inch

iPhone 3.5 inch with Retina display

iPhone 4 inch with Retina display

iPad

iPad with Retina display

Supporting every device means handling multiple layouts. Cocos2d gives us a lot of help here by supporting points instead of pixels when specifying layout. Pixels represent an exact screen location, whereas points are relative to the device. This makes it easy to support Retina devices whose screens are the same size but pack in twice as many pixels.

Seven Bridges goes farther by defining a tile system. Tiles are a way of grouping pixels into larger squares for easier layout. We make the screen 28 tiles tall and at least 42 tiles wide. The tiles make it easy to define where the sprites go on each level. Switch the DEBUG_DRAW constant in Bridgecolors.h to true and you’ll be able to see the tile grid in the background of each level.

Touches and Interactions

UIKit handles touches on controls such as buttons and tables, but for the game scene we’ll need a generic touch handler. Cocos2d provides that with the ccTouchesEnded method. First, we have to tell Cocos2d that we want touch events, like this:

self.isTouchEnabled = YES;

Then, we implement the ccTouchesEnded method to get called whenever the user taps the screen.

If the player isn’t on the screen yet, then place the player sprite where the user has touched and return.

If the player is on the screen, then move the player from the current location to the place where the user has touched.

That’s all we need to do for the touch. The real logic of the game happens when we handle the collisions.

Boxes and Collisions

Each object on the screen is a sprite: a little image file that sits at particular coordinates on the screen. Cocos2d can handle the position and animation of those images, but it won’t know if they run into each other. This is where Box2d shines.

The LayerMgr class adds and removes all of the images from the layers in the game. When it adds an image, it also creates a box around it. Boxes are the fundamental concept of Box2d. They outline the position of an object on the screen and detect when it interacts with other objects. Box2d supports complex box shapes, but all of the boxes in this game are rectangles.

Whenever we add an image to the scene, we draw a box around it with Box2d. The box works with a helper class called a contact listener. This listener sits in a timer and asks the Box2d model if any of the objects have run into each other after each frame of the game.

The contact listener implements two important functions: BeginContact and EndContact. These functions are called when two objects come into contact and when that contact stops. When that happens, we save the data so that we can use it in the layer that renders the level.

The contact listener gives us the two sprites that collided, and we’ll use the tag of each sprite to determine how to handle it. When two sprites run into each other, we react with some simple logic:

If the player contacts a river, then bounce back.

If the player contacts a bridge, then check whether the bridge has been crossed.

If the bridge is crossed, then bounce back.

If the bridge isn’t crossed, then cross it and move the player to the other side.

Separating the player’s movement from the collision makes it easy to move the player around the screen. We don’t have to define islands or worry about managing where the player is allowed to move. We just place each sprite in the level and wait for the player to run into it.

Defining Levels

Each level in the game is defined in a separate JSON document. Separating the level definitions from the code is important so that we can update the levels or add new ones without having to change the code.

Cocos2d manages the sprite sheets with a caching mechanism it calls the sprite frame cache. We loaded the frame cache in the previous code snippet when we loaded the bridgesprites.pvr.gz sprite sheet file and the bridgesprites.plist file that describes it. Cocos2d loaded all of those images into the cache, and we can now get them by name.

All positions in Cocos2d are based on a coordinate system in which point 0,0 is in the bottom-left corner. UIKit puts the point 0,0 at the top left of the screen. There are a few places where we need to convert back and forth further in the code.

Instead of using points directly, the game uses a tile system to position the sprites.

Pixels, Points and Tiles

iOS supports five devices:

iPhone 3.5 inch

iPhone 3.5 inch with Retina display

iPhone 4 inch with Retina display

iPad

iPad with Retina display

Supporting every device means handling multiple layouts. Cocos2d gives us a lot of help here by supporting points instead of pixels when specifying layout. Pixels represent an exact screen location, whereas points are relative to the device. This makes it easy to support Retina devices whose screens are the same size but pack in twice as many pixels.

Seven Bridges goes farther by defining a tile system. Tiles are a way of grouping pixels into larger squares for easier layout. We make the screen 28 tiles tall and at least 42 tiles wide. The tiles make it easy to define where the sprites go on each level. Switch the DEBUG_DRAW constant in Bridgecolors.h to true and you’ll be able to see the tile grid in the background of each level.

Touches and Interactions

UIKit handles touches on controls such as buttons and tables, but for the game scene we’ll need a generic touch handler. Cocos2d provides that with the ccTouchesEnded method. First, we have to tell Cocos2d that we want touch events, like this:

self.isTouchEnabled = YES;

Then, we implement the ccTouchesEnded method to get called whenever the user taps the screen.

If the player isn’t on the screen yet, then place the player sprite where the user has touched and return.

If the player is on the screen, then move the player from the current location to the place where the user has touched.

That’s all we need to do for the touch. The real logic of the game happens when we handle the collisions.

Boxes and Collisions

Each object on the screen is a sprite: a little image file that sits at particular coordinates on the screen. Cocos2d can handle the position and animation of those images, but it won’t know if they run into each other. This is where Box2d shines.

The LayerMgr class adds and removes all of the images from the layers in the game. When it adds an image, it also creates a box around it. Boxes are the fundamental concept of Box2d. They outline the position of an object on the screen and detect when it interacts with other objects. Box2d supports complex box shapes, but all of the boxes in this game are rectangles.

Whenever we add an image to the scene, we draw a box around it with Box2d. The box works with a helper class called a contact listener. This listener sits in a timer and asks the Box2d model if any of the objects have run into each other after each frame of the game.

The contact listener implements two important functions: BeginContact and EndContact. These functions are called when two objects come into contact and when that contact stops. When that happens, we save the data so that we can use it in the layer that renders the level.

The contact listener gives us the two sprites that collided, and we’ll use the tag of each sprite to determine how to handle it. When two sprites run into each other, we react with some simple logic:

If the player contacts a river, then bounce back.

If the player contacts a bridge, then check whether the bridge has been crossed.

If the bridge is crossed, then bounce back.

If the bridge isn’t crossed, then cross it and move the player to the other side.

Separating the player’s movement from the collision makes it easy to move the player around the screen. We don’t have to define islands or worry about managing where the player is allowed to move. We just place each sprite in the level and wait for the player to run into it.

Defining Levels

Each level in the game is defined in a separate JSON document. Separating the level definitions from the code is important so that we can update the levels or add new ones without having to change the code.

Each level defines a name, an ID and a set of objects that go in the level.

Each object has a coordinate in the tile system and a set of properties that define how the object works. For example, a bridge specifies an orient property to determine whether the bridge is horizontal or vertical and a color property to determine whether the bridge has a particular color. A separate page defines everything you can put in a level.

Many games define levels in XML, but I chose JSON because it’s faster to parse and works better with the Web. One day, I’d like to create a mode in which you can load new levels from a website without having to download a new version of the game.

The Level class defines each level; levels are controlled by a LevelMgr. The level manager loads the levels, sorts them for the menu and creates the thumbnails of each level on the menu screen.

Seven Bridges does a lot more, but this is a good place to stop for the first article.

We’ve reviewed the major frameworks, looked at the anatomy of a game, and learned the basics of user interactions. In the next article, we’ll delve deeper into the pieces and see how they all fit together.