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:

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 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 overlaid with 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 “children” 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 on top of each other.

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

// query the number of children
int numChildren = sprite.numChildren; // -> 2
// move everything 50 pixels to the right
sprite.x += 50; 
// 'width' and 'height' take into account the sizes and
// positions of the children:
float totalWidth = sprite.width;
// rotate everything 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 stuck the pin through is the root of the coordinate system.

Coordinate systems

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 therefore 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
- (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.

A sample Messagebox

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;

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];

Now you can add additional methods to the class (like 'fadeIn' and 'fadeOut'); and you will need to find out which button the user clicked! This is done using Sparrow's event mechanism, which is shown in a later chapter.

Next Section: Textures and Images

  manual_v1/display_objects.txt · Last modified: 2013/04/30 01:05 (external edit)
Powered by DokuWiki