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

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

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

【cocos2d】スクロール速度を変化させてみた

cocos2d Objective-C iPhoneアプリ BeeCluster

BeeClusterの4面には高速スクロール部分を作ろうと考えています。
いくつかの方法を考えてみましたが、結局素直にCCMoveByアクションをずらずらと並べるというシンプルな結論に達しました。
まずは動画をご覧ください、ステージ開始後40秒からじわじわとスピードアップし、そこから60秒たつと速度が落ち始め、最後には停止します。

速度変化が滑らかに見えるよう1秒毎にCCMoveByアクションを切り替えています。
例によって動画キャプチャーのためにフレームレートが落ちてガクガクしてしまっていますが、通常は60fpsで滑らかに動いています。
クラスの実装はこのようになっています。完全に4面専用の処理にしてしまいました。
1から3面まではテクスチャー6枚分をメモリーに読み込みますが、4面は2枚のテクスチャーを切り替えて繰り返し表示しています。高速スクロールによってステージの縦方向のピクセル数がかなり大きくなるのでメモリー節約のためです。
4面用のイニシャライザーでは何も考えずに大量のアクションをCCSequenceで並べています。
ちなみに、アクションを細切れにして、終了したら次のアクションを掛ける方法も試しましたが失敗しました。
この場合、アクションの掛け替えのところでわずかなタイムラグが発生し、スクロールがぎこちなくなってしまいます。

@implementation BGLayer
{
    float _height;
}


+ (id)stage:(int)level
{
    switch (level) {
        case 1:
            return [[self alloc] initWithFile:@"stage1-" height:1000 count:6 duration:120 repeat:NO];
            break;
        case 2:
            return [[self alloc] initWithFile:@"stage2" height:1000 count:6 duration:120 repeat:YES];
            break;
        case 3:
            return [[self alloc] initWithFile:@"stage3" height:1000 count:6 duration:120 repeat:YES];
            break;
        case 4:
            return [[self alloc] initStage4WithFile:@"stage4" height:1000];
        default:
            return nil;
            break;
    }
}

- (id)initWithFile:(NSString *)filename height:(float)height count:(NSInteger)count duration:(ccTime)duration repeat:(BOOL)repeat
{
    if ((self = [super init])) {
        for (int i = 0; i < count; i++) {
            NSString* file;
            if (repeat) {
                file = [NSString stringWithFormat:@"%@.jpg", filename]; //同じ絵の使い回し
            } else {
                file = [NSString stringWithFormat:@"%@%d.jpg", filename, i];
            }
            CCSprite* background = [CCSprite spriteWithFile:file];
            [self addChild:background];
            background.anchorPoint = ccp(0, 0);
            background.position = ccp(0, i * height);
        }
        CGSize winSize = [CCDirector sharedDirector].winSize;
        id moveBy = [CCMoveBy actionWithDuration:duration position:ccp(0, -(height * count - winSize.height))];
        [self runAction:moveBy];
    }
    return self;
}

- (id)initStage4WithFile:(NSString *)filename height:(float)height
{
    if ((self = [super init])) {
        _height = height;
        NSString* file = [NSString stringWithFormat:@"%@.jpg", filename]; //同じ絵の使い回し
        CCSprite* background1 = [CCSprite spriteWithFile:file];
        CCSprite* background2 = [CCSprite spriteWithFile:file];
        [self addChild:background1];
        [self addChild:background2];
        background1.anchorPoint = ccp(0, 0);
        background2.anchorPoint = ccp(0, 0);
        background1.position = ccp(0, 0);
        background2.position = ccp(0, height);
        id slow = [CCMoveBy actionWithDuration:40 position:ccp(0, -100*40)];
        id up1 = [CCMoveBy actionWithDuration:1 position:ccp(0, -125)];
        id up2 = [CCMoveBy actionWithDuration:1 position:ccp(0, -150)];
        id up3 = [CCMoveBy actionWithDuration:1 position:ccp(0, -175)];
        id up4 = [CCMoveBy actionWithDuration:1 position:ccp(0, -200)];
        id up5 = [CCMoveBy actionWithDuration:1 position:ccp(0, -225)];
        id up6 = [CCMoveBy actionWithDuration:1 position:ccp(0, -250)];
        id up7 = [CCMoveBy actionWithDuration:1 position:ccp(0, -275)];
        id up8 = [CCMoveBy actionWithDuration:1 position:ccp(0, -300)];
        id up9 = [CCMoveBy actionWithDuration:1 position:ccp(0, -325)];
        id up10 = [CCMoveBy actionWithDuration:1 position:ccp(0, -350)];
        id up11 = [CCMoveBy actionWithDuration:1 position:ccp(0, -375)];
        id fast = [CCMoveBy actionWithDuration:60 position:ccp(0, -400*60)];
        id down1 = [CCMoveBy actionWithDuration:1 position:ccp(0, -375)];
        id down2 = [CCMoveBy actionWithDuration:1 position:ccp(0, -350)];
        id down3 = [CCMoveBy actionWithDuration:1 position:ccp(0, -325)];
        id down4 = [CCMoveBy actionWithDuration:1 position:ccp(0, -300)];
        id down5 = [CCMoveBy actionWithDuration:1 position:ccp(0, -275)];
        id down6 = [CCMoveBy actionWithDuration:1 position:ccp(0, -250)];
        id down7 = [CCMoveBy actionWithDuration:1 position:ccp(0, -225)];
        id down8 = [CCMoveBy actionWithDuration:1 position:ccp(0, -200)];
        id down9 = [CCMoveBy actionWithDuration:1 position:ccp(0, -175)];
        id down10 = [CCMoveBy actionWithDuration:1 position:ccp(0, -150)];
        id down11 = [CCMoveBy actionWithDuration:1 position:ccp(0, -125)];
        id down12 = [CCMoveBy actionWithDuration:1 position:ccp(0, -100)];
        id down13 = [CCMoveBy actionWithDuration:1 position:ccp(0, -75)];
        id down14 = [CCMoveBy actionWithDuration:1 position:ccp(0, -50)];
        id down15 = [CCMoveBy actionWithDuration:1 position:ccp(0, -25)];
        id down16 = [CCMoveBy actionWithDuration:1 position:ccp(0, -12)];
        [self runAction:[CCSequence actions: slow, up1, up2, up3, up4, up5, up6,
                         up7, up8, up9, up10, up11, fast,
                         down1, down2, down3, down4, down5, down6, down7, down8,
                         down9, down10, down11, down12, down13, down14, down15, down16,
                         nil]];
        [self schedule:@selector(update:) interval:0.1];
    }
    return self;
}

- (void)update:(ccTime)delta
{    
    for (CCSprite* sprite in self.children) {
        //画面からはみ出たら上に上げる
        CGPoint worldPosition = [self convertToWorldSpace:sprite.position]; //絶対座標を得る
        if (worldPosition.y + _height < 0) {
            sprite.position = ccp(sprite.position.x, sprite.position.y + _height * 2);
        }
    }
}

@end