An Introduction to Sparrow

Table of Contents

Foreword

The Sparrow framework allows you to create interactive applications for the iPhone platform. The main target is the creation of 2D games, but Sparrow can be used for any graphical applications.

This document provides an introduction to developers who want to learn the basic principles of Sparrow. After reading this document, you should be able to start developing your project with Sparrow.

Preconditions

Sparrow is written in pure Objective-C. You will need to have a basic knowledge of this language to use Sparrow. There are numerous books available that teach you the language, and there is a very good introduction available on Apple’s iPhone developer page. If you have not used the language yet, fear not: if you know any other object oriented language, you will learn it quickly.

You don’t need to know much about the iPhone API. If you plan to develop your application completely with Sparrow, you won’t need to use any Cocoa classes.

For a lot of applications, though, it makes sense to intermix Sparrow and Cocoa. Personally, I create the game menu using Cocoa (thus having easy access to text input, table view controllers, etc.) and the game itself with Sparrow. All Sparrow rendering is done inside an ordinary UIView object (SPView), just like anything else in Cocoa.

Your first Sparrow project

There is a small scaffold project for Xcode delivered with Sparrow. Follow the instructions in Getting Started to convert this to the starting point of your application.

When you start the application, you will see a red rectangle on the screen. This rectangle was rendered with Sparrow — if you can see it, everything was set up correctly.

Now have a look at the source code of the project. For now, the only important class is “Game”. It is the starting point of your game. All necessary setup-stuff (creating the window, connecting Sparrow to Cocoa, etc.) was already prepared for you.

Game.h:

@interface Game : SPStage
@end

Game.m:

@implementation Game

- (id)initWithWidth:(float)width height:(float)height
{
    if (self = [super initWithWidth:width height:height])
    {
        // this is where the code of your game will start.
        // in this sample, we add just a simple quad

        SPQuad *quad = [SPQuad quadWithWidth:100 height:100];
        quad.color = 0xff0000; // 0xRRGGBB -> this is red
        quad.x = 50;
        quad.y = 50;
        [self addChild:quad];
    }
    return self;
}
@end

As you can see, the Game class inherits from SPStage. The stage is the root display object of your game. Everything that is somehow connected to your stage will be rendered. To create a game, you populate the stage with your content.

In the sample, the content is just a simple SPQuad (a red rectangle). It has a color (red), a size (100×100) and a position (50, 50). And it is a child of the stage. It has to be, otherwise it would not be rendered. This is a part of the “display tree” architecture of Sparrow, into which we will dive in the following section.

Display Objects

The root class of everything that can be displayed on the screen is a subclass of SPDisplayObject. The SPQuad of the last chapter inherits from SPDisplayObject, as do many other classes: Sparrow displays images, textfields, and more.

This is the class hierarchy of display objects:

If we ignore the event dispatcher (we will come back to it later), the topmost class is SPDisplayObject. As I said before, everything that is displayed on the screen will be a subclass of this class. SPDisplayObject is an abstract class — you cannot use it directly. But it provides methods and properties that all display objects share:

  • position (x, y)
  • size (width, height)
  • scale factor (scaleX, scaleY)
  • rotation
  • opacity (alpha)
  • etc.

If you have a look at the concrete subclasses of SPDisplayObject, you see several classes that can be used out of the box: SPQuad, SPImage, SPTextField, SPButton and SPMovieClip. It should be relatively obvious what can be done with those classes.

And then there is another abstract class called SPDisplayObjectContainer. As its (clumsy) name suggests, it acts as a container for other display objects. It allows display objects to be organized into a logical system — the display tree.

The Display Tree

More often than not, you will want to compose basic objects to create more complex ones. Imagine an image that has some text painted on it (an SPImage and an SPTextField). When you move the image, the text should move with it. The same applies for changes in size, scaling, rotation, and opacity. The two objects should simply act as one.

For that, you use an SPDisplayObjectContainer, or an SPSprite. Those classes are basically the same; the only difference is that SPDisplayObjectContainer is an abstract class, while SPSprite is not. (In the future, SPSprite might get additional methods and properties, but currently that is the only difference.)

So, to group text and image together, you create an SPSprite and add text and image as ‘childs’ of that class:

SPSprite *sprite = [SPSprite sprite];
SPImage *image = [SPImage imageWithContentsOfFile:@"image.png"];
SPTextField *textField = [SPTextField textFieldWithWidth:100
                                      height:20 text:@"Text"];
[sprite addChild:image];
[sprite addChild:textField];

First we added the image to the sprite, then the textfield. The order in which you add the children is relevant — the children are placed like layers over each other.

Now, the sprite has two children, and manipulating it will influence how textfield and image are displayed.

int numChildren = sprite.numChildren; // --> 2

// the parent is now the sprite object created above
SPDisplayObject *parent = image.parent;
sprite.x += 50; // moves everything 50 pixels to the right

// 'width' and 'height' take into account the sizes and
// positions of its children:
float totalWidth = sprite.width;

// rotation by 90 degrees (Sparrow expects radians)
sprite.rotation = SP_D2R(90);

Every display object container has its own coordinate system. Imagine pinning sheets of paper on a pin-board. Each sheet represents a coordinate system with a horizontal x-axis and a vertical y-axis. The position you sticked the pin through is the root of the coordinate system.

Now, when you rotate the sheet of paper, everything that is drawn onto it (e.g. image and text) will rotate with it — as do the x- and y-axes. However, the root of the coordinate system (the pin) stays where it is.

The position of the pin therefor represents the point the x- and y-coordinates of the sheet are pointing at, relative to the parent coordinate system (= the pin-board).

Keep the analogy with the pin-board in mind when you create your display hierarchy. This is a very important concept you need to understand when working with Sparrow.

SPDisplayObjectContainer defines many methods that help you manipulate its children:

- (void)addChild:(SPDisplayObject *)child;
- (void)addChild:(SPDisplayObject *)child atIndex:(int)index;
- (BOOL)containsChild:(SPDisplayObject *)child;
- (SPDisplayObject *)childAtIndex:(int)index;
- (int)childIndex:(SPDisplayObject *)child;
- (void)removeChild:(SPDisplayObject *)child;
- (void)removeChildAtIndex:(int)index;
- (void)swapChild:(SPDisplayObject*)child1
        withChild:(SPDisplayObject*)child2;
- (void)swapChildAtIndex:(int)index1 withChildAtIndex:(int)index2;

Creating custom Display Objects

When you create a game, you split it up into logical parts. A simple game of chess might contain the board, the pieces, a pause button and a message box. All those elements will be displayed on the screen — thus, each will be represented by a class derived from SPDisplayObject.

Take a simple message box as an example.

The message box has a background, an icon, text, and two buttons. Yet you want to have one single MessageBox-class that hides those implementation details.

To achieve this, you create a new class called “MsgBox” that inherits from SPDisplayObjectContainer:

@interface MsgBox : SPDisplayObjectContainer
- (id)initWithText:(NSString *)text;
@end

In its constructor, you create everything that makes up the message box:

- (id)initWithText:(NSString *)text
{
    if (self = [super init])
    {
        SPImage *background = [SPImage imageWithContentsOfFile:@"msgbox_bg.png"];
        SPTextField *textField = [SPTextField textFieldWithWidth:100 height:20 text:text];

        SPTexture *buttonTexture = [SPTexture textureWithContentsOfFile:@"button.png"];
        SPButton *yesButton = [SPButton buttonWithUpState:buttonTexture text:@"yes"];
        SPButton *noButton  = [SPButton buttonWithUpState:buttonTexture text:@"no"];

        yesButton.x = 10;
        yesButton.y = 20;

        noButton.x = 60;
        noButton.y = 20;

        [self addChild:background];
        [self addChild:textField];
        [self addChild:yesButton];
        [self addChild:noButton];
    }
    return self;
}

Now, you have a simple class displaying a message and two buttons. To use it, just create an instance of MsgBox and add it to the display tree:

MsgBox *myMsgBox = [[MsgBox alloc] initWithText:@"Really exit?"];
[self addChild:myMsgBox];
[myMsgBox release];

This can be done with all kinds of objects. Of course, you will add additional methods to the class, like ‘fadeIn’ and ‘fadeOut’-methods. And you will need a way to find out which button the user chose! This is done using Sparrow’s event mechanism, which is shown in a later chapter.

Images vs. Textures

In the last section, you might have noticed that we displayed an image using the SPImage object, but used an SPTexture object to set the button’s image. What is the difference between an image and a texture?

A texture is just the data that describes an image — like the file that is saved on your digital camera. You can’t show anybody that file alone. It’s all zeros and ones, after all. You need some device that can display the image, like your iPod.

In Sparrow, that device is SPImage. SPImage has another constructor that makes it more clear what happens.

// create an image from a texture:
SPTexture *texture = [SPTexture textureWithContentsOfFile:
                      @"face.png"];
SPImage *image = [SPImage imageWithTexture:texture];

// or do the same in one line:
SPImage *image = [SPImage imageWithContentsOfFile:@"face.png"];

Every texture you create takes up precious memory. So, if you need to display something multiple times simultaneously, load the texture only once, and use different SPImage objects to display them.

If you have understood that concept, have a look at the SPTextureAtlas, as well. In a nutshell, it allows you to load a lot of textures from one single texture file — that speeds up rendering and saves memory. (Due to the architecture of the iPhone’s graphics hardware.) The demo project will get you started, and there’s a blog article that shows how to use it, as well.

Events

The last chapter showed you how to create a class that displays a message box with 2 buttons. However, the message box lacked a critical feature: there was no way to notice when a user clicked on one of its buttons, and on which button.

In Sparrow, this is handled through a flexible event system. If you scroll back to the image displaying the class hierarchy of display objects, you will notice that SPDisplayObject inherits from a class called SPEventDispatcher. Thus, each and every display object is already prepared to handle events. In Sparrow, the event system is tightly coupled with the display tree. This has some advantages we will see later.

The easiest way to understand the event system is by looking at an example. The following code creates a button and logs out a message when it was pressed.

- (id)init
{
    if (self = [super init])
    {
        SPTexture *buttonTexture = [SPTexture textureWithContentsOfFile:@"button.png"];
        mButton = [SPButton buttonWithUpState:buttonTexture];
        [mButton addEventListener:@selector(onButtonTriggered:) atObject:self
                          forType:SP_EVENT_TYPE_TRIGGERED];
        [self addChild:mButton];
    }
    return self;
}

- (void)onButtonTriggered:(SPEvent *)event
{
    NSLog(@"The button was triggered!")
}

After creating the button, you use the method ‘addEventListener:atObject:forType’ to register an event listener on that button. The button inherited this method from SPEventDispatcher. The arguments have the following meaning:

  • addEventListener: the method that is called when the event is fired. That’s always a method with one argument of type SPEvent (or a subtype).
  • atObject: the object on which the above method should be invoked.
  • forType: an event type is always identified by a string. In this case, we want to listen for all “triggered”-events.

It’s a good practice to remove that event listener when it’s no longer needed. You can do so e.g. in the dealloc method. (The event dispatcher does not retain the listener, though, so you won’t create a memory leak if you don’t remove it.)

- (void)dealloc
{
    [mButton removeEventListenersAtObject:self
             forType:SP_EVENT_TYPE_TRIGGERED];
    [super dealloc];
}

There are already several useful events predefined in Sparrow:

  • SP_EVENT_TYPE_TRIGGERED: a button was triggered
  • SP_EVENT_TYPE_ADDED: a display object was added to a container
  • SP_EVENT_TYPE_ADDED_TO_STAGE: a display object was added to a container that is connected to the stage
  • SP_EVENT_TYPE_REMOVED: a display object was removed from a container
  • SP_EVENT_TYPE_REMOVED_FROM_STAGE: a display object lost its connection to the stage
  • SP_EVENT_TYPE_ENTER_FRAME: some time has passed, a new frame is rendered (shown in the animation section)
  • SP_EVENT_TYPE_TOUCH: a touch event occurred
  • SP_EVENT_TYPE_TWEEN_STARTED / UPDATED / COMPLETED: a tween changed its state (shown in the animation section)

But you are not limited to those events. It’s easy to create your own.

Creating custom events

The message box we created above should dispatch a custom event when the user pushed one of its buttons. There are 2 ways to do this. One is to fire a different event type for each button, like this.

First, define names for the events (MsgBox.h):

#define EVENT_TYPE_YES_TRIGGERED @"yesTriggered"
#define EVENT_TYPE_NO_TRIGGERED  @"noTriggered"

The event listeners of the buttons can now dispatch those events (MsgBox.m):

// the listener we created for the yes-button:
- (void)onYesButtonTriggered:(SPEvent *)event
{
    SPEvent *event = [SPEvent eventWithType:EVENT_TYPE_YES_TRIGGERED];
    [self dispatchEvent:event]
}

// the listener we created for the no-button:
- (void)onNoButtonTriggered:(SPEvent *)event
{
    SPEvent *event = [SPEvent eventWithType:EVENT_TYPE_NO_TRIGGERED];
    [self dispatchEvent:event]
}

Anybody who uses the message box can now listen for those events:

- (void)init
{
    if (self = [super init])
    {
        // ...
        MsgBox msgBox = [[MsgBox alloc] initWithText:@"Really exit?"];
        [msgBox addEventListener:@selecotor(onMsgBoxYesTriggered:) atObject:self
                         forType:EVENT_TYPE_YES_TRIGGERED];
        [msgBox addEventListener:@selecotor(onMsgBoxNoTriggered:) atObject:self
                         forType:EVENT_TYPE_NO_TRIGGERED];
        [self addChild:msgBox];
        [msgBox release];
    }
    return self;
}

The other way is to always fire the same event, but include information about which button was pressed. This is a common procedure; you will frequently need to pass some information with an event.

This is done by creating a subclass of SPEvent:

MsgBoxClosedEvent.h:

#define EVENT_TYPE_MSG_BOX_CLOSED @"msgBoxClosed"

@interface MsgBoxClosedEvent : SPEvent
{
    BOOL mResult;
}

- (id)initWithType:(NSString *)type result:(BOOL)result
           bubbles:(BOOL)bubbles;

@property (nonatomic, readonly) BOOL result;

@end

MsgBoxClosedEvent.m:

@implementation MsgBoxClosedEvent

@synthesize result = mResult;

- (id)initWithType:(NSString *)type result:(BOOL)result
{
    if (self = [super initWithType:type bubbles:NO])
    {
        mResult = result;
    }
    return self;
}

@end

The event listeners within MsgBox.m would now dispatch this custom event:

MsgBoxClosedEvent *event = [[MsgBoxClosedEvent alloc]
    initWithType:EVENT_TYPE_MSG_BOX_CLOSED result:YES];
[self dispatchEvent:event];
[event release];

Whoever created the MsgBox would now have to listen only to one event type: EVENT_TYPE_MSG_BOX_CLOSED. Which button was pressed is passed via the custom event type.

- (void)onMsgBoxClosed:(MsgBoxClosedEvent *)event
{
    NSLog(@"MsgBox was closed. Result: %d", event.result);
}

Bubbling events

In our previous examples, the event dispatcher and the event listener were directly connected via the “addEventListener”-method. But sometimes, that’s not what you want.

Let’s say you created a complex game with a deep display tree. Somewhere in the branches of this tree, a penguin sprite just collided with a boost container. Now, this information is needed at the root of the display tree, in the game’s main class (the stage). It would be really cumbersome to hand this event down from the penguin over numerous display objects until it reaches the stage.

That’s why events support something that is called “bubbling”. Imagine a real tree (it’s your display tree) and turn him around by 180 degrees, so that the trunk points upwards. The trunk, that’s your stage, and the leaves of the tree are you display objects. Now, if a leave creates a bubbling event, it will go upwards just like the bubbles in a glass of soda, traveling from branch to branch (from parent to parent) until it finally reaches the trunk.

Any display object along this route can listen to this event. It can even pop the bubble and stop it from traveling further.

All that is required to do that is to set the “bubbles”-property of an event to YES.

SPEvent *bubblingEvent = [[SPEvent alloc]
    initWithType:SOME_EVENT_TYPE bubbles:YES];
[self dispatchEvent:bubblingEvent];
[bubblingEvent release];

Anywhere along its path, you can listen to this event:

[self addEventListener:@selector(onBubblingEvent:)
              atObject:self forType:SOME_EVENT_TYPE];

Notice that we are listening to events from ‘self’ — even though the event was dispatched by some other object. We could add a listener to any object along the path of the bubbling event.

This feature comes in handy in numerous situations. Especially concerning touch events.

Touch events

One of the most revolutionary features of the iPhone is that it allows you to control it with your fingers. No physical buttons, not even a stylus is required — and you can even use several fingers simultaneously. Sparrow makes it easy for you to react to touches of one or more fingers.

First of all: if you need support for multitouch, make sure that your instance of SPView (the Cocoa canvas Sparrow draws into) has “multipleTouchEnabled” set to YES. Otherwise, Cocoa won’t inform Sparrow about more than one touch at a time.

Now, to react to touch events, just add the following event listener to your class:

[self addEventListener:@selector(onTouch:) atObject:self
               forType:SP_EVENT_TYPE_TOUCH];

Again, we added the listener to ‘self’, without ever dispatching a touch event ourself. That’s a strong indication that touch events are bubbling events — and indeed, that’s the case. If you think about it, that makes perfect sense:

Imagine our message box. When the user clicked on the text field, obviously anybody listening to touches on the text field must be notified. But the same is true for somebody listening for touch events on the message box — the textfield is part of the message box, so the latter was touched as well. And if somebody listens to touch events on the stage, he will also be notified. The message box is part of the stage, after all.

Now, how to handle touch events? Have a look at this sample code.

- (void)onTouchEvent:(SPTouchEvent*)event
{
    SPTouch *touch = [[event touchesWithTarget:self
            andPhase:SPTouchPhaseBegan] anyObject];
    if (touch)
    {
        SPPoint *touchPosition = [touch locationInSpace:self];
        NSLog(@"Touched position (%f, %f)",
            touchPosition.x, touchPosition.y);
    }
}

That’s the most basic case: Find out if somebody touched the screen, and log the coordinates (relative to the current display object). The method “touchesWithTarget:andPhase:” is provided by SPTouchEvent and helps you find the touches you are interested in.

The target “self” means: find any touches that occurred on me (self) OR my children. Think back to our message box: the target “self” would include the message box and the text field.

The touch phase is “touchPhaseBegan”, thus the position will be logged the moment the finger touched the screen, but not while the finger moves around or leaves the screen. Those are the available touch phases. Their names should be self explanatory.

  • SPTouchPhaseBegan
  • SPTouchPhaseMoved
  • SPTouchPhaseStationary
  • SPTouchPhaseEnded

The method “touchesWithTarget:andPhase:” returns an NSSet containing suitable touches, which are encapsulated in SPTouch objects. The method “locationInSpace:” of SPTouch returns the touch coordinates in just the coordinate system you need.

So much for single touches. Multiple touches are handled just the same. The only difference is that the set returned by “touchesWithTarget:andPhase” contains multiple SPTouch objects.

NSArray *touches = [[event touchesWithTarget:self
    andPhase:SPTouchPhaseMoved] allObjects];

if (touches.count == 1)
{
    // one finger touching
    SPTouch *touch = [touches objectAtIndex:0];
    SPPoint *currentPos = [touch locationInSpace:self];
    SPPoint *previousPos = [touch previousLocationInSpace:self];
    // ...
}
else if (touches.count >= 2)
{
    // at least two fingers touching
    SPTouch *touch1 = [touches objectAtIndex:0];
    SPTouch *touch2 = [touches objectAtIndex:1];
    // ...
}

Notice the method “previousLocationInSpace:” of SPTouch. In this sample, we requested touches with the phase SPTouchPhaseMoved. “previousLocationInSpace” will return the point the touch had in previous frame. This comes in handy when you want to drag an object around: just move the object along the vector between the current and the last touch position.

The demo application contains the class “TouchSheet”, which is used in the “Multitouch” demo scene. It shows you how to let the user drag objects around, rotate and scale them.

Displaying Text

Displaying text is easy. The class SPTextField should be quite self explanatory. By default, it uses the system fonts of the iPhone.

SPTextField *textField = [SPTextField textFieldWithWidth:145
    height:80 text:@"Text" fontName:@"Helvetica"
    fontSize:12.0f color:0xff0000];
textField.hAlign = SPHAlignRight;  // horizontal alignment
textField.vAlign = SPVAlignBottom; // vertical alignment
textField.border = YES;

This is adequate for text that does not change very often. If you need another font than those provided by the iPhone, or you have a textfield that constantly changes its contents, you should use a bitmap font.

Sparrow makes it easy to use bitmap fonts. There is a great tool provided by the folks from AngelCode that lets you create a bitmap font out of any TrueType font you can imagine: Bitmap Font Generator.

This tool is only available for Windows, but there is a similar Java programm called Hiero that does the job nearly as well. However, as it does not create XML output, you have to convert its output files before using them in Sparrow. A blog article has more details on this.

Anyway: after loading a font in that tool, export the font data as an XML file, and the texture as a png with white characters on a transparent background (32 bit). The result is a “.fnt” file and an associated png image containing the characters.

To use the bitmap font, just register it at SPTextField:

NSString *bmpFontName =
    [SPTextField registerBitmapFontFromFile:@"bmp_font.fnt"];

This method returns the name of the font as defined in the fnt-file. Now you can use the font just like any system font.

textField.fontName = bmpFontName;

That’s all you need to do. By the way: you can add the font png to your texture atlas, as well. Just use the following method for registering the font:

NSString *bmpFontName = [SPTextField
    registerBitmapFontFromFile:@"bmp_font.fnt"
    texture:[myAtlas textureByName:@"bmp_font"]];

That way, you save texture space and speed up the rendering.

Animations

Animations are a fundamental part of any game. Sparrow helps you by making animations as simple as possible.

If you think about it, there are two kinds of animations. There are the animations where you know from the beginning exactly what will happen — e.g. when you fade in a message box, or when you move some interface element out of the screen. Then there are very dynamic animations, like enemies moving toward the player. The movement of the enemies can change in any frame, as it depends on the actions of the player.

Let’s first look at the latter type, dynamic animations.

SPEnterFrameEvent

In some game engines, you have what is called a “run-loop”. That’s a endless loop which constantly updates all elements of the scene. In Sparrow, due to the display tree architecture, such a run loop would not make much sense. You separated your game in numerous different custom display elements, and each should know for itself what to do when some time has passed.

That’s what the EnterFrameEvent is for. It’s available in any display object, and is dispatched once in every frame. Here is how you use it:

// e.g. in the init-method
[self addEventListener:@selector(onEnterFrame:) atObject:self
               forType:SP_EVENT_TYPE_ENTER_FRAME];

// the corresponding event listener
- (void)onEnterFrame:(SPEnterFrameEvent *event)
{
    NSLog(@"Time passed since last frame: %f", event.passedTime);
    [enemy moveBy:event.passedTime * enemy.velocity];
}

The method ‘onEnterFrame:’ is called once per frame, and you receive the time that has passed since the last frame. With that information, you can move your enemies, let snowflakes fall down just a little further, etc.

Tween

Now to predefined animations. They are very common and have names such as “Movement”, “Fading”, “Rotation”, etc. Sparrow has a very simple yet powerful approach for those kinds of animations. Basically, you can animate any property of any object, as long as it is numeric (int, float, double).

Those animations are described in an object called “SPTween”. The term “Tween” comes from hand drawn animations, where a lead illustrator would draw important key frames, while the rest of the team drew the frames in be”tween” those frames.

Enough theory, let’s go for an example:

SPTween *tween = [SPTween tweenWithTarget:msgBox time:0.5f];
[tween animateProperty:@"scaleX" targetValue:2.0f];
[tween animateProperty:@"scaleY" targetValue:2.0f];
[tween animateProperty:@"alpha"  targetValue:0.0f];

This tween describes an animation that scales the msgBox to twice its size, and simultaneously reduces its opacity till it is invisible. The animation takes half a second. If you are missing the start values: the animation will start using the current values of the specified properties.

As you can see, you can animate arbitrary properties of an object, and you can do multiple animations in one tween object.

There is more to tweens: you can change the way the animation is executed — e.g. letting it start slowly and get quicker and quicker. That’s done by specifying a transition type.

These are the available transition types:
(the default transition type, ‘linear’, was omitted)

Sparrow's transition types

You can also start the animation only after a delay. And you can listen to events that are dispatched when a tween starts or is completed.

SPTween *tween = [SPTween tweenWithTarget:msgBox time:0.5f
                               transition:SP_TRANSITION_EASE_IN];
tween.delay = 2.0f;
[tween addEventListener:@selector(onTweenCompleted:) atObject:self
                forType:SP_EVENT_TYPE_TWEEN_COMPLETED];

But for now, nothing will happen. A tween object describes the animation, but it does not execute it. That’s what the juggler is for.

Juggler

The juggler takes anything that can be animated (that is, anything implementing the SPAnimatable protocol), and executes the animation. There is always a default juggler available at the stage. Since you can access the stage from any display object (as long as it’s connected to the stage), the easiest way to execute an animation is by the line below — just add the animation (tween) to the stage juggler, and you are done.

[self.stage.juggler addObject:tween];

In many cases, that will be fine. But sometimes, you want to group your animations into different contexts. Let’s say your stage contains a game area, where the game action takes place. When the user clicks on the exit button, you want to pause the game and show an animated message box (this thing won’t stop to pop up in any chapter!), asking the player if he is sure that he wants to exit the game.

In such a case, it makes sense to give the game area its own juggler. As soon as the exit button is pressed, this juggler should just stop to animate anything. The game will freeze in its current state. The message box, however, uses the stage juggler. Thus, it fades in just fine.

When you create a custom juggler, all you have to do is to call its “advanceTime:” method in every frame. I recommend using jugglers the following way:

// In your main game class, listen to EnterFrameEvents:
- (void)onEnterFrame:(SPEnterFrameEvent *)event
{
    if (activeMsgBox)
       // message box is visible, wait for user input
    else
       [gameArea advanceTime:event.passedTime];
}

// the game area class advances its juggler in the
// "advanceTime:" method
- (void)advanceTime:(double)seconds
{
    [gameJuggler advanceTime:seconds];
}

That way, you have neatly separated the animations of the game and the message box.

By the way: the juggler is not constricted to Tweens. As soon as your class implements the SPAnimatable protocol, you can add it to the juggler. The protocol has one method and one property:

- (void)advanceTime:(double)seconds;
@property (nonatomic, readonly) BOOL isComplete;

You already know what “advanceTime:” is for. The “isComplete” property should return “NO” as long as the animation is not complete (an SPTween returns “YES” as soon as the tween has finished). When the juggler finds a completed animation, it removes it from its list of animations. That’s all this method is for.

That way, you could e.g. create a simple MovieClip-class. In its “advanceTime:” method, it could constantly change the texture that is displayed. To start the movie clip, you just add it to a juggler. This blog entry has the details. (Sparrow now contains a class for movies called SPMovieClip, so you will probably use this class instead — but look at the blog post anyway, as you learn a lot about how to use the juggler.)

Delayed calls

There is another type of animation that is not that obvious. Sometimes, you want to do something in the future. Let’s say the player just lost all his energy. Now, you don’t want to stop the game immediately — that would end the game too abruptly. So, you want to call your “gameOver” method with a delay of, say, 3 seconds.

The juggler can do this just as well. The advantage of letting the juggler do this becomes obvious when the game is paused, like in the example above. You not only want to stop all animations, but you want this delayed “gameOver” call be delayed even more.

To do that, make a call like the following:

[[juggler delayInvocationAtTarget:self byTime:3.0f] gameOver];

Confused? This syntax takes a little time to get used to — but it’s very powerful. To understand it, just imagine that first part of the call (the [juggler delay...] part) returns the “target” argument (in our case: self), but only after 3 seconds (!). Thus, three seconds from now, the following code will be executed:

[self gameOver];

That syntax makes it easy to call arbitrarily complex methods. It’s perfectly fine to do something like this:

[[juggler delayInvocationAtTarget:self byTime:1.0f]
    doXYZ:@"text" withA:someObject andB:1234];

Audio Playback

Any decent game requires sound. Sound creates an atmosphere and pulls the player right into the game’s world. Thankfully, Sparrow makes it easy to play sound.

The audio engine needs to be initialized at the game’s start, and stopped before the game ends. If you started your project with the latest scaffold project, this will already be done for you.

// at the beginning:
[SPAudioEngine start];

// and at the end:
[SPAudioEngine stop];

Now to the playback. If you’re in a hurry, this one-liner will play audio in any format known to the iPhone:

[[SPSound soundWithContentsOfFile:@"sound.aif"] play];

This loads the sound from a file and starts playback. Don’t worry, the auto release pool won’t touch this sound until it’s done playing.

Normally, though, you’ll want to have more control about playback. You will use the following two classes to add sound to your game:

  • SPSound
  • SPSoundChannel

You can imagine this class-couple analog to the ‘SPTexture’ and ‘SPImage’ classes. Just as ‘SPTexture’ contains image data, ‘SPSound’ contains sound data. To display an image, you use ‘SPImage’ — to play a sound, you use ‘SPSoundChannel’.

So, if you need a sound multiple times, the ‘SPSound’ will be in memory just once, while several ‘SPSoundChannels’ will reference it. Here is a real-life example:

SPSound *sound = [SPSound soundWithContentsOfFile:@"sound.caf"];
SPSoundChannel *channel = [sound createChannel];
channel.volume = 0.6f;

[channel play];
[channel pause];
[channel stop];
[channel addEventListener:@selector(onSoundCompleted:)
         atObject:self
         forType:SP_EVENT_TYPE_SOUND_COMPLETED];

That should speak for itself. Behind the scenes, the SPSound class will choose the appropriate technology for playback: uncompressed files will use OpenAL, compressed sound will be handled by Apple’s AVAudioPlayer. You don’t have to care. Besides, your sounds will automatically be paused when the application is disrupted (e.g. by a phone call), and will continue playback where they stopped.

You can also set a master volume that influences all sounds at once:

[SPAudioEngine setMasterVolume:0.5f];

The only thing left for you to decide is which file formats to use for your sounds, and how to convert them into this format. There is a blog entry that will teach you all you need to know.

Extending Sparrow

Sometimes, you want to squeeze out the last bit of performance. You want to make the calls to OpenGL yourself. In Sparrow, that’s easy to do. Just override the “render:” method that is implemented by any display object.

- (void)render:(SPRenderSupport *)support
{
    [support bindTexture:mTexture];

    glEnable(GL_POINT_SPRITE_OES);
    // ...
    glDisable(GL_POINT_SPRITE_OES);
}

In that sample, the developer wants to use OpenGL point sprites. By overriding the “render:” method, Sparrow will use this custom code to draw the object.

  • When the render method is executed, OpenGL’s transformation matrices are already pointing to the local coordinate system.
  • The “support” objects contains some methods that help during rendering, e.g. a “bindTexture:” method that only forwards the call to OpenGL if another texture is bound.

If you want to take this path, I recommend you have a look at Sparrow’s default rendering code, and find some orientation there.

That’s it!

Phew! You are finished! Those were the most important parts of Sparrow. If you came this far, you should be prepared for creating your own application.

If you find yourself stuck one time, don’t hesitate to look into the source code of Sparrow. All Sparrow source code was designed to be as readable and understandable as possible. Method and property names were chosen carefully so that everything does exactly what you would expect. There are no vicious side effects or unknown traps. Just learn Sparrow by doing! The small fella won’t bite you, promised =)