Saturday, October 26, 13
Saturday, October 26, 13
A game is a system in which players
engage in an artificial conflict,
defined by rules, that results in a
quantifiable outcome.
Rules of Play

Saturday, October 26, 13
Saturday, October 26, 13
[Expect(game)
toBe:Fun]
TDD in Games with Cocos2D

Saturday, October 26, 13
Sprites and Sprite Sheets
Scene Management
Actions and Animations
Effects
Menus
Tile Maps
And more....

Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
The player starts
the game and has
three buckets.  
They can move
the buckets right
and left.

Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Hello World Layer
+(CCScene *) scene
{
!// 'scene' is an autorelease object.
!CCScene *scene = [CCScene node];
!
!// 'layer' is an autorelease object.
!HelloWorldLayer *layer = [HelloWorldLayer node];
!
!// add layer as a child to scene
![scene addChild: layer];
!
!// return the scene
!return scene;
}

Saturday, October 26, 13
Hello World Layer
// on "init" you need to initialize your instance
-(id) init
{
! // always call "super" init
! // Apple recommends to re-assign "self" with the "super's" return value
! if( (self=[super init]) ) {
!!
! ! // create and initialize a Label
! ! CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World"
fontName:@"Marker Felt" fontSize:64];
!!
!!
!
!!
!!
!!
!!
!!

// ask director for the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );
// add the label as a child to this Layer
[self addChild: label];

Saturday, October 26, 13
Buckets
OCDSpec2Context(BucketsSpec) {
Describe(@"moving", ^{
It(@"moves to the right", ^{
Buckets *buckets = [[Buckets alloc] initWithPosition:CGPointMake(10.0, 10.0)];
[buckets move:1];
[ExpectFloat(buckets.position.x) toBe:11.0 withPrecision:0.00001];
});
It(@"moves to the left", ^{
Buckets *buckets = [[Buckets alloc] initWithPosition:CGPointMake(10.0, 10.0)];
[buckets move:-1.0];
[ExpectFloat(buckets.position.x) toBe:9.0 withPrecision:0.00001];
});
});
}

Saturday, October 26, 13
Buckets
@implementation Buckets
-(id) initWithPosition:(CGPoint) position
{
if (self = [super init])
{
self.position = position;
}
return self;
}
-(void) move:(float) movement
{
self.position = CGPointMake(self.position.x + movement,
self.position.y);
}
@end

Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Bombing Layer
-(id) init
{
if( (self=[super init]) ) {
! ! // ask director for the window size
! ! CGSize size = [[CCDirector sharedDirector] winSize];
Buckets *buckets = [[Buckets alloc] initWithPosition:CGPointMake(size.width / 2,
size.height / 2)];
BucketsSprite *sprite = [BucketsSprite spriteWithBuckets:buckets];
! ! // add the sprite as a child to this Layer
! ! [self addChild: sprite];
!}
! return self;
}

Saturday, October 26, 13
Buckets Sprite
@implementation BucketsSprite
+(id) spriteWithBuckets:(Buckets *)buckets
{
BucketsSprite *sprite =
[BucketsSprite spriteWithFile:@"buckets.png"];
sprite.buckets = buckets;
[sprite scheduleUpdate];
return sprite;
}
-(void)update:(ccTime)delta {
[self setPosition:self.buckets.position];
}
@end

Saturday, October 26, 13
Saturday, October 26, 13
Bombing Layer
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint point = [touch locationInView:[touch view]];
CGSize size = [[CCDirector sharedDirector] winSize];
if (point.x > size.width / 2)
{
self.movement++;
}
else
{
self.movement--;
}
return YES;
}

Saturday, October 26, 13
Bombing Layer
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint point = [touch locationInView:[touch view]];
CGSize size = [[CCDirector sharedDirector] winSize];
if (point.x > size.width / 2)
{
self.movement--;
}
else
{
self.movement++;
}
}

Saturday, October 26, 13
Bombing Layer
-(void)update:(ccTime)delta
{
BucketsSprite *sprite =
(BucketsSprite *)[self getChildByTag:kBucket];
[sprite move:self.movement];
}

Saturday, October 26, 13
Saturday, October 26, 13
?
Saturday, October 26, 13
Saturday, October 26, 13
Bomber Spec
OCDSpec2Context(BomberSpec) {
Describe(@"moving back and forth", ^{
It(@"moves towards its next spot", ^{
RiggedLocations *locations = [RiggedLocations newWithValues:@[@0.0]];
Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(10, 40)
speed:1.0
locationChooser:locations];
[bomber start];
[bomber update:1.0];
[ExpectFloat(bomber.position.x) toBe:9.0 withPrecision:0.0001];
});

Saturday, October 26, 13
Rigged Locations
@implementation RiggedLocations
-(float) next {
float value = [(NSNumber *) [self.values firstObject]
floatValue];
if (self.values.count > 1) {
[self.values removeObjectAtIndex:0];
}
return value;
}
+(RiggedLocations *) newWithValues:(NSArray *)array {
RiggedLocations *locations = [RiggedLocations new];
locations.values = [NSMutableArray
arrayWithArray:array];
return locations;
}
@end

Saturday, October 26, 13
Random Locations
Describe(@"using the random number generator", ^{
It(@"uses the random number generator for its next location", ^{
id rand = [OCMockObject mockForProtocol:@protocol(RandomNumberGenerator)];
NSRange range = NSMakeRange(0, 100);
RandomLocationChooser *chooser = [RandomLocationChooser newChooserWithRange:range generator:rand];
float retVal = 0.0;
[[[rand stub] andReturnValue:OCMOCK_VALUE(retVal)] generate];
[ExpectFloat([chooser next]) toBe:0.0 withPrecision:0.0001];
});

Saturday, October 26, 13
Saturday, October 26, 13
Bomber Spec
It(@"stops at the location when coming from the left", ^{
RiggedLocations *locations = [RiggedLocations newWithValues:@[@19.0, @22.0]];
Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(17, 40) speed:2.0
locationChooser:locations];
[bomber start];
[bomber update:1.0];
[bomber update:1.0];
[ExpectFloat(bomber.position.x) toBe:21.0 withPrecision:0.0001];
});
It(@"doesn't move until it is started", ^{
RiggedLocations *locations = [RiggedLocations newWithValues:@[@19.0, @22.0]];
Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(17, 40) speed:2.0
locationChooser:locations];
[bomber update:1.0];
[ExpectFloat(bomber.position.x) toBe:17.0 withPrecision:0.0001];
});

Saturday, October 26, 13
Bomber
- (void)update:(float)deltaTime
{
if (self.started)
{
float moveDistance = self.speed * deltaTime;
float distanceRemaining = abs(self.location - self.position.x);
if (self.location > self.position.x) {
self.position = CGPointMake(self.position.x + moveDistance, self.position.y);
}
else {
self.position = CGPointMake(self.position.x - moveDistance, self.position.y);
}
if (moveDistance >= distanceRemaining) {
self.location = [self.locations next];
}
}

Saturday, October 26, 13
“Random”
-(id) init
{
if (self = [super init])
{
srand48(time(0));
}
return self;
}
-(float) generate
{
return drand48();
}

Saturday, October 26, 13
Bombing Layer
Bomber *bomber = [[Bomber alloc]
initWithPosition:CGPointMake(size.width / 2,
speed:60.0
locationChooser:chooser];
BomberSprite *bomberSprite =
[BomberSprite newSpriteWithBomber:bomber];
[self addChild:bomberSprite z:0 tag:kBomber];
[bomber start];

Saturday, October 26, 13
Bomber Sprite
@implementation BomberSprite
+(id) newSpriteWithBomber:(Bomber *)bomber
{
BomberSprite *sprite = [BomberSprite
spriteWithFile:@"bomber.png"];
sprite.bomber = bomber;
[sprite scheduleUpdate];
return sprite;
}
-(void)update:(ccTime)delta
{
[self.bomber update:delta];
[self setPosition:self.bomber.position];
}

Saturday, October 26, 13
Saturday, October 26, 13
Bomber Spec
It(@"drops a bomb when it changes direction", ^{
RiggedLocations *locations = [RiggedLocations
newWithValues:@[@18.0]];
Bomber *bomber = [[Bomber alloc]
initWithPosition:CGPointMake(17.0,
speed:1.0
locationChooser:locations];
[bomber start];
[bomber update:1.0];
[ExpectInt(bomber.bombCount) toBe:1];
});

Saturday, October 26, 13
Bomber Spec
It(@"moves the bomb on each update", ^{
RiggedLocations *locations = [RiggedLocations
newWithValues:@[@18.0]];
Bomber *bomber = [[Bomber alloc]
initWithPosition:CGPointMake(17.0
speed:1.0
locationChooser:locations
height:10
bombHeight:20];
[bomber start];
[bomber update:1.0];
[bomber update:1.0];
CGPoint bombPosition;
[(NSValue *) bomber.bombs[0] getValue:&bombPosition];
[ExpectInt(bombPosition.x) toBe:18];
[ExpectInt(bombPosition.y) toBe:55 - kGravity];
});

Saturday, October 26, 13
Bomber Sprite
[self.bomber.bombs enumerateObjectsUsingBlock:^(id obj, NSUInteger
idx, BOOL *stop) {
[self.parent removeChildByTag:kBomb + idx];
NSValue *bombValue = (NSValue *)obj;
CGPoint location;
[bombValue getValue:&location];
CCSprite *bombSprite = [CCSprite spriteWithFile:@"bomb.png"];
bombSprite.position = location;
[self.parent addChild:bombSprite z:0 tag:kBomb + idx];
}];

Saturday, October 26, 13
SHOW ME

Saturday, October 26, 13
TDD and Games
• Have a Plan
• You Use the Framework
• Keep Views dumb
Bullets Suck - Hi Eric

Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
Saturday, October 26, 13
5!

Saturday, October 26, 13
@paytonrules
www.paytonrules.com
www.8thlight.com
www.github.com/paytonrules

Saturday, October 26, 13

Test Driven Cocos2d

  • 1.
  • 2.
  • 3.
    A game isa system in which players engage in an artificial conflict, defined by rules, that results in a quantifiable outcome. Rules of Play Saturday, October 26, 13
  • 4.
  • 5.
    [Expect(game) toBe:Fun] TDD in Gameswith Cocos2D Saturday, October 26, 13
  • 6.
    Sprites and SpriteSheets Scene Management Actions and Animations Effects Menus Tile Maps And more.... Saturday, October 26, 13
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
    The player starts thegame and has three buckets.   They can move the buckets right and left. Saturday, October 26, 13
  • 16.
  • 17.
  • 18.
    Hello World Layer +(CCScene*) scene { !// 'scene' is an autorelease object. !CCScene *scene = [CCScene node]; ! !// 'layer' is an autorelease object. !HelloWorldLayer *layer = [HelloWorldLayer node]; ! !// add layer as a child to scene ![scene addChild: layer]; ! !// return the scene !return scene; } Saturday, October 26, 13
  • 19.
    Hello World Layer //on "init" you need to initialize your instance -(id) init { ! // always call "super" init ! // Apple recommends to re-assign "self" with the "super's" return value ! if( (self=[super init]) ) { !! ! ! // create and initialize a Label ! ! CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64]; !! !! ! !! !! !! !! !! // ask director for the window size CGSize size = [[CCDirector sharedDirector] winSize]; // position the label on the center of the screen label.position = ccp( size.width /2 , size.height/2 ); // add the label as a child to this Layer [self addChild: label]; Saturday, October 26, 13
  • 20.
    Buckets OCDSpec2Context(BucketsSpec) { Describe(@"moving", ^{ It(@"movesto the right", ^{ Buckets *buckets = [[Buckets alloc] initWithPosition:CGPointMake(10.0, 10.0)]; [buckets move:1]; [ExpectFloat(buckets.position.x) toBe:11.0 withPrecision:0.00001]; }); It(@"moves to the left", ^{ Buckets *buckets = [[Buckets alloc] initWithPosition:CGPointMake(10.0, 10.0)]; [buckets move:-1.0]; [ExpectFloat(buckets.position.x) toBe:9.0 withPrecision:0.00001]; }); }); } Saturday, October 26, 13
  • 21.
    Buckets @implementation Buckets -(id) initWithPosition:(CGPoint)position { if (self = [super init]) { self.position = position; } return self; } -(void) move:(float) movement { self.position = CGPointMake(self.position.x + movement, self.position.y); } @end Saturday, October 26, 13
  • 22.
  • 23.
  • 24.
    Bombing Layer -(id) init { if((self=[super init]) ) { ! ! // ask director for the window size ! ! CGSize size = [[CCDirector sharedDirector] winSize]; Buckets *buckets = [[Buckets alloc] initWithPosition:CGPointMake(size.width / 2, size.height / 2)]; BucketsSprite *sprite = [BucketsSprite spriteWithBuckets:buckets]; ! ! // add the sprite as a child to this Layer ! ! [self addChild: sprite]; !} ! return self; } Saturday, October 26, 13
  • 25.
    Buckets Sprite @implementation BucketsSprite +(id)spriteWithBuckets:(Buckets *)buckets { BucketsSprite *sprite = [BucketsSprite spriteWithFile:@"buckets.png"]; sprite.buckets = buckets; [sprite scheduleUpdate]; return sprite; } -(void)update:(ccTime)delta { [self setPosition:self.buckets.position]; } @end Saturday, October 26, 13
  • 26.
  • 27.
    Bombing Layer -(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent *)event { CGPoint point = [touch locationInView:[touch view]]; CGSize size = [[CCDirector sharedDirector] winSize]; if (point.x > size.width / 2) { self.movement++; } else { self.movement--; } return YES; } Saturday, October 26, 13
  • 28.
    Bombing Layer -(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent *)event { CGPoint point = [touch locationInView:[touch view]]; CGSize size = [[CCDirector sharedDirector] winSize]; if (point.x > size.width / 2) { self.movement--; } else { self.movement++; } } Saturday, October 26, 13
  • 29.
    Bombing Layer -(void)update:(ccTime)delta { BucketsSprite *sprite= (BucketsSprite *)[self getChildByTag:kBucket]; [sprite move:self.movement]; } Saturday, October 26, 13
  • 30.
  • 31.
  • 32.
  • 33.
    Bomber Spec OCDSpec2Context(BomberSpec) { Describe(@"movingback and forth", ^{ It(@"moves towards its next spot", ^{ RiggedLocations *locations = [RiggedLocations newWithValues:@[@0.0]]; Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(10, 40) speed:1.0 locationChooser:locations]; [bomber start]; [bomber update:1.0]; [ExpectFloat(bomber.position.x) toBe:9.0 withPrecision:0.0001]; }); Saturday, October 26, 13
  • 34.
    Rigged Locations @implementation RiggedLocations -(float)next { float value = [(NSNumber *) [self.values firstObject] floatValue]; if (self.values.count > 1) { [self.values removeObjectAtIndex:0]; } return value; } +(RiggedLocations *) newWithValues:(NSArray *)array { RiggedLocations *locations = [RiggedLocations new]; locations.values = [NSMutableArray arrayWithArray:array]; return locations; } @end Saturday, October 26, 13
  • 35.
    Random Locations Describe(@"using therandom number generator", ^{ It(@"uses the random number generator for its next location", ^{ id rand = [OCMockObject mockForProtocol:@protocol(RandomNumberGenerator)]; NSRange range = NSMakeRange(0, 100); RandomLocationChooser *chooser = [RandomLocationChooser newChooserWithRange:range generator:rand]; float retVal = 0.0; [[[rand stub] andReturnValue:OCMOCK_VALUE(retVal)] generate]; [ExpectFloat([chooser next]) toBe:0.0 withPrecision:0.0001]; }); Saturday, October 26, 13
  • 36.
  • 37.
    Bomber Spec It(@"stops atthe location when coming from the left", ^{ RiggedLocations *locations = [RiggedLocations newWithValues:@[@19.0, @22.0]]; Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(17, 40) speed:2.0 locationChooser:locations]; [bomber start]; [bomber update:1.0]; [bomber update:1.0]; [ExpectFloat(bomber.position.x) toBe:21.0 withPrecision:0.0001]; }); It(@"doesn't move until it is started", ^{ RiggedLocations *locations = [RiggedLocations newWithValues:@[@19.0, @22.0]]; Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(17, 40) speed:2.0 locationChooser:locations]; [bomber update:1.0]; [ExpectFloat(bomber.position.x) toBe:17.0 withPrecision:0.0001]; }); Saturday, October 26, 13
  • 38.
    Bomber - (void)update:(float)deltaTime { if (self.started) { floatmoveDistance = self.speed * deltaTime; float distanceRemaining = abs(self.location - self.position.x); if (self.location > self.position.x) { self.position = CGPointMake(self.position.x + moveDistance, self.position.y); } else { self.position = CGPointMake(self.position.x - moveDistance, self.position.y); } if (moveDistance >= distanceRemaining) { self.location = [self.locations next]; } } Saturday, October 26, 13
  • 39.
    “Random” -(id) init { if (self= [super init]) { srand48(time(0)); } return self; } -(float) generate { return drand48(); } Saturday, October 26, 13
  • 40.
    Bombing Layer Bomber *bomber= [[Bomber alloc] initWithPosition:CGPointMake(size.width / 2, speed:60.0 locationChooser:chooser]; BomberSprite *bomberSprite = [BomberSprite newSpriteWithBomber:bomber]; [self addChild:bomberSprite z:0 tag:kBomber]; [bomber start]; Saturday, October 26, 13
  • 41.
    Bomber Sprite @implementation BomberSprite +(id)newSpriteWithBomber:(Bomber *)bomber { BomberSprite *sprite = [BomberSprite spriteWithFile:@"bomber.png"]; sprite.bomber = bomber; [sprite scheduleUpdate]; return sprite; } -(void)update:(ccTime)delta { [self.bomber update:delta]; [self setPosition:self.bomber.position]; } Saturday, October 26, 13
  • 42.
  • 43.
    Bomber Spec It(@"drops abomb when it changes direction", ^{ RiggedLocations *locations = [RiggedLocations newWithValues:@[@18.0]]; Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(17.0, speed:1.0 locationChooser:locations]; [bomber start]; [bomber update:1.0]; [ExpectInt(bomber.bombCount) toBe:1]; }); Saturday, October 26, 13
  • 44.
    Bomber Spec It(@"moves thebomb on each update", ^{ RiggedLocations *locations = [RiggedLocations newWithValues:@[@18.0]]; Bomber *bomber = [[Bomber alloc] initWithPosition:CGPointMake(17.0 speed:1.0 locationChooser:locations height:10 bombHeight:20]; [bomber start]; [bomber update:1.0]; [bomber update:1.0]; CGPoint bombPosition; [(NSValue *) bomber.bombs[0] getValue:&bombPosition]; [ExpectInt(bombPosition.x) toBe:18]; [ExpectInt(bombPosition.y) toBe:55 - kGravity]; }); Saturday, October 26, 13
  • 45.
    Bomber Sprite [self.bomber.bombs enumerateObjectsUsingBlock:^(idobj, NSUInteger idx, BOOL *stop) { [self.parent removeChildByTag:kBomb + idx]; NSValue *bombValue = (NSValue *)obj; CGPoint location; [bombValue getValue:&location]; CCSprite *bombSprite = [CCSprite spriteWithFile:@"bomb.png"]; bombSprite.position = location; [self.parent addChild:bombSprite z:0 tag:kBomb + idx]; }]; Saturday, October 26, 13
  • 46.
  • 47.
    TDD and Games •Have a Plan • You Use the Framework • Keep Views dumb Bullets Suck - Hi Eric Saturday, October 26, 13
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.