cocos2d 2.0for iPhone
       勉強会
   cocos2dを覚えよう!
       初級編④
動作環境
•  Mac OS X 10.8 Mountain Lion
•  xcode 4.5.2
•  cocos2d for iPhone 2.0
 http://www.cocos2d-iphone.org/
はじめに
•  cocos2dを覚えよう!初級編④
•  当たり判定とかそういうやつ!
もくじ
•    忍ベーダー
•    敵の移動
•    手裏剣
•    当たり判定
•    点数の増加とレベル
•    改造してみよう!
忍ベーダー
•  次々と現れる侵略者(忍ベーダー)をたおしていこう!
•  忍ベーダーが、ライフラインを超えるとゲームオーバー
ルール
•  タイトル画面でボタンを押すとゲーム開始
•  READY→STARTのあと、ゲーム開始
•  忍ベーダーは、上から攻めてくる。大きさによって硬さ
   や得点が違う
•  忍者は、ボタンで左右移動
•  忍者が止まると上を向いて、手裏剣を自動で打つ
•  ライフラインに忍ベーダーが来たらゲームオーバー
•  GAMEOVERを表示して、タイトル画面に戻る
4inch対応
     •  今回は画面上部の見え
        る部分を変更すること
        で対応します
     •  スコアなどの文字は、
        画面の高さをもとに表
        示して、画面内に収ま
        るようにします。
     •  忍ベーダーや手裏剣は、
        4inchの座標で計算しま
        す。3.5inchでも得ない
        画面外の部分でも処理
        を行います。
がめんこうせい
タイトルscene   メインscene
タイトルscene
•  フロントlayer
  –  スコア
•  タイトルlayer
  –  タイトルロゴ
  –  スタートボタン
  –  床
•  背景layer
  –  背景画像
メインscene
•  フロントlayer
  –  スコア
•  スティックlayer
  –  左右のボタン
•  メインlayer
  –    READY? 、START!
  –    ゲームオーバー
  –    忍ベーダー、手裏剣
  –    忍者
  –    点線、床
•  背景layer
  –  背景画像
今回作るところ
  •  敵が出る
  •  忍者が手裏剣を投げる
  •  あたりはんてい
音楽素材について①
•  音楽素材については、PANICPUMPKIN様
   のご好意により、使用させていただいてい
   ます。
•  http://panicpumpkin.omiki.com/
•  ゲームBGMに特化したサイトなのでおす
   すめ!(曲数も多いです)
•  利用報告、 著作表記、リンクなどの明記
   不要で商用利用可能の音楽素材を多数公
   開されています。
音楽素材について②
•  有料でオリジナル曲を制作してもらえる
   「PANSOUND」というサイトもあるので、
   ぜひ利用してみてください!
•  http://pansound.com/
敵の移動
•  上からせまってくる忍ベーダーを作ってみ
   よう!
忍ベーダーの仕様
  •  一定間隔で上から出現
  •  いろんな大きさがある
  •  下に移動する
Invader.h
@interface Invader : NSObject{
}

@property   float   score;
@property   float   speed;
@property   float   minX;
@property   float   maxX;

@property int life;

@property (nonatomic, retain) CCSprite *sprite;
@property (nonatomic, retain) MainLayer *mainLayer;

-(BOOL)damage:(int)pow;
-(BOOL)tick:(ccTime)dt;

@end
Invader.h
@interface Invader : NSObject{
}

@property   float   score;
@property   float   speed;
@property   float   minX;
@property   float   maxX;

@property int life;

@property (nonatomic, retain) CCSprite *sprite;
@property (nonatomic, retain) MainLayer *mainLayer;

-(BOOL)damage:(int)pow;
-(BOOL)tick:(ccTime)dt;

@end
Invader.m①
- (id)init{
    if((self = [super init])){
        self.sprite = [CCSprite spriteWithSpriteFrameName:@"vader.png"];
        GameData *gameData = [GameData getInstance];

    self.sprite.scale = (CCRANDOM_0_1() * (gameData.invaderScaleMax -
gameData.invaderScaleMin)) + gameData.invaderScaleMin;

     self.minX = self.sprite.boundingBox.size.width / 2;
     self.maxX = 320 - (self.sprite.boundingBox.size.width / 2);
Invader.m②
        float ex = (CCRANDOM_0_1() * (self.maxX - self.minX)) + self.minX;
        float ey = 568 + (self.sprite.boundingBox.size.height / 2);

        self.sprite.position = ccp(ex, ey);
        self.speed = gameData.invaderSpeed / self.sprite.scale;
        self.life = (int)(self.sprite.scale * MIN_LIFE);
        self.score = self.life;

        [self animeStart];
    }
           return self;
}
かいせつ
•  sprite.scaleで大きさを変更できる(通常
   は1.0f)
•  画面の端から自分の大きさを引いたりして、
   表示位置の最小と最大を求める
•  最小と最大の間をランダムで求めて、初期
   表示の位置にする!
かいせつ2
•  speed、life、scoreを大きさを元に設定
   する!
•  大きいほどゆっくりで、lifeを大きくする
   と、良い感じになる!
Invader.m③
-(void)animeStart{
   [self.sprite stopAllActions];

  NSMutableArray *animFrames = [NSMutableArray array];
  [animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:@"vader.png"]];
  [animFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:@"vader2.png"]];

   CCAnimation *animation = [CCAnimation
animationWithSpriteFrames:animFrames delay:0.2f];
   id repeatAnim = [CCRepeatForever actionWithAction:[CCAnimate
actionWithAnimation:animation]];
   [self.sprite runAction:repeatAnim];
}
かいせつ
•  CCAnimation でフレームを指定してパラ
   パラアニメみたいなのをする!
•  CCRepeatForever で繰り返せる!
•  これで、忍ベーダーがアニメーションする
   ようになる!
MainLayer.h
@property float invaderDelayCount;

@property (nonatomic, retain) NSMutableArray *invaderArray;
MainLayer.m①
  // 敵を出す
  self.invaderDelayCount += dt;
  if(gameData.invaderDelayMax <= self.invaderDelayCount){
      self.invaderDelayCount = 0;

      Invader *invader = [[[Invader alloc] init] autorelease];
      CCSpriteBatchNode *gameBatchNode = (CCSpriteBatchNode*)
[self getChildByTag:OBJECT_GAME];
      [gameBatchNode addChild:invader.sprite
z:SPRITE_GAME_ENEMY tag:SPRITE_GAME_ENEMY];

      [self.invaderArray addObject:invader];
  }
MainLayer.m②

// 敵の移動
BOOL isGameOver = NO;
for (Invader *invader in self.invaderArray) {
   BOOL isAttack = [invader tick:dt];
   if(isAttack){
       isGameOver = YES;
       break;
   }
}
かいせつ
•  self.invaderDelayCountが一定の値を超
   えたら、忍ベーダーを1体追加する
•  忍ベーダーは、配列に入れておく!
•  忍ベーダーの移動処理は、配列の中をルー
   プして一体ずつ行う!
invader.m
-(BOOL)tick:(ccTime)dt{
   float x = self.sprite.position.x;
   float y = self.sprite.position.y - (self.speed * dt);
   self.sprite.position = ccp(x, y);

   // 侵略判定
   float dy = self.sprite.position.y -
(self.sprite.boundingBox.size.height / 2);
   if(dy <= GAMEOVER_LINE){
       return YES;
   }

    return NO;
}
かいせつ
•  移動は、自分の座標をスピード*経過時間
   分減らすことで行う!
•  移動後、境界線まで来てるかどうかを
   チェックして返却する!
できた!
•  てきだ!
手裏剣
•  手裏剣を投げてみよう
•  INVADERみたいにクラスを作ってもいい
   けど、シンプルにspriteだけで作ってみよ
   う!
MainLayer.m①
-(void)shotAction:(CGPoint)startPos{
   if([self.smallShotArray count] >= SHOT_MAX){
       return;
   }

  CCSprite *sp = [CCSprite spriteWithSpriteFrameName:@"shot1.png"];
  sp.position = ccp(startPos.x, startPos.y + 24.0f + 20.0f);

  CCSpriteBatchNode *gameBatchNode = (CCSpriteBatchNode*)[self getChildByTag:OBJECT_GAME];
  [gameBatchNode addChild:sp z:SPRITE_GAME_SHOT];

  NSMutableArray* actions = [NSMutableArray array];
  [actions addObject:[CCSpawn actions:
               [CCMoveTo actionWithDuration:2.0f position:ccp(sp.position.x, 500)],
               [CCSequence actions:
                [CCRotateBy actionWithDuration:0.5f angle:360],
                [CCRotateBy actionWithDuration:0.5f angle:360],
                [CCRotateBy actionWithDuration:0.5f angle:360],
                [CCRotateBy actionWithDuration:0.5f angle:360],
                nil],
               nil]];
MainLayer.m②
   [actions addObject:[CCCallFuncN actionWithTarget:self
selector:@selector(shotActionEnd:)]];

  id action;
  action = [CCSequence actionWithArray:actions];
      [sp runAction:action];

  [self.smallShotArray addObject:sp];
}
-(void)shotActionEnd:(id)node{
   [self.smallShotArray removeObject:node];
   [node removeFromParentAndCleanup:YES];
}
かいせつ
•  self.smallShotArrayに手裏剣を入れて管
   理する!
•  CCRotateBy で1回転のアニメーション
   を作って4連続させることでくるくるまわ
   す!
•  shotActionEndでアニメーションが終
   わったら、手裏剣を配列から消す!画面
   からも消す!
Ninja.m
self.waitCount += dt;
          if(self.way != NINJYA_WAY_UP && self.waitCount >=
NINJA_UP_DELAY){
              self.waitCount = 0;

              CCSpriteFrame *sf = [[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:@"ninjya_u.png"];
              [self.sprite setDisplayFrame:sf];
              self.way = NINJYA_WAY_UP;
          }
          if(self.way == NINJYA_WAY_UP && self.waitCount >=
SHOT_DELAY){
              self.waitCount = 0;
              [self.mainScene.mainLayer
shotAction:self.sprite.position];
          }
かいせつ
•  忍者が上を向いてちょっとたったら手裏剣
   発射!
•  手裏剣を投げたら、self.waitCount = 0;
   にして待ち時間をリセットする!
できた!
•  ゲームっぽくなってき
   た!
当たり判定
•  手裏剣と敵の当たり判定をつけてみよう
MainLayer.m①
             //	
  弾の処理
                           NSMutableArray	
  *delShotSpriteArray	
  =	
  [NSMutableArray	
  array];	
  
	
  	
  	
  	
  NSMutableArray	
  *delInvaderArray	
  =	
  [NSMutableArray	
  array];	
  
	
  	
  	
  	
  for(CCSprite	
  *shotSprite	
  in	
  self.smallShotArray){	
  
	
  	
  	
  	
  	
  	
  	
  	
  BOOL	
  isDelShot	
  =	
  NO;	
  
	
  	
  	
  	
  	
  	
  	
  	
  //	
  攻撃
	
  	
  	
  	
  	
  	
  	
  	
  for	
  (Invader	
  *invader	
  in	
  self.invaderArray)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  if(CGRectIntersectsRect(shotSprite.boundingBox,	
  invader.sprite.boundingBox)){	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(CGRectContainsPoint(invader.sprite.boundingBox,	
  shotSprite.position)){	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  BOOL	
  isDel	
  =	
  [invader	
  damage:SHOT_SMALL_POW];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(isDel){	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  CCParticleSystem	
  *	
  ps	
  =	
  [CCParticleSystemQuad	
  particleWithFile:@"fire.plist"];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ps.position	
  =	
  invader.sprite.position;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ps.scale	
  =	
  invader.sprite.scale	
  *	
  0.5f;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ps.autoRemoveOnFinish	
  =	
  YES;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [self	
  addChild:ps	
  z:SPRITE_GAME_PARTICLE];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
MainLayer.m②
	
  [delInvaderArray	
  addObject:invader];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [invader.sprite	
  removeFromParentAndCleanup:YES];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [[SimpleAudioEngine	
  sharedEngine]playEffect:@"xout.caf"];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }else{	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [[SimpleAudioEngine	
  sharedEngine]playEffect:@"xhit.caf"];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  isDelShot	
  =	
  YES;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  [self.invaderArray	
  removeObjectsInArray:delInvaderArray];	
  
	
  	
  	
  	
  	
  	
  	
  	
  [delInvaderArray	
  removeAllObjects];	
  
	
  
	
  	
  	
  	
  	
  	
  	
  	
  //	
  弾を削除
	
  	
  	
  	
  	
  	
  	
  	
  if(isDelShot){	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [delShotSpriteArray	
  addObject:shotSprite];	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [shotSprite	
  removeFromParentAndCleanup:YES];	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  [self.smallShotArray	
  removeObjectsInArray:delShotSpriteArray];
かいせつ①
•  今回は、手裏剣の中心が忍ベーダーの当た
   り判定に入っているかどうかで判定!
   CGRectContainsPointを使う。
•  当たり判定については、見た目に自然にな
   るように判定方法を考えよう!
•  手裏剣があたったら、忍ベーダーにダメー
   ジを与える!
かいせつ②
•  忍ベーダーが消滅したら消滅処理!パー
   ティクルを表示する!
•  ps.autoRemoveOnFinish = YES;を忘れ
   ずに!これをYESにすると、パーティクル
   消滅後自動的に終了処理が行われる!
•  ループ中の配列から値を消去するには、
   removeObjectsInArrayを使おう!
かいせつ③
•  今回は、後半たくさん敵が来るので、1個
   の手裏剣で複数ダメージにしてみた!
•  重なっている忍ベーダすべてにダメージを
   あたえる!(それがいやならbreakしよ
   う!)
Invader.m
-(BOOL)damage:(int)pow{
   self.life -= pow;
   self.sprite.position = ccp(self.sprite.position.x, self.sprite.position.y +
32.0f);

  CCSpriteFrame *sf = [[CCSpriteFrameCache
sharedSpriteFrameCache] spriteFrameByName:@"vader_hit.png"];
  [self.sprite setDisplayFrame:sf];

    if(self.life <= 0){
        GameData *gameData = [GameData getInstance];
        [gameData addScore:self.score];
        return YES;
    }
    return NO;
}
かいせつ
•  ダメージを受けたら、画像を変えて少し後
   ろの位置に表示する!
できた!
•  ゲームっぽくなってき
   た!
点数の増加とレベル
•  点数の増加に合わせて、レベルが上がるよ
   うにしよう!
GameData.m
-(void)resetScore{
   self.score = 0;
   self.exp = 0;
   self.level = 1;

    self.invaderScaleMin = 1.0f;
    self.invaderScaleMax = 1.0f;
    self.invaderDelayMax = 2.0f;
    self.invaderSpeed = 90;
}

-(void)addScore:(int)point{
   self.score += point;
   self.exp++;

    if(self.level < (self.exp / 10)){
        self.level++;
        self.invaderScaleMax += 0.3f;
        self.invaderDelayMax -= 0.25f;
        self.invaderSpeed += 5;
    }
}
かいせつ
•  スコアの増加を一箇所にまとめて、スコア
   増加→レベルアップの処理をおこなう!
•  レベルアップするたびに、忍ベーダーの最
   大の大きさと、移動速度をあげて、出現間
   隔を小さくする!
かんせい!
 •  まあまあいいかんじ!
改造してみよう!
•  アイテムを作ってみよう!
•  バランスを調整しよう!
アイテムを作ってみよう!
•  パワーアップするアイテムを作ってみよ
   う!
•  連射速度や、手裏剣の飛距離を変更してみ
   よう!
•  自分の移動速度を早くしよう!
•  貫通手裏剣を作ってみよう!
バランスを調整しよう!
•  敵の出現間隔を調整しよう!
•  アイテムの出る感覚を調整しよう!
•  敵の速度を調整しよう!
かんせい!
 •  アップストア申請中!
まとめ
•  簡単なゲームが出来た!
•  cocos2d 1.0.の資料だけど、参考に!
 –  http://tonosamart.com/blog/
とのさまラボ!
•  勉強会に参加した人どうしのコミニュティがほしい!
   勉強会の開催日時の告知がほしい!

 という意見をみなさまからいただいたので、
 コミニュティを作成しました!

 ぜひ「いいね!」をお願いします!




https://www.facebook.com/TonosamaLabo	
  
おわり
主催(共同開催):
株式会社 gooya
http://www.gooya.co.jp/
メドレー株式会社
http://www.medley.co.jp/

講師:西田 寛輔 (Tonosamart)
http://www.facebook.com/tonosamart

㉖cocos2dを覚えよう!