TweetFollow Us on Twitter

Game Development for iPad, iPhone and iPod Touch Using the cocos2d and Chipmunk Frameworks

Volume Number: 26
Issue Number: 03
Column Tag: Game Development for iPad, iPhone and iPod Touch

Game Development for iPad, iPhone and iPod Touch Using the cocos2d and Chipmunk Frameworks

Tools for building 2D games

by Rich Warren

Let's Get These Engines Running!

Last time, we looked at the cocos2d for iPhone graphics framework, and the Chipmunk Physics Engine. If you followed the instructions from Part 1, you have already installed both libraries and played around with the examples. If not, please check out Part 1, because we're going to hit the ground running.

In this article, we will build a simple pachinko game. When the user taps the screen, a ball will fall from the top, and bounce off a number of pins. If it goes into the target, a bell will ring. Otherwise, the ball simply exits off the bottom of the screen. We will try to keep this simple, and focused on the graphics and physics engines. There's no score. No ability to affect the ball once it is launched, and only very simple sound effects. Still, by the time we're done you should have a good handle on how to use cocos2d and Chipmunk in your own projects. So, without further introduction, let's jump right into the code.

Building a New 2D Game App

Launch Xcode, and create a new cocos2D Chipmunk application: File--> New Project...--> iPhone OS--> Application--> cocos2d-0.8.2 Chipmunk Application. Name the project Pachinko, and click the Save button. As I mentioned in Part 1, the current release of cocos2d defaults to the iPhone 2.2 SDK. Unfortunately, this version is not included in recent versions of Xcode. The simplest solution is to change the Active SDK in the Overview drop-down menu, and select an existing SDK. Usually, I select the most recent simulator release (as of writing, 3.1.3).


Create a New Cocos2D Chipmunk Application

Next, any game needs graphics and sound effects. Open up the Resources group. As you can see, the template already has four PNG files. Default.png is the splash screen that's displayed while the application launches. Icon.png is the application icon that appears on the iPhone's home screen. For simplicity's sake, we will leave these alone; however, you will want to replace them in your own projects. The fps_images.png image is used to display the frame rate while testing the application. We can use that feature as a quick and dirty performance test, so leave that image alone. However, the grossini_dance_atlas.png image is only used by the template's sample application. You can safely delete that file.

Creating artwork and sound effects is beyond the scope of this article. Instead, you should download the article's source code from ftp://ftp.mactech.com/src/mactech/volume26_2010/Warren-Pachinko_iPhone_Source.zip. Now copy the following files to the resource folder: bell.wav, tick1.wav, tick2.wav, right_bumper.png, left_bumper.png, back-ground.png, ball.png, pin.png and target.png. To add these files, right-click on the Resource group, then click Add Existing Files.... Select the desired files, and then click the Add button.

Wrapping Up our Entities

Our pachinko game will have a number of balls that can interact with other things on the screen: other balls, pins, bumpers and the target. As described in Part 1, a single game entity is a combination of graphical elements and physical attributes. These entities are represented by a number of cocos2d objects and Chipmunk structures. All of these need to be created, initialized and (when the time is right) destroyed properly. That's a lot of code just to get something on the screen. Fortunately, most of this code is identical from entity to entity. We'll take advantage of this by wrapping all the objects and structures in our own class, Entity. This class will encapsulate all the common creation and destruction code, properly handling the memory management for the underlying objects and structures.

A classic Object Oriented approach would involve encapsulating the common elements in a base class, and then create a number of subclasses to represent each of the different entity types. While that would work here, I've chosen a slightly different approach. We will use the Entity class for all our entities, and encapsulate the differences between the various types using a configuration source object. To do this, we first create the EntityConfigurationSource protocol. This protocol is similar to the DataSource protocols used throughout in the Cocoa Framework. If you've ever used a UITableViewDataSource to fill the contents of a table, you have the basic idea. There is one small, technical difference. Typically, to avoid reference loops, a Cocoa class does not retain its data source. However, following that convention would only add unnecessary complications to our code. Therefore, to avoid any confusion, we will call our protocol a ConfigSource, not a DataSource.

EntityConfigSource

Let's build the EntityConfigSource. Right click on the Classes group and then select Add New Group. Create a new subgroup named Entities. Now, right click on the Entities group and select Add New File... iPhone OS Cocoa Touch Class Objective-C Protocol. Name this protocol EntityConfigSource.h, and click the Finish button. Now modify the file as shown below:

EntityConfigSource.h

This protocol encapsulates the differences between different types of Entities.

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "chipmunk.h"
#import "Utility.h"
@protocol EntityConfigSource <NSObject>
-(enum SpriteType) spriteType;
-(int) depth;
-(BOOL) isStatic;
-(cpBody*) createBody;
-(NSArray*) createShapesGivenBody:(cpBody*)body;
@optional
-(CGPoint) initialLocation;
-(CGFloat) initialSpriteRotationForLocation:(CGPoint)location;
-(CGFloat) updatedSpriteRotationForLocation:(CGPoint)location;
@end

This protocol contains a mixture of required and optional methods. spriteType returns a value that can be used to look up the appropriate image file name. depth determines the image's z-depth (higher numbers are drawn overtop lower numbers). isStatic returns YES if the entity is static (non-moving), or NO if it is mobile. createBody creates the entity's cpBody structure, and createShapesGivenBody builds an array of shapes associated with that body. As described in Part 1, the body defines the entity's mass and moment of inertia, while the shapes define how the entity interacts with other entities (typically by defining its surface geometry and characteristics that determine the effects of collisions).

The optional methods can be used to set the object's initial location and its initial rotation. Finally, the updateSpriteRotationForLocation: method is used to set the sprite's rotation based on its current location. We will use this to create fake 3D lighting effects later on.

Note: this class imports the Utility.h file. This defines the enumerations and a number of short helper functions used throughout this project. For the sake of saving time and space, I will not spend much time on those functions here. Simply copy Utility.h and Utility.m from the article's source code.

Entity Class

OK, let's look at the Entity class. Create a new Objective-C class in the Entities subgroup (right click Entities and select Add New File... iPhone OS Cocoa Touch Class Objective-C Class), and name it Entity. Now open Entity.h and modify it as shown below:

Entity.h

#import <Foundation/Foundation.h>
#import "EntityConfigSource.h"
@interface Entity : NSObject {
    
    id <EntityConfigSource> configSource;
    // cocos2D Graphical Features
    Layer* layer;
    Sprite* sprite;
    
    // Chipmunk Physics Features
    cpSpace* space;
    cpBody* body;
    NSArray* shapes;
}
@property (readonly, nonatomic) CGPoint position;
@property (readonly, nonatomic) NSArray* shapes;
// Init
 -(id)initInSpace:(cpSpace*)theSpace 
        forLayer:(Layer*)theLayer 
      atLocation:(CGPoint)location 
 withConfigSource:(id <EntityConfigSource>)theConfigSource;
// Update
-(void) updateSprite;
// Convenience methods for creating different Entities
+(id) pinInSpace:(cpSpace*)space 
        forLayer:(Layer*)layer 
      atLocation:(CGPoint)location;
+(id) leftBumperInSpace:(cpSpace*)space 
               forLayer:(Layer*)layer;
+(id) rightBumperInSpace:(cpSpace*)space 
                forLayer:(Layer*)layer;
+(id) targetInSpace:(cpSpace*)space 
           forLayer:(Layer*)layer 
         atLocation:(CGPoint)location;
 
+(id) ballInSpace:(cpSpace*)space 
         forLayer:(Layer*)layer 
       atLocation:(CGPoint)location;
@end

We'll look at the methods in more detail below. For now, focus on the instance variables. Notice how the Entity's appearance is described using a Sprite placed within a Layer, while the physical attributes are defined using a cpBody and one or more cpShapes placed within a cpSpace.

Note: cpShape is a C structure, not an Objective-C Object. As a result, we cannot place it directly into an NSArray. There are a few possible solutions to this problem. In this case, I chose to create a simple object called Shape that wraps the cpShape structure. We then place the Shape object into the NSArray. I'll leave the actual implementation of Shape as a homework assignment (or just grab a copy from the source code).

So far, everything looks simple enough. Lets move on to the implementation. We'll go slow and take this in bite-size chunks.

Defining an extension for Entity

#import "Entity.h"
#import "PinConfigSource.h"
#import "LeftBumperConfigSource.h"
#import "RightBumperConfigSource.h"
#import "TargetConfigSource.h"
#import "BallConfigSource.h"
#import "Shape.h"
@interface Entity()
-(void)setConfigSource:(id<EntityConfigSource>)theConfigSource;
 -(void) setPosition:(CGPoint)thePosition;
    -(void) setSpace:(cpSpace*)theSpace;
    -(void) setLayer:(Layer*)theLayer;
@end

We start by creating an extension on Entity that declares a number of private methods. We will use these methods to initialize our Entitiy instances. We will look at the actual definitions in just a moment.

init and dealloc

@implementation Entity
@synthesize shapes;
-(id)initInSpace:(cpSpace*)theSpace 
        forLayer:(Layer*)theLayer 
      atLocation:(CGPoint)location 
withConfigSource:(id <EntityConfigSource>)theConfigSource {
    
    if( (self=[super init])) {
        
        // these methods must be called in this order.
        [self setConfigSource: theConfigSource];
        [self setPosition:location];    
        [self setSpace: theSpace];
        [self setLayer: theLayer];   
        
    }
    
    return self;
 }
-(id) init {
    
    [NSException raise:NSInternalInconsistencyException 
                format:@"Cannot initialize using init, use %@ instead",
    NSStringFromSelector(
        @selector(initInSpace:forLayer:atLocation:withConfigSource:))];
    
    // we will never get here
    return nil;    
}
// remember: do not release during chipmunk callbacks (e.g. collisions).
-(void) dealloc {
    
    [layer removeChild:sprite cleanup:NO];
    [layer release];
    
    [sprite release];
    
    for (Shape* shape in shapes) {
        cpSpaceRemoveShape(space, shape.pointer);
    }
        
    [shapes release];
    
    if (![configSource isStatic]) {
        cpSpaceRemoveBody(space, body);
    }
        
    cpBodyFree(body);
    
    [configSource release];
    [super dealloc];
}

Next we synthesize the reader method for our shapes property. Notice, we do not synthesize the position property. We will provide a custom implementation instead. While synthesizing position won't cause an error, I prefer to use @synthesize only on properties actually managed by the compiler.

Next we define the initialization and deallocation methods. initInSpace:forLayer:atLocation:with-ConfigSource: is our default initialization method. It simply calls the private methods defined in our extension. Additionally, we want to prevent people from calling NSObject's init method by mistake, so our implementation overrides init and throws an exception. Notice that we dynamically generate the string for our method name, rather than hard coding it in our exception. This helps ensure that our message will still be relevant, even if we refactor our initialization methods.

The dealloc method is somewhat more complex. We remove our entity from the Layer and the cpSpace, then release or free all the components. Notice that we must handle the memory management of Chipmunk structures differently than standard Objective-C objects. Instead of calling retain and release, we must use Chipmunk's functions to allocate and free those structures.

Correctly managing both the Objective-C objects and the C structures is one of the more complicated aspects of using Chipmunk on the iPhone. Fortunately, our Entity class will hide all this complexity from us.

There is one small wrinkle with this code. We cannot release any Entities during a Chipmunk callback (e.g. when processing collisions). I believe this restriction may be removed in later versions of Chipmunk. Still, it is not hard to work around. Either make a list of objects to release, and then release them all once the callbacks have finished, or use performSelector:withObject:afterDelay: to schedule the release. Even setting the delay value to 0 will cause the method to be delayed until the run loop is empty, and you've moved safely outside the callback code.

Now lets look at the implementation of our private methods. Again, these do the bulk of the work involved in setting up our Entities. Notice that these must be called in the correct order. First set the config source. Then set the position. Finally set the space and the layer.

setConfigSource:

-(void) setConfigSource:(id<EntityConfigSource>)theConfigSource {
    
    // store the config source
    configSource = theConfigSource;
    [configSource retain];
    
    // and setup the sprite FILE_NAMES[[delegate spriteType]]
    sprite = [Sprite spriteWithFile: 
                 FILE_NAMES[[configSource spriteType]]];
    [sprite retain];
}

This method stores and retains the config source. Then it queries the source for the sprite type and uses that to determine the correct file name. It then initializes and retains the sprite. Note: FILE_NAMES is an array of NSStrings defined in Utility.h.

setPosition

-(void) setPosition:(CGPoint)thePosition {
    
    sprite.position = thePosition;
    
    if ([configSource respondsToSelector:
             @selector(initialSpriteRotationForLocation:)]) {
        
         sprite.rotation = [configSource 
             initialSpriteRotationForLocation:thePosition];
    }
    
}

The setPosition: method sets the sprite's position. Then we check to see if our config source has implemented the optional initialSpriteRotationForLocation: method. If it has, we call that method and use the result to set our rotation.

setSpace:

-(void) setSpace:(cpSpace*)theSpace {
    
    space = theSpace;
    
    body = [configSource createBody];
    body->p = sprite.position;
    
    if (![configSource isStatic]) {
        cpSpaceAddBody(space, body);
    }
    
    shapes = [configSource createShapesGivenBody:body];
    [shapes retain];
    
    for (Shape* shape in shapes) {
        
        // save the InteractiveObject in the cpShape's data field.
        [shape setData: self];
        
        if ([configSource isStatic]) {
            
            // only add the static shape
            cpSpaceAddStaticShape(space, shape.pointer);
        }
        else {
            
            // add the non-static shape
            cpSpaceAddShape(space, shape.pointer);
            
        }
    }
}

This method is a bit more complex than the rest. As mentioned previously, the cpSpace structure holds references to a number of bodies and shapes. When we step the space forward in time, Chipmunk will calculate the new position and rotation for all the bodies, as well as checking for collisions among any shapes within that space. This method creates the body and the shapes needed for our Entity and add them to the given space.

First we store a reference to the cpSpace structure. Then we ask our config source to build our cpBody structure. We then set the cpBody's position equal to the Sprite's position. As mentioned in Part 1, both the cpBody and Sprite store the entity's position, and we need to keep them in sync. Finally, we query the config source to see if this entity is static (non-moving). If it is not static, we add the body to the space.

Note: Do not add the bodies of static objects to the cpSpace structure. Any bodies in the space will have the force of gravity applied to them each time we iterate the space. While the static objects won't move, the accumulated force will cause very odd bugs during collisions.

Next, we query the config source for a list of shapes associated with our entity. We store and retain the list of Shapes. Remember, we are wrapping the cpShape structure inside a simple Objective-C class, named Shape. The cpShape structure also has a data field that can be used to store pointers to arbitrary user data. In our case, we will store a reference to this Entity. That will allow us to find the correct Entity for a given cpShape.

Iterate over all the Shapes and call setData: to set the cpSpace->data field (see the source code for implementation details). Then add the shape to the space. Note: if the shape is a static shape, you will add it using the cpSpaceAddStaticShape() function. Otherwise add it using cpSpaceAddShape().

-(void) setLayer:(Layer*)theLayer {
    
    layer = [theLayer retain];
    [theLayer addChild:sprite z:[configSource depth]];
    
}

Finally, we add the Layer. First, we store a reference to the Layer and retain it. We then add the Sprite to the Layer. In many ways, the Layer is to cocos2D objects what the cpSpace is to Chipmunk structures.

Also note, we query our config source for our entity's z-value. Objects with a higher z-value will be drawn over objects with a lower z-value. In Utility.h we define an enumeration for all the z-values in ascending order for the background, bumpers, ball, target and pins. The background is drawn on the bottom, and the pins are drawn on the top.

Careful attention to the z-values is important for making the game look right. For example, we have designed the target's artwork so that when the ball goes into the target, it disappears behind the target. Getting the effect we want requires coordination between the art design, the z-values and the target's shape.

Update

-(void) updateSprite {
    
    if (![configSource isStatic]) {
        
        sprite.position = body->p;
        
        if ([configSource
                respondsToSelector:@selector(updatedSpriteRotation)]) {
            
            sprite.rotation = [configSource 
                               updatedSpriteRotationForLocation:body->p];
        }
    }
}

The next method is used to update our sprite position and rotation. As mentioned previously, the Chipmunk physics engine will automatically update the position of our entitiy's cpBody every time we iterate the space. We then need to call this method to keep our sprite's position in sync with the cpBody.

First, we check to make sure our object is not static. Static objects will not move, so their Sprite positions never needs updating. Then we simply copy the cpBody's position over to the Sprite.

Typically, you would want to copy the cpBody's rotation as well, but we're going to do something a little different. We only have one type of moving object, the balls, and they are round, reflective, and largely featureless. Rotating them does not make a lot of sense. However, we have placed a highlight at the top of the ball image. If we assume this is the reflection from a single light source somewhere off the top of the screen, then we should to rotate the ball so that the highlight points towards this light source. This allows us to fake 3D lighting effects within our 2D game.

First, we check to see if the config source implements the optional upatedSpriteRotation method. If it does, we set the Sprite's rotation by calling this method. If not, we do not change the rotation at all (though, we could set it equal to the body's rotation if we had other moving objects where rotating the image made sense).

Next we implement the reader for our position property. This simply returns the body's position.

Entity Accessor Methods

-(CGPoint) position {
    return body->p;
}

Now we start building convenience functions. Remember, the goal of building the Entity class was to hide as much of the complexity as possible. By creating a convenience method for each Entity type, we further simplify the interface. Our code only needs to access the Entity class itself. It does not need to know about the different EntityConfigSources.

Below are the convenience methods for the ball and the left bumper. I leave the rest as homework.

Entity Public Convenience Methods

+(id) ballInSpace:(cpSpace*)space 
         forLayer:(Layer*)layer 
       atLocation:(CGPoint)location {
    
    id <EntityConfigSource> configSource = 
        [[[BallConfigSource alloc] init] autorelease];
    
    return [[[Entity alloc] initInSpace:space 
                               forLayer:layer 
                             atLocation:location
                       withConfigSource:configSource] autorelease];
}
+(id) leftBumperInSpace:(cpSpace*)space 
               forLayer:(Layer*)layer  {
    
    id <EntityConfigSource> configSource = 
        [[[LeftBumperConfigSource alloc] init] autorelease];
    
    return [[[Entity alloc] initInSpace:space 
                               forLayer:layer 
                             atLocation:[configSource initialLocation]
                       withConfigSource:configSource] autorelease];
}

These methods simply create the correct config source, and then instantiate the Entity. Note, that ballInSpace:forLayer:atLocation: takes a location parameter, which is used to set the ball's initial location. leftBumperInSpace:forLayer: does not. The location is set by the config source's initialLocation method.

There is a design issue here worth discussing. This is not a truly general approach to creating cocos2d/Chipmunk objects. It works well for this particular game, but it would be easy to come up with situations where this approach is simply not appropriate. Partially, I did that to keep the Entity code relatively simple for the purpose of this tutorial. Writing truly general code is both complicated and hard. But, part of it is also just good software engineering. You shouldn't over-engineer your classes by adding a lot of features that you may never use. Instead, add new features as and when they are needed, refactoring your existing code as you go.

Config Sources

Of course, we're not quite done yet. We still need our config sources. I'll show you two. One for the ball and one for the target. Again, I leave the rest as homework.

BallConfigSource.m

#import "BallConfigSource.h"
#import "Shape.h"
@implementation BallConfigSource
-(id) init {
   
   if ((self = [super init])) {
      
      CGSize size = getSpriteSize(BALL);
      radius = size.width / 2.0f;
      
   }
   
   return self;
}
-(enum SpriteType) spriteType {
   return BALL;
}
-(int) depth {
   return BALL_DEPTH;
}
 
-(BOOL) isStatic {
   return NO;
}
-(cpBody*) createBody {
   
   return cpBodyNew(10, cpMomentForCircle(10, 0.0, radius, cpv(0,0)));
}
-(NSArray*) createShapesGivenBody:(cpBody*)body {
   
   cpShape* shape = cpCircleShapeNew(body, radius, cpv(0.0, 0.0));
   
   shape->e = 0.75f;
   shape->u = 0.5f;
   shape->collision_type = BALL_COLLISION;
   
   return [NSArray arrayWithObject:[Shape shapeFromPointer:shape]];
}
-(CGFloat) initialSpriteRotationForLocation:(CGPoint)location {
   return getLightRotation(location);
}
-(CGFloat) updatedSpriteRotationForLocation:(CGPoint)location {
   return getLightRotation(location);
}
@end

Most of this should be relatively straightforward. The init method simply calculates the ball's radius based on the sprite size. Both the BALL enum and the getSpriteSize() method are defined in Utility.h.

The createBody method creates a cpBody structure with a mass of 10.0 and a moment of inertia based on Chipmunk's cpMomentForCircle() function. The CreateShapesGivenBody: method creates a single circular shape, and then sets the elasticity to 0.75 and the friction to 0.5. It then sets the collision type. We will use the collision types later to correctly dispatch different collisions.

Finally, as mentioned earlier, the balls should be rotated so that their highlight points towards our imaginary light source. The initialSpriteRotationForLocation: and updatedSpriteRotationForLocation: methods handle this by calling the getLightRotation() function, also from Utility.h.

TargetConfigSource.m

#import "TargetConfigSource.h"
#import "Shape.h"
Shape* buildShape(cpBody* body, cpVect start, cpVect end) {
    
    cpShape* shape = cpSegmentShapeNew(body, start, end, 0.0f);
    
    shape->e = 0.5f;
    shape->u = 0.5F;
    shape->collision_type = TARGET_WALL_COLLISION;
    
    return [Shape shapeFromPointer:shape];
} 
@implementation TargetConfigSource
-(enum SpriteType) spriteType {
    return TARGET;
}
-(int) depth {
    return TARGET_DEPTH;
}
-(BOOL) isStatic {
    return YES;
}
-(cpBody*) createBody {
    return cpBodyNew(INFINITY, INFINITY);
}
-(NSArray*) createShapesGivenBody:(cpBody*)body {
    
    CGSize size = getSpriteSize(TARGET);
    CGFloat halfWidth = size.width / 2.0f;
    
    cpVect upperLeft  = cpv(-halfWidth, halfWidth);
    cpVect lowerLeft  = cpv(11.0f - halfWidth, -halfWidth);
    cpVect lowerRight = cpv(halfWidth - 11.0f, -halfWidth);
    cpVect upperRight = cpv(halfWidth, halfWidth);
    
    // These ball will bounce off these.
    Shape* left = buildShape(body, upperLeft, lowerLeft);
    Shape* bottom = buildShape(body, lowerLeft, lowerRight);
    Shape* right = buildShape(body, lowerRight, upperRight);
    
    // But this shape will trigger the target.
    Shape* goal = buildShape(body, 
                             cpv(15.0f - halfWidth, 4.0f - halfWidth), 
                             cpv(halfWidth - 15.0f, 4.0f - halfWidth));
    
    // Need to change the collision type
    goal.pointer->collision_type = TARGET_COLLISION;
    
    return [NSArray arrayWithObjects:left, bottom, right, goal, nil];
}
@end

The left bumper is a little more complex, but not by much. Since this is a static object, we create a cpBody with an infinite mass and moment of inertia. We then define a complex shape. This starts with three lines (left, bottom and right) that define an open cup shape. We then define a fourth goal line at the bottom of the cup. Notice, fast-moving balls might penetrate the target's wall by a few pixels before a collision is detected. To prevent the ball from accidentally triggering the goal, we create a 4-pixel padding between it and the cup. Also notice that the coordinates for the shapes are all relative to the cpBody's position (or the center of the Sprite's icon). The target's walls are given a TARGET_WALL_COLLISION, while the goal is given a TARGET_COLLISION. This will allow us to process the collisions separately. When a ball hits the wall, it will just bounce off. But, when it hits the goal, we will ring a bell and delete the ball. We also gave the target's shapes a relatively low elasticity. When a ball goes into the cup, we don't want it to just bounce out again.

Believe it or not, most of the heavy work is now done. We just need to make our main layer, and then link everything together in our app delegate.

Fake It Till You Make It

The actual implementation of the Main Layer will have to wait until the next installation. Still, we want to see our Entities in action. So, lets make a few quick modifications to the existing HelloWorldScene.m file.

First, import our Entity class at the top of the file. Then modify the eachShape() function as shown below. This simply extracts the Entity from the shape data and calls our updateSprite function.

eachShape()

static void
eachShape(void *ptr, void* unused)
{
    cpShape *shape = (cpShape*) ptr;
    Entity *entity = shape->data;
    
    [entity updateSprite];
}

Now delete the following lines from the init method.

Init

AtlasSpriteManager *mgr = [AtlasSpriteManager 
    spriteManagerWithFile:@"grossini_dance_atlas.png" capacity:100];
[self addChild:mgr z:0 tag:kTagAtlasSpriteManager];

Now modify the addNewSpriteX:y: method as shown below.

addNewSpriteX:y:

-(void) addNewSpriteX: (float)x y:(float)y
{
    
   double offx = (CCRANDOM_0_1() * 2.0 - 1.0);
   double offy = (CCRANDOM_0_1() * 2.0 - 1.0);
    [[Entity ballInSpace:space 
                forLayer:self 
              atLocation:ccp(x+offx, y + offy)] retain];
}

Here, we simply create a ball at the given position. We add a slight random nudge to the position, to avoid adding multiple balls in the exact same location. The collision detection system doesn't work properly if their positions are exactly the same.

Notice how much simpler addNewSpriteX:y: has become. Our Entity class hides much of the complexity that the previous implementation had to manage manually. Of course, we're creating a memory leak here by retaining and never releasing all these Entity objects. But, that's OK for a quick test.

Compile and run the application. It should add a ball whenever you tap the screen.


Sample Ball Entities

Once everything works properly, go ahead and start playing around. Look through the HelloWorldScene.m file. Try changing the gravity vector. Add different Entity types. Play around with the code and get a feel for how things work. Don't worry about messing up the HelloWorldScene class. We won't be using it at all in Part 3. Instead, we will write our own layer, MainLayer, and use that to wrap up our Pachinko game.

Most importantly, until next time, have fun. This is supposed to be a game.


Rich Warren lives in Honolulu, Hawaii with his wife, Mika, daughter, Haruko, and his son, Kai. He is a software engineer, freelance writer and part time graduate student. When not playing on the beach, he is probably writing, coding or doing research on his MacBook Pro. You can reach Rich at rikiwarren@mac.com, check out his blog at http://freelancemadscience.blogspot.com/ or follow him at http://twitter.com/rikiwarren.

 
AAPL
$471.35
Apple Inc.
+3.99
MSFT
$32.41
Microsoft Corpora
-0.47
GOOG
$877.86
Google Inc.
-7.65

MacTech Search:
Community Search:

Software Updates via MacUpdate

VueScan 9.2.23 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Acorn 4.1 - Bitmap image editor. (Demo)
Acorn is a new image editor built with one goal in mind - simplicity. Fast, easy, and fluid, Acorn provides the options you'll need without any overhead. Acorn feels right, and won't drain your bank... Read more
Mellel 3.2.3 - Powerful word processor w...
Mellel is the leading word processor for OS X, and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
Iridient Developer 2.2 - Powerful image...
Iridient Developer (was RAW Developer) is a powerful image conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more
Delicious Library 3.1.2 - Import, browse...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Epson Printer Drivers for OS X 2.15 - Fo...
Epson Printer Drivers includes the latest printing and scanning software for OS X 10.6, 10.7, and 10.8. Click here for a list of supported Epson printers and scanners.OS X 10.6 or laterDownload Now Read more
Freeway Pro 6.1.0 - Drag-and-drop Web de...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With it's user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Transmission 2.82 - Popular BitTorrent c...
Transmission is a fast, easy and free multi-platform BitTorrent client. Transmission sets initial preferences so things "Just Work", while advanced features like watch directories, bad peer blocking... Read more
Google Earth Web Plug-in 7.1.1.1888 - Em...
Google Earth Plug-in and its JavaScript API let you embed Google Earth, a true 3D digital globe, into your Web pages. Using the API you can draw markers and lines, drape images over the terrain, add... Read more
Google Earth 7.1.1.1888 - View and contr...
Google Earth gives you a wealth of imagery and geographic information. Explore destinations like Maui and Paris, or browse content from Wikipedia, National Geographic, and more. Google Earth... Read more

Meet Daniel Singer, the Thirteen-Year-Ol...
Ever had the idea for an app, but felt like the lack of programming and design ability was a bit of a non-starter? Well, 13-year-old Daniel Singer has made an app. He’s the designer of Backdoor, a chat app that lets users chat with their friends... | Read more »
Flashout 2 Gets Revealed, Offers Up An E...
Flashout 2 Gets Revealed, Offers Up An Enhanced Career Mode and Exciting New Circuits Posted by Andrew Stevens on August 13th, 2013 [ permalink ] | Read more »
Mickey Mouse Clubhouse Paint and Play HD...
Mickey Mouse Clubhouse Paint and Play HD Review By Amy Solomon on August 13th, 2013 Our Rating: :: 3-D FUNiPad Only App - Designed for the iPad Color in areas of the Mickey Mouse Clubhouse with a variety of art supplies for fun 3-... | Read more »
Strategy & Tactics: World War II Upd...
Strategy & Tactics: World War II Update Adds Two New Scenarios Posted by Andrew Stevens on August 12th, 2013 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Expenses Planner Review
Expenses Planner Review By Angela LaFollette on August 12th, 2013 Our Rating: :: PLAIN AND SIMPLEUniversal App - Designed for iPhone and iPad Expenses Planner keeps track of future bills through due date reminders, and it also... | Read more »
Kinesis: Strategy in Motion Brings An Ad...
Kinesis: Strategy in Motion Brings An Adaptation Of The Classic Strategic Board Game To iOS Posted by Andrew Stevens on August 12th, 2013 [ | Read more »
Z-Man Games Creates New Studio, Will Bri...
Z-Man Games Creates New Studio, Will Bring A Digital Version of Pandemic! | Read more »
Minutely Review
Minutely Review By Jennifer Allen on August 12th, 2013 Our Rating: :: CROWDSOURCING WEATHERiPhone App - Designed for the iPhone, compatible with the iPad Work together to track proper weather conditions no matter what area of the... | Read more »
10tons Discuss Publishing Fantasy Hack n...
Recently announced, Trouserheart looks like quite the quirky, DeathSpank-style fantasy action game. Notably, it’s a game that is being published by established Finnish games studio, 10tons and developed by similarly established and Finnish firm,... | Read more »
Boat Watch Lets You Track Ships From Por...
Boat Watch Lets You Track Ships From Port To Port Posted by Andrew Stevens on August 12th, 2013 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

Snag an 11-inch MacBook Air for as low as $74...
 The Apple Store has Apple Certified Refurbished 2012 11″ MacBook Airs available starting at $749. An Apple one-year warranty is included with each model, and shipping is free: - 11″ 1.7GHz/64GB... Read more
15″ 2.3GHz MacBook Pro (refurbished) availabl...
 The Apple Store has Apple Certified Refurbished 15″ 2.3GHz MacBook Pros available for $1449 or $350 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. Read more
13″ 2.5GHz MacBook Pro on sale for $150 off M...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $1049.95 including free shipping. Their price is $150 off MSRP plus NY sales tax only. B&H will include free copies of Parallels Desktop... Read more
iPod touch (refurbished) available for up to...
The Apple Store is now offering a full line of Apple Certified Refurbished 2012 iPod touches for up to $70 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free: -... Read more
27″ Apple Display (refurbished) available for...
The Apple Store has Apple Certified Refurbished 27″ Thunderbolt Displays available for $799 including free shipping. That’s $200 off the cost of new models. Read more
Apple TV (refurbished) now available for only...
The Apple Store has Apple Certified Refurbished 2012 Apple TVs now available for $75 including free shipping. That’s $24 off the cost of new models. Apple’s one-year warranty is standard. Read more
AnandTech Reviews 2013 MacBook Air (11-inch)...
AnandTech is never the first out with Apple new product reviews, but I’m always interested in reading their detailed, in-depth analyses of Macs and iDevices. AnandTech’s Vivek Gowri bought and tried... Read more
iPad, Tab, Nexus, Surface, And Kindle Fire: W...
VentureBeat’s John Koetsier says: The iPad may have lost the tablet wars to an army of Android tabs, but its still first in peoples hearts. Second place, however, belongs to a somewhat unlikely... Read more
Should You Buy An iPad mini Or An iPad 4?
Macworld UK’s David Price addresses the conundrum of which iPAd to buy? Apple iPad 4, iPad 2, iPad mini? Or hold out for the iPad mini 2 or the iPad 5? Price notes that potential Apple iPad... Read more
iDraw 2.3 A More Economical Alternative To Ad...
If you’re a working graphics pro, you can probably justify paying the stiff monthly rental fee to use Adobe’s Creative Cloud, including the paradigm-setting vector drawing app. Adobe Illustrator. If... Read more

Jobs Board

Sales Representative - *Apple* Honda - Appl...
APPLE HONDA AUTOMOTIVE CAREER FAIR! NOW HIRING AUTO SALES REPS, AUTO SERVICE BDC REPS & AUTOMOTIVE BILLER! NO EXPERIENCE NEEDED! Apple Honda is offering YOU a Read more
*Apple* Developer Support Advisor - Portugue...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more than Read more
RBB - *Apple* OS X Platform Engineer - Barc...
RBB - Apple OS X Platform Engineer Ref 63198 Country USA…protected by law. Main Function | The engineering of Apple OS X based solutions, in line with customer and Read more
RBB - Core Software Engineer - Mac Platform (...
RBB - Core Software Engineer - Mac Platform ( Apple OS X) Ref 63199 Country USA City Dallas Business Area Global Technology Contract Type Permanent Estimated publish end Read more
*Apple* Desktop Analyst - Infinity Consultin...
Job Title: Apple Desktop Analyst Location: Yonkers, NY Job Type: Contract to hire Ref No: 13-02843 Date: 2013-07-30 Find other jobs in Yonkers Desktop Analyst The Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.