Pages

Sunday, October 30, 2011

A Simple Arcade Game With Sparrow: Part 1


        The iOS game market has become one of the biggest game markets available today. The low cost of entry into the market helps make it a prime area for independent developers. The biggest challenge then becomes graphics programing. If you have little or no graphics programing knowledge and try to take on an OpenGL project, you will see what I mean. 
This is exactly how I discovered the Sparrow Framework. I had been doing iOS programing for a little over a year. I had published a few utility apps and was starting to carve out my place in the market. I wanted to get into the game market as well, so I dove into OpenGL. After a frustrating month of trial and error, forum searching, and googling, i thought there must be a better way. 
Then I found the Sparrow Framework. Sparrow is a Cocoa Touch wrapper for 2D OpenGL. Sparrow is open source project created by Daniel Sperl. Its is simple, fast, high quality, and has an amazing support group. Daniel himself is available to answer most questions.  
I was very impressed by Sparrow’s power and ease, and that is why I want to share it with all of you. The remainder of this article will describe how to input Sparrow
in your own projects. I will do this creating a simple arcade style game. Lets get started; the Sparrow Framework is available for download here: http://www.sparrow-framework.org/download/ or from GitHub here: https://github.com/PrimaryFeather/Sparrow-Framework
There are several different ways to incorporate Sparrow into into your project. This tutorial is going to use the scaffold project that is included with the download. After downloading the project you need to add it to your source trees in XCode. Open XCode, go to Preferences, and select the Sources Trees Tab. Click the add button. The Setting and Display Name needs to be SPARROW_SRC. The path needs to be the full path to Sparrow’s src folder, for example: /Users/Matt/Desktop/iPhone-Projects/Frameworks/sparrow/sparrow/src.

From here find the Sparrow scaffold project, and copy it. From the download the scaffold project can be found at samples->scaffold->src. Just copy the entire src folder and paste where you want your project. Then open the XCode project that is inside that folder. Rename the project going to the targets build settings, find the Product Name field and change the name. 
Build and run the project. If everything is set right you will see a black screen with  red square. 


In the scaffold project the Sparrow Framework is already set up for you. Look over the ApplicationDelegate h and m files to get an idea of how the start up works. When you are building a Sparrow project think of it like you are building a tree. The trunk of the tree is the SPStage object. The branches are subclass of the SPDisplayObject. Sparrow has several prebuilt subclasses to include sprites, images, buttons, text fields, and movies. All of these will branch out from trunk. 
The scaffold project already has the an SPStage subclass set up for you, it is the Game h and m file. Generally your game will only need one stage. For the demo we will add three more classes named Ship, Enemy, and Laser, they will all be SPSprite subclasses. For this game we just make our sprites simple squares. Sparrow has a SPQuad object that will render colored squares with OpenGL. This is most effect method to use if you don’t need detailed sprites. 
Add the following code to the Ship.m file: 
- (id)init {
    self = [super init];
    if (self) {
        SPQuad *quad = [SPQuad quadWithWidth:30.0 height:30.0];
        quad.color = 0xffffff;
        quad.x = -quad.width/2.0;
        quad.y = -quad.height/2.0;
        [self addChild:quad];
        
    }
    return self;
}
- (void)dealloc {
    [super dealloc];
}
This what Sparrow code will look like. We have a basic init method, where we make the square. I’ll go over this code line by line to give you the idea of what is happening. 
SPQuad *quad = [SPQuad quadWithWidth:30.0 height:30.0];
This is a basic factory method for creating a new instance of SPQuad. Just pass a width and height parameters for the size you want.
quad.color = 0xffffff;
This sets the color of the square. Sparrow uses the hexadecimal color reference. 0x000000 will give you white.
quad.x = -quad.width/2.0;
quad.y = -quad.height/2.0;
These two lines set x and y position of the quad relative to its parent. Setting the x and y to the width and height divided by two moves the origin to the center of the quad. This needs to be done for the proper rotating of the object.
[self addChild:quad];
This is the Sparrow equivalent of addSubview: You need to call this in order for the quad to be displayed.
All of Sparrow display objects are created in a similar way. Some of the specialty display objects have unique properties and methods. Check the Sparrow documentation for more information. For our game the Enemy and Laser will be set up in the same way as the Ship was.
Add the following code to Enemy.m:
- (id)init {
    self = [super init];
    if (self) {
        SPQuad *quad = [SPQuad quadWithWidth:30.0 height:30.0];
        quad.color = 0x0000ff;
        quad.x = -quad.width/2.0;
        quad.y = -quad.height/2.0;
        [self addChild:quad];
        
    }
    return self;
}
- (void)dealloc {
    [super dealloc];
}
Add the following code to Laser.m:
- (id)init {
    self = [super init];
    if (self) {
        SPQuad *quad = [SPQuad quadWithWidth:10.0 height:30.0];
        quad.color = 0xff0000;
        quad.x = -quad.width/2.0;
        quad.y = -quad.height/2.0;
        [self addChild:quad];
        
    }
    return self;
}
- (void)dealloc {
    [super dealloc];
}
Now that our sprites are created we can put the game together. Go into the the Game.m file and import the three sprites. Game.m will have an initWithWidth:height: method. Inside that method you will see all the code that created red square seen earlier. Delete this code out and replace it with:
- (id)initWithWidth:(float)width height:(float)height {
    if (self == [super initWithWidth:width height:height]) {
        Ship *ship = [[Ship alloc] init];
        ship.x = 150.0;
        ship.y = 400.0;
        ship.rotation = SP_D2R(45);
        ship.name = @"Ship";
        
        [self addChild:ship];
        [ship release];
        
        Enemy *enemy = [[Enemy alloc] init];
        enemy.x = 150.0;
        enemy.y = 50.0;
        enemy.name = @"Enemy";
        
        [self addChild:enemy];
        [enemy release];
    }
    return self;
}
This is just adding the sprites we created to the game’s stage. You will notice there are a couple of new properties set here. The name property gives us the ability to find the object later on by its name. Also we used the rotation property. OpenGL expects radian values to preform rotations. Sparrow comes with a handy macro to convert degrees to radian values. To rotate the ship 45 degrees we just have use the macro SP_D2R(45). Positive values rotate clockwise and negative values will rotate counterclockwise. If you build and run now you should see this:


Now this game needs some control. We want the enemy to move by itself and the ship to be moved by the user. Sparrow has a built in timer to step your game, so there is no need to create your own. Just add the follow method to the Game.m file:
- (void)advanceTime:(double)seconds {
    [super advanceTime:seconds];
    
}
The amount of times this method is called depends on what the stage’s frame rate is set at. Most games can run at 30 fps. If your game needs some faster speeds the  iOS devices can run at 60 fps. Make sure you call advanceTime: on the super object to keep the rest of the graphics framework running smooth. 
We want the enemy to move back and fourth across the screen and move down getting closer to the ship.  First we need to know which way the enemy is moving when it is going across the screen, to do this add an bool iVar named enemyIsMovingLeft. To move the enemy add the following code into the advanceTime: method. 
 Enemy *enemy = (Enemy *)[self childByName:@"Enemy"];
    
    if (!enemyIsMovingLeft) {
        enemy.x = (enemy.x + 1);
        if (enemy.x == 300) {
            enemy.y = (enemy.y + 20);
            enemyIsMovingLeft = YES;
        }
    }
    else {
        enemy.x = (enemy.x - 1);
        if (enemy.x == 20) {
            enemy.y = (enemy.y + 20);
            enemyIsMovingLeft = NO;
        }
    }
All of Sparrow display objects have an x and y property that can be changed at any time. If you need continuos movement, using logic such as the above in the advanceTime: method is a good way to manage it. The first line of this block is one of the most useful methods of Sparrow. The childByName: method can look up a display object by the name you assign to it. You should use this as often as possible, instead of retaining the object in an iVar.
The next thing we need to do is provide a way for the user to move the ship and shoot at the enemy. We will make the ship move by the user dragging it to the left or right and shoot a laser with by double tapping the ship. Sparrow has its own touch recognition built in. Define the method -(void)onTouch:(SPTouchEvent *)touch for the Ship class, and add the following line to the init method.
[self addEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
This adds the Ship object to Sparrow’s event dispatching. There are several events that Sparrow will dispatch itself, or you can create your own events. When you add an event listener you are responsible for removing it as well. Add the follow line to the dealloc method to remove the event listener.
[self removeEventListener:@selector(onTouch:) atObject:self forType:SP_EVENT_TYPE_TOUCH];
Now that the Ship can receive touches we just have to code them in. In Sparrow all the touches are handled by one method, in this case it is the onTouch: method we defined. In that method the touch phase (began, moved, or ended) will be defined.
The onTouch: method should look like this:
- (void)onTouch:(SPTouchEvent *)touch {
    SPTouch *begin = [[touch touchesWithTarget:self andPhase:SPTouchPhaseBegan] anyObject];
    SPTouch *moved = [[touch touchesWithTarget:self andPhase:SPTouchPhaseMoved] anyObject];
    
    if (begin) {
        if ([begin tapCount] == 2) {
            SPEvent *shoot = [SPEvent eventWithType:@"ShootEvent" bubbles:YES];
            [self dispatchEvent:shoot];
        }
    }
    
    if (moved) {
        if ([moved locationInSpace:self.parent].x > 30 && [moved locationInSpace:self.parent].x < 300) {
            self.x = [moved locationInSpace:self.parent].x;
        }
    }
}
There are a few things happening here. First we define the two touch phases we need by calling the touchesWithTarget:andPhase: methods. This method returns an NSSet of SPTouch objects. Next is the code for when a touch begins. First we check and see if there has been a double tap, this is done by calling tapCount on the begin instance of SPTouch. If there was a double tap a custom SPEvent is created and dispatched. 
When creating a Sparrow event you have the option to make it bubble. To visualize a bubbling event, think of the display tree discussed earlier.  When a bubbling event is dispatched will work its way from its own branch of the tree back to the trunk. Any branch along the way can preform process with the event and it will continue to move unit it reaches the trunk or it is stopped. 
The last thing we do is process when a touch is moved. We just want the ship to move to the left and right along the bottom of the screen.  Since the Ship object needs to move around on the stage, we need to the location of the touch in the stage. so we use the locationInSpace: method. This method gives the touche’s location inside of the given display object. In this case that object is the parent so we pass self.parent to the method. To move the Ship first make sure the touch is within a specific area so the ship cannot be moved of the screen. Next just set the ship’s x property to the x location of the touch. 
All of our touches are set, the next thing to do is handle the shoot event that we created. We will handle this event with the game’s stage. First thing is to go define a method -(void)onShoot:(SPEvent *)event in the Game class. All though the shoot event is a bubbling event, we still need to add an event listener in the Game.m initWithWidth:height: method. Do not forget to remove it.
[self addEventListener:@selector(onShoot:) atObject:self forType:@"ShootEvent"];
  The Laser needs to start at the ship and quickly go up the screen. We will use an instance of SPPoint to find out where the ship is at, and SPTween to move the laser up the screen. The SPTween class powers Sparrow’s animations. Animations should be used when moving the object with advanceTime: method is impractical. When it is done your onShoot: method will look like this:
- (void)onShoot:(SPEvent *)event {
    Ship *ship = (Ship *)[self childByName:@"Ship"];
    
    SPPoint *lPoint = [SPPoint pointWithX:ship.x y:(ship.y - 30.0)];
    
    Laser *laser = [[Laser alloc] init];
    laser.x = lPoint.x;
    laser.y = lPoint.y;
    laser.name = @"Laser";
    
    [self addChild:laser atIndex:0];
    
    SPTween *tween = [SPTween tweenWithTarget:laser time:0.25];
    [tween animateProperty:@"y" targetValue:-30.0];
    [self.juggler addObject:tween];
    
    [self performSelector:@selector(removeChild:) withObject:[self childByName:@"Laser"] afterDelay:tween.time];
    
    [laser release];
}
First we have to find the where the ship is on the screen. To do that we access the ship using the childByName: method. After that we create an instance of SPPoint using the ship’s x and y coordinates. I have went ahead and subtracted 30 from the ship’s y coordinate to make laser appear out the ship instead of on top of it. After that we create an instance of the Laser sprite and set the x and y properties using the point from above.  This time we use addChild:atIndex: method to add the laser. Passing 0 to this method will put the laser sprite behind all the other sprites on the screen. 
To move the laser, we use an instance of SPTween. The SPTween was created using the tweenWithTarget:time: factory method. The target is the display object that you want to animate and the time is how long the animation should take. To configure the animation we use animateProperty:targetValue: method. With Sparrow any display object property that is a number can be animated. Properties are passed by giving an NSString representation of the property. The targetValue it where you want the object to be when the animation is done. In order for the tween to work it has to be added to a juggler. Every instance of SPStage has its own juggler built in, so we just used it. SPTween dispatches several events to monitor its progress, however since all we need to do with the laser is remove it when the animation is done. It is easiest to do this using NSObjects performSelector:withObject:afterDelay: method, using the tween’s time for the delay.
  The last thing needed is some collision detection. Sparrow does have basic collision detection built in. If you need advanced collision detection I would recommend adding a physics engine to your game.  For this example it is easiest to handle the collisions in the stage. Define a testCollisions method and call it from the advanceTime: method. This way the game will test for collisions on every step. We only need to see the if the laser hits the enemy. The testCollisions method will look like this:
- (void)testCollisions {
    SPRectangle *enemy = [[self childByName:@"Enemy"] boundsInSpace:self];
    SPRectangle *laser = [[self childByName:@"Laser"] boundsInSpace:self];
    
    if (laser) {
        if ([laser intersectsRectangle:enemy]) {
            SPTween *tween = [SPTween tweenWithTarget:[self childByName:@"Enemy"] time:0.5];
            [tween animateProperty:@"rotation" targetValue:([self childByName:@"Enemy"].rotation + SP_D2R(360))];
            [self.juggler addObject:tween];
        }
    }
}
Basic collision detection is fairly simple with Sparrow. What happened here is we created a bounding box for the enemy and the laser using instances of SPRectangle. With SPRectangle we can find out where each object is using the boundsInSpace: method. In this case the space is our stage. After we know where the objects are we check to see if they are touching each other. SPRectangle instances can check if they are touching another instance with the intersectsRectangle: method. This method returns a bool value. If the laser and enemy are in the same space we just give the enemy a little animation to show that it was hit. 
There is no menus, points, and the graphics are not superb, but as you can see with the Sparrow Framework you can have functioning game play in less than hour. If you want to explore Sparrow more, visit their web site at www.sparrow-framework.org. The web site has lots of tutorials on the finer points of Sparrow, its own forum, full documentation, and recently added a wiki where you can find several extensions from other developers.
Note: This article was written for Sparrow v1.1, however Sparrow v1.2 was released while I was writing. Make sure you check the release notes for changes if you download v1.2.

Follow me on Twitter @matt62king
This projects is available at https://github.com/matt62king/SparrowDemo

2 comments:

  1. Thank you for the good tutorial - however it seems that instead of

    - (void)advanceTime:(double)seconds {
    [super advanceTime:seconds];

    }

    I need to use onEnterFrame.

    [self addEventListener:@selector(onEnterFrame:)
    atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];

    ReplyDelete