Introduction

iTouchBalls is an iOS
port of Balls!, a very simple
puzzle game in which the player must remove an specific number of bouncing
balls from the screen by starting a chain reaction of explosions. In this
port, instead of using the mouse, the player must tap on the screen to start
the first explosion and trigger the chain reaction.

Being an iOS application, iTouchBalls is written in Objective-C.

Download

There is no precompiled version of iTouchBalls available because I highly
doubt that Apple would allow this game in his
App Store. However, the source
code, extracted with
atangle, and an
Xcode project file
are available at the following URL:

Code

The View

Even tough Apple recommends to separate the application in three different
responsibilities using a Model-View-Controller (MVC) pattern, I’ve decided to
put everything in a single UIView derived class which I named BallsView.

This class must be initialized by calling the UIView inherited
initWithFrame function and passing the frame with the view’s size.

<<create view>>=BallsView*view=[[BallsViewalloc]initWithFrame:frame];

This method, besides calling its parent’s method and initialize the object’s
interface, sets the view’s background color as well as the application’s main
loop which will update the game’s logic and force a redraw of the view.

Once the view is created, I can set it to the application’s window to make the
view visible. The function that I am using to set the view, setContentView,
is not documented and, in fact, XCode will complain that UIWindow might not
respond to this message. The method exists and I use it because I remember
using it from regular OS X development. Unfortunately, I wasn’t able to set
the view using any other method.

<<set view in window>>=[windowsetContentView:view];

Main Loop

Differing from traditional imperative applications, an iOS application does not
have the common even loop in which the application can check for incoming
events and act upon them. This is all handled by the UIApplicationMain
function, among other initialization and set up routines. Unfortunately, for
games, this means that I no longer have the option to add the calls to the
methods that update the game’s internal logic each frame in that loop.
Instead, I need to find a way to call my update function periodically. This is
where NSTimer comes to help me.

With NSTimer I can schedule a call to a function of my choice at some point
in the future and this call will be repeated until the timer gets invalidated.
Given that I want to update the game’s logic at an speed of 15 frames per
second, I need to call the update function every 66 milliseconds (1000 / 15.)

<<set up main loop>>=mainLoop=[NSTimerscheduledTimerWithTimeInterval:0.066ftarget:selfselector:@selector(update)userInfo:nilrepeats:YES];

The mainLoop variable is defined in the BallsView class and released when
the class is deallocated.

<<BallsView interface>>=NSTimer*mainLoop;

<<release main loop>>=[mainLooprelease];

The function referenced in the selector parameter is the function that will
be called repeatedly every 0.066 seconds. This function’s responsibility is to
update the balls and the explosions in the game as well as to check whether the
game is done. Once these are updated, I trigger a redraw by telling iOS that
the view needs to display again.

The drawing is performed in drawRect:. Here I need to get the current
graphic’s context and then simply draw the balls, the explosions, and the
status text onto this context, if there is any ball on the level. If there are
no balls yet, I need to tell the player to tap the screen to start the level.

To draw the label, I am going to use ‘NSString` drawAtPoint:withFont which
prints the text at the given point on the current context — the one I’ve got
when called UIGraphisGetCurrentContext — with the specified font. As I want
this text to be drawn at the view’s center, fist I need to compute the position
where to draw the text. Fortunately, NSString also has the sizeWithFont:
function that returns the rendered string’s size if it were drawn using the
passed font. Knowing the text’s size as well as the view’s size, the position
on there to start drawing is half the view’ size less half the text size, in
both vertical and horizontal.

Status Text

The status text is an string with the number of balls removed from the level
and the target balls to remove. I need to get the current level and the
remaining balls on the screen to know the number of removed balls, that I can
get with a simple subtraction.

This string needs to be drawn at the screen’s bottom right corner. Again, I am
using NSStringsizeWithFont: to know the area used by the text with the
intended font but this time I have to subtract the whole text’s size from the
view’s size to render the text at the bottom’s right corner.

Levels

The levels only requires two pieces of information: the number of balls
bouncing on the screen and the minimum number of balls to remove, known as
target. These two never change during the lifetime of a level and thus can be
set read-only.

The levels are initialized by the BallsView when created and they are stored
in an NSArray object. I also keep a variable to know in which level the game
is currently. This variable is initially set to the first level, which is 0.

<<BallsView interface>>=NSArray*levels;unsignedintcurrentLevel;

For convenience, I am going to add a class method to Level that creates the
level and calls autorelease on it, thus freeing me to have to keep the
references and releasing them when adding them to the array.

Balls

The balls in this game are just circles that bounce around the screen. Thus,
they need a position, a direction, a radius and a color. But, to make it
easier to draw onto a Quartz context, I am going to store the balls as the
rectangle that encloses the circle, the direction it is moving, and its color.

Thus drawing all the balls is a matter of calling this function with the
context to draw on.

<<draw balls>>=for(Ball*ballinballs){[balldrawToContext:context];}

Although this approach makes updating the balls a little more complicated
because now, besides updating the current position according to the direction,
I have to find the rectangle’s center position in order to know whether to
change the direction when the ball must bounce around the screen’s limits,
which are passed as a parameter when updating.

Given that I’ll use the balls position later to know whether the balls collides
with an explosion, I think it is better to put this center position in a
position property of Ball. This property returns the Ball’s enclosing
rectangle’s origin point but with an offset equal to the ball’s radius.

<<Ball position property>>=-(CGPoint)position{returnCGPointMake(self.rect.origin.x+self.radius,self.rect.origin.y+self.radius);}

The ball’s radius is also a property, for the same reason as the position, and
can also be extracted from the ball’s rectangle, knowing that both the
rectangle’s width and height — they are the same value — are the ball’s
diameter, the radius is half either of these values.

balls, both when drawing and when updating, is an object of type
NSMutableArray owned by BallsViews and thus is created when initializing
BallsView.

<<BallsView interface>>=NSMutableArray*balls;

<<init BallsView interface>>=balls=[[NSMutableArrayalloc]init];

Of course, since BallsView has ownership of this object, it is also
responsible of releasing it.

<<release BallsView interface>>=[ballsrelease];

Explosions

Like the balls, the explosions in iTouchBalls are simple circles. But,
unlike the balls, instead of moving around the screen they change their radius
in such a way to mimic an explosion’s blast. Like I did for the balls, I am
going to store the explosions as the rectangle that encloses the explosion and
its color. The explosion has also a growth rate which is the speed in which
the explosion grows or shrinks.

But updating the rectangle is now a little more involved than just updating the
radius property. Now I have to grow or shrink the explosion’s rectangle around
the center. Given that the growth rate is exactly how many pixels the radius
grow each update, I only have to move the rectangle’s position away from the
center by that amount while, at the same time, I make the rectangle bigger by
double that amount, because the rectangle’s size represents the diameter, not
the radius. Of course, if the growth is negative (i.e., shrinking) then the
operation is reversed, but this happens automatically because the growth rate’s
variable is negative.

Another thing to take into account when updating the explosions is that they
grow until a certain limit. This limit is passed as a parameter to the update
function, but this limit is the maximum radius, which is half the rectangle’s
width or height. When this limit is reached, then the explosion starts to
shrink until the ball’s diagonal is 0.

Although the idea behind Explosion’s updateWithMaxRadius: is the same as the
ball’s updateWithBounds:, traversing and updating the explosions is not as
straightforward as with the balls. The main difference is that besides calling
the update function, I have to check whether the explosions collide with a ball
or not. I also have to make sure that explosions no longer valid (i.e., its
radius reached 0) get removed from the game.

The check whether the explosion must be removed, I need to know its radius. If
the radius reached 0, then the explosion is no longer valid and must be removed
from the array. I also need to subtract one from the explosion’s index or I
would skip the next explosion, because removing the explosion moves all
posterior explosions one position to the beginning of the array.

If the explosion is still active (i.e., its radius is greater than 0) then I
need to check whether collides with any of the balls on the screen. If the
explosion is colliding with a ball, that ball is replaced with a new explosion
at the same position and with the same color as the colliding ball. The new
explosion is added at the array’s beginning in order to be draw under older
explosions. This is because I am drawing the explosions from the first index
to the last. If the drawing for-loop was done in reverse, then putting the
explosion at the array’s end would be enough, and it could even improve the
application’s performance.

Knowing that both the explosion and the ball are circles, I can compute the
euclidean distance between the two to check whether they are colliding or not.
If the distance is less than the sum of their radius, then the explosion and
the ball are colliding. To avoid the square root in computing the euclidean
distance, instead of the actual distance, which I do not require, I am going to
check whether the squared distance is less than the squared sum of their
radii.

The only last thing to check for is whether the level is done or not. The
level is done when the last explosion is fired and destroyed. This happens
when the there was any explosion before the last update and then there is not
more explosion left. Since the player can only start a single explosion per
level, if there is no longer an explosion available, then I can assume there is
nothing else to do.

<<check if there is any explosion>>=BOOLanyExplosionLeft=[explosionscount]>0;

To end the level, the only thing I have to do is remove all the balls from
their array. But before that, I need to check if we reached the level’s target
amount of balls to remove. With the number of balls still left and the number
of balls that there were initially, I can readily find out whether the level is
completed or not. If it is, then move to the next level, if there are more
levels.

explosions, like balls above, is an object of class NSMutableArray owned
by BallsView and created with it.

<<BallsView interface>>=NSMutableArray*explosions;

<<init BallsView interface>>=explosions=[[NSMutableArrayalloc]init];

As BallsView has ownership of this object, it is also responsible to release
them.

<<release BallsView interface>>=[explosionsrelease];

Tap, Tap, Tapping

Now that I have most of the pieces in place, I need to be able to start the
game by tapping on the screen.

When starting, the game waits for the player to tap the screen before creating
the balls for the current level. I know when the player tapped the screen
because BallsView receives the touchesEnded:withEvent message from the
underlaying system. I’ve chosen touchesEnded:withEvent instead of
touchesBegan:withEvent because I am going to use the same message to create
the explosions and I want a way to rectify, as it were, the explosion’s initial
position if I did choose the wrong spot.

In this message I thus need to know whether the level has already started. I
can easily know that by looking at the number of elements inside the balls
array. If this array is empty, the level hasn’t started yet and I must start
the game by creating as many balls as dictated by the current level.

The arc4random() is a function that returns a pseudo-random number between 0
and 232-1 and doesn’t require a seed value, unlike rand(). Thus, it is
more convenient in this case, but I need to include the stdlib header where
it is declared.

<<BallsView includes>>=#include<stdlib.h>

The explosion is easier to create than the balls. The player can only create a
single explosion, hence I need to check whether there is already an explosion
in the level or not. If there is none, then I create a 1 pixel explosion at
the position where the player tapped the screen. I can’t create a 0 pixel
explosion because when an explosion reaches 0 it gets removed from the level.

Appendix A. Main

The main file for an iOS application is really simple. I only have to call
UIApplicationMain passing the arguments from the command line and telling
which is the application class. As with most Objective-C applications, the
main function is wrapped inside an NSAutoreleasePool that manages the
application’s memory allocation.

The ITouchBalls class is not only responsible to allocate the window but also
its view, which is the class the actually manages the whole game’s logic. This
must be done inside the applicationDidFinishLaunching: function called by
UIApplicationMain when the application has done with the initialization
process. This could be considered the “real” application’s main.

<<create and set view>>=-(void)applicationDidFinishLaunching:(UIApplication*)application{

The first thing I want to do in this function is to retrieve the device’s
screen area because I am going to need the area later to create the window and
its view.

The screen area can be retrieved either using the function bounds or
applicationFrame of UIScreen applied to the mainScreen. The difference
between the two functions is that bounds will give the total device’s area
while applicationFrame returns the device’s area minus the space occupied by
the status bar, if shown. In this case, I want to make a full screen window,
thus I’m going to use bounds.

<<create and set view>>=CGRectframe=[[UIScreenmainScreen]bounds];

Now I can make the window and its view, assign the view to the window, mark
this window as the main window, and show it.

<<create and set view>>= <<create view>>window=[[UIWindowalloc]initWithFrame:frame]; <<set view in window>>[windowmakeKeyAndVisible];[viewrelease];}

Appendix B. Prefix Header

A prefix header is a header that the compiler automatically includes at the
top of each source file. This header file is pre-compiled in order to speed up
the building time. Thus, in this header I import all the other headers used by
an iOS application, but only if the source being compiled is actually an
Objective-C source file.

Along with the bundle’s name, iOS also shows an icon. In this case, though,
I’ll use an empty icon’s filename because I don’t have any icon for this game.

<<BundleSettings>>=
<key>CFBundleIconFile</key><string></string>

The bundle needs to have an identifier that can be used by iOS to associate
file types to the application, or store the application’s settings in the
appropriate place, among other things. This identifier is a reverse DNS style
string where, generally, the first part is a developer’s prefix — usually the
developer’s domain — followed by the application’s name. The application name
must be RFC-1034 compliant.

Appendix D. License

This program is distributed under the terms of the GNU General Public License
(GPL) version 2.0 as follows:

<<license>>=// This program is free software; you can redistribute is and/or modify// it under the terms of the GNU General Public License version 2.0 as// published by the Free Software Foundation.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 50 Temple Place, Suite 330, Boston, MA 02111-1307 USA