読者です 読者をやめる 読者になる 読者になる

夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【cocos2d】クマの目玉を動かしビームを発射

cocos2d BeeCluster Objective-C

今回はBeeCluster 4面のボスとして登場するクマの目から発射する赤いビーム光線についての解説です。
今回はこのゲームで初めてCCTint系のアクションを使ってみました。
まずは動画でクマの動きをお確かめください。

クマの目玉がハチを追って動いているのがわかりますか?

目玉を動かす処理はこのようになっています。このメソッドはupdateで毎フレーム呼ばれます。

- (void)rotateEyes
{
    CGPoint touch = ccpAdd([TouchLayer sharedTouch].location, ccp(0, -400)); //遠くに焦点を合わせる
    CGPoint vector = ccpSub(touch, _eyeL.position);
    CGPoint downVector = ccp(0, -100);
    float angle = ccpAngleSigned(vector, downVector);
    _eyeL.rotation = CC_RADIANS_TO_DEGREES(angle);
    
    vector = ccpSub(touch, _eyeR.position);
    angle = ccpAngleSigned(vector, downVector);
    _eyeR.rotation = CC_RADIANS_TO_DEGREES(angle);
}

ハチがいる方向=タッチしている方向ではなく、そこよりかなり下の方に向けて目玉を回転させます。
厳密にタッチの方向に向けようとすると大げさに目玉が動いて不気味な顔になってしまいます。
目玉の回転角度はccpAngleSigned関数で求めます。適当な長さの下向きのベクトルを基準とした角度となります。

次に、ビームを撃つ処理です。

- (void)shootRedBeam
{
    id tintRed = [CCTintTo actionWithDuration:0.5 red:255.0 green:80.0 blue:80.0];
    id tintRedR = [CCCallBlock actionWithBlock:^ {
        [_eyeR runAction:[CCTintTo actionWithDuration:0.5 red:255.0 green:80.0 blue:80.0]];
    }];
    id tintBack = [CCTintTo actionWithDuration:0.5 red:255.0 green:255.0 blue:255.0];
    id tintBackR = [CCCallBlock actionWithBlock:^ {
        [_eyeR runAction:[CCTintTo actionWithDuration:0.5 red:255.0 green:255.0 blue:255.0]];
    }];
    id shoot = [CCCallBlock actionWithBlock:^ {
        CGPoint touch = ccpAdd([TouchLayer sharedTouch].location, TOUCH_OFFSET); //狙う場所はタッチ位置の少し上
        CGPoint eyeLWorld = ccpAdd([self convertToWorldSpace:_eyeL.position], ccp(0, -32));
        CGPoint eyeRWorld = ccpAdd([self convertToWorldSpace:_eyeR.position], ccp(0, -32));
        CGPoint vectorL = ccpSub(touch, eyeLWorld);
        CGPoint vectorR = ccpSub(touch, eyeRWorld);
        EnemyBullet* bulletL = [EnemyBullet bulletWithPosition:eyeLWorld name:@"redBeam" vector:vectorL speed:500];
        EnemyBullet* bulletR = [EnemyBullet bulletWithPosition:eyeRWorld name:@"redBeam" vector:vectorR speed:500];
        [[SpriteBatch sharedSpriteBatch] addChild:bulletL z:30]; //他のオブジェクトより手前に表示されるようにする
        [[SpriteBatch sharedSpriteBatch] addChild:bulletR z:30]; //他のオブジェクトより手前に表示されるようにする
    }];
    id wait = [CCRotateBy actionWithDuration:0.1 angle:0.0];
    id sequence = [CCSequence actions:tintRedR, tintRed, shoot, wait, shoot, wait, shoot, wait,
                   shoot, wait, shoot, wait, shoot, tintBackR, tintBack, nil];
    [_eyeL runAction:sequence];
    [self runAction:[CCRotateBy actionWithDuration:1.0 angle:0.0]]; //時間待ち
}

ビームを撃つ前に目玉を赤く光らせる処理が入ります。
CCTintToアクションをつかいます。赤くするには、緑と青の成分を下げます。赤が最大値255なのに対し、緑と青は80まで下げています。
右目のアクションは左目のアクションと同期させるため、左目のCCCallBlockアクションの中でrunActionします。
これらのアクションを呼ぶ順番は重要です。右目用のCCCallBlockアクションを呼んでから左目のCCTintToアクションを呼びます。逆だと同時ではなく左目、右目の順に色が変化します。

そして、最後にselfで時間稼ぎのアクションを実行します。
このクラスのupdateメソッドは次のようになっていて、selfのアクションが完了すると
SEL _actionMethod変数にいれてある次のコマンドを実行するようになっているので、あるコマンド実行中に次のコマンドを実行したくない場合はselfのアクションをその間継続させる必要があります。

- (void)update:(ccTime)delta
{
    if ([self numberOfRunningActions] == 0) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self performSelector:_actionMethod];
#pragma clang diagnostic pop
    }
    if (!_blinking) {
        [self collision]; //点滅中はあたり判定しない
    }
    [self updatePalms];
    [self collisionPalms];
    [self rotateEyes];
}