Book Image

Cocos2d for iPhone 1 Game Development Cookbook

By : Nathan Burba
Book Image

Cocos2d for iPhone 1 Game Development Cookbook

By: Nathan Burba

Overview of this book

Cocos2d for iPhone is a robust but simple-to-use 2D game framework for iPhone. It is easy to use, fast, flexible, free, and Appstore approved. More than 2500 AppStore games already use it, including many best-seller games. Do you want to take your cocos2d game development skills to the next level and become more professional in cocos2d game design? Cocos2d for iPhone 1 Game Development Cookbook will help you reach that next level. You will find over 100 recipes here that explain everything from the drawing of a single sprite to AI pathfinding and advanced networking. Full working examples are emphasized. Starting with the first chapter, Graphics, you will be taken through every major topic of game development. You will find both simple and complex recipes in the book. Each recipe is either a solution to a common problem (playing video files, accelerometer steering) or a cool advanced technique (3D rendering, textured polygons). This cookbook will have you creating professional quality iOS games quickly with its breadth of working example code.
Table of Contents (15 chapters)
Cocos2d for iPhone 1 Game Development Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Animating sprites


Now it is time to add some animation to our sprites. One thing that should be stressed about animation is that it is only as complicated as you make it. In this recipe we will use very simple animation to create a compelling effect. We will create a scene where bats fly around a creepy looking castle. I've also added a cool lightning effect based on the technique used to make the swords glow in the previous recipe.

Getting ready

Please refer to the project RecipeCollection01 for full working code of this recipe. Also note that some code has been omitted for brevity.

How to do it...

Execute the following code:

//SimpleAnimObject.h
@interface SimpleAnimObject : CCSprite {
  int animationType;
  CGPoint velocity;
}

@interface Ch1_AnimatingSprites {
  NSMutableArray *bats;
  CCAnimation *batFlyUp;
  CCAnimation *batGlideDown;
  CCSprite *lightningBolt;
  CCSprite *lightningGlow;
  int lightningRemoveCount;
}

-(CCLayer*) runRecipe {
  //Add our PLIST to the SpriteFrameCache
  [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"simple_bat.plist"];
      
  //Add a lightning bolt
  lightningBolt = [CCSprite spriteWithFile:@"lightning_bolt.png"];
  [lightningBolt setPosition:ccp(240,160)];
  [lightningBolt setOpacity:64];
  [lightningBolt retain];

  //Add a sprite to make it light up other areas.
  lightningGlow = [CCSprite spriteWithFile:@"lightning_glow.png"];
  [lightningGlow setColor:ccc3(255,255,0)];
  [lightningGlow setPosition:ccp(240,160)];
  [lightningGlow setOpacity:100];
  [lightningGlow setBlendFunc: (ccBlendFunc) { GL_ONE, GL_ONE }];
  [lightningBolt addChild:lightningGlow];
  
  //Set a counter for lightning duration randomization
  lightningRemoveCount = 0;

  //Bats Array Initialization
  bats = [[NSMutableArray alloc] init];
  
  //Add bats using a batch node.
  CCSpriteBatchNode *batch1 = [CCSpriteBatchNode batchNodeWithFile:@"simple_bat.png" capacity:10];
  [self addChild:batch1 z:2 tag:TAG_BATS];
  
  //Make them start flying up.
  for(int x=0; x<10; x++){
    //Create SimpleAnimObject of bat
    SimpleAnimObject *bat = [SimpleAnimObject spriteWithBatchNode:batch1 rect:CGRectMake(0,0,48,48)];
    [batch1 addChild:bat];
    [bat setPosition:ccp(arc4random()%400+40, arc4random()%150+150)];
    
    //Make the bat fly up. Get the animation delay (flappingSpeed).
    float flappingSpeed = [self makeBatFlyUp:bat];
    
    //Base y velocity on flappingSpeed.
    bat.velocity = ccp((arc4random()%1000)/500 + 0.2f, 0.1f/flappingSpeed);
    
    //Add a pointer to this bat object to the NSMutableArray
    [bats addObject:[NSValue valueWithPointer:bat]];
    [bat retain];
    
    //Set the bat's direction based on x velocity.
    if(bat.velocity.x > 0){
      bat.flipX = YES;
    }
  }

  //Schedule physics updates
  [self schedule:@selector(step:)];
  
  return self;
}

-(float)makeBatFlyUp:(SimpleAnimObject*)bat {
  CCSpriteFrameCache * cache = [CCSpriteFrameCache sharedSpriteFrameCache];

  //Randomize animation speed.
  float delay = (float)(arc4random()%5+5)/80;
  CCAnimation *animation = [[CCAnimation alloc] initWithName:@"simply_bat_fly" delay:delay];

  //Randomize animation frame order.
  int num = arc4random()%4+1;
  for(int i=1; i<=4; i+=1){
    [animation addFrame:[cache spriteFrameByName:[NSString stringWithFormat:@"simple_bat_0%i.png",num]]];
    num++;
    if(num > 4){ num = 1; }
  }    
  
  //Stop any running animations and apply this one.
  [bat stopAllActions];
  [bat runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:animation]]];
  
  //Keep track of which animation is running.
  bat.animationType = BAT_FLYING_UP;

  return delay;  //We return how fast the bat is flapping.
}

-(void)makeBatGlideDown:(SimpleAnimObject*)bat {
  CCSpriteFrameCache * cache = [CCSpriteFrameCache sharedSpriteFrameCache];

  //Apply a simple single frame gliding animation.
  CCAnimation *animation = [[CCAnimation alloc] initWithName:@"simple_bat_glide" delay:100.0f];
  [animation addFrame:[cache spriteFrameByName:@"simple_bat_01.png"]];
  
  //Stop any running animations and apply this one.
  [bat stopAllActions];
  [bat runAction:[CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:animation]]];
  
  //Keep track of which animation is running.
  bat.animationType = BAT_GLIDING_DOWN;
}

-(void)step:(ccTime)delta {
  CGSize s = [[CCDirector sharedDirector] winSize];
  for(id key in bats){
    //Get SimpleAnimObject out of NSArray of NSValue objects.
    SimpleAnimObject *bat = [key pointerValue];
  
    //Make sure bats don't fly off the screen
    if(bat.position.x > s.width){
      bat.velocity = ccp(-bat.velocity.x, bat.velocity.y);
      bat.flipX = NO;
    }else if(bat.position.x < 0){
      bat.velocity = ccp(-bat.velocity.x, bat.velocity.y);
      bat.flipX = YES;
    }else if(bat.position.y > s.height){
      bat.velocity = ccp(bat.velocity.x, -bat.velocity.y);
      [self makeBatGlideDown:bat];
    }else if(bat.position.y < 0){
      bat.velocity = ccp(bat.velocity.x, -bat.velocity.y);
      [self makeBatFlyUp:bat];
    }
    
    //Randomly make them fly back up
    if(arc4random()%100 == 7){
      if(bat.animationType == BAT_GLIDING_DOWN){ [self makeBatFlyUp:bat]; bat.velocity = ccp(bat.velocity.x, -bat.velocity.y); }
      else if(bat.animationType == BAT_FLYING_UP){ [self makeBatGlideDown:bat]; bat.velocity = ccp(bat.velocity.x, -bat.velocity.y); }
    }
    
    //Update bat position based on direction
    bat.position = ccp(bat.position.x + bat.velocity.x, bat.position.y + bat.velocity.y);
  }
  
  //Randomly make lightning strike
  if(arc4random()%70 == 7){
    if(lightningRemoveCount < 0){
      [self addChild:lightningBolt z:1 tag:TAG_LIGHTNING_BOLT];
      lightningRemoveCount = arc4random()%5+5;
    }
  }
  
  //Count down
  lightningRemoveCount -= 1;
  
  //Clean up any old lightning bolts
  if(lightningRemoveCount == 0){
    [self removeChildByTag:TAG_LIGHTNING_BOLT cleanup:NO];
  }
}

@end

How it works...

This recipe shows us how to structure animation based classes through the use of SimpleAnimObject:

  • Animated object class structure:

    When switching from one animation to another it is often important to keep track of what state the animated object is in. In our example we use SimpleAnimObject, which keeps an arbitrary animationType variable. We also maintain a velocity variable that has a Y scalar value that is inversely proportional to the animation frame delay:

    @interface SimpleAnimObject : CCSprite {
      int animationType;
      CGPoint velocity;
    }

    Depending on how in-depth you want your animation system to be you should maintain more information such as, for example, a pointer to the running CCAnimation instance, frame information, and physical bodies.

There's more...

As you get more involved with Cocos2d game development you will become more and more tempted to use asynchronous actions for gameplay logic and AI. Derived from the CCAction class, these actions can be used for everything from moving a CCNode using CCMoveBy to animating a CCSprite using CCAnimate. When an action is run, an asynchronous timing mechanism is maintained in the background. First time game programmers often over-rely on this feature. The extra overhead required by this technique can multiply quickly when multiple actions are being run. In the following example we have used a simple integer timer that allows us to regulate how long lightning lasts onscreen:

  //Randomly make lightning strike
  if(arc4random()%70 == 7){
    if(lightningRemoveCount < 0){
      [self addChild:lightningBolt z:1 tag:TAG_LIGHTNING_BOLT];
      lightningRemoveCount = arc4random()%5+5;
    }
  }
  
  //Count down
  lightningRemoveCount -= 1;
  
  //Clean up any old lightning bolts
  if(lightningRemoveCount == 0){
    [self removeChildByTag:TAG_LIGHTNING_BOLT cleanup:NO];
  }

Synchronous timers like the one shown in the preceding code snippet are often, but not always, preferable to asynchronous actions. Keep this in mind as your games grow in size and scope.