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

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

【cocos2d】CCParallaxNodeでサクッと多重スクロール

こちらのポストではCCParallaxNodeを使わずに多重スクロールする方法を解説しました。
今回はCCParallaxNodeを使って手軽に多重スクロールを実装したいと思います。
せっかくなので背景1枚+前景4枚の5重スクロールとしました。
CCParallaxNodeによるスクロールの場合、前景と背景の間にキャラクターのレイヤーを入れる事はできませんので、今回のような壁の表現は可能ですが、上で紹介した例のような前景にキャラクターが隠れるような表現はできませんので使い分けが必要になります。

なお、今回は実験的にRetina用で4000ピクセルのサイズのテクスチャーを使ってみました。
Learn cocos2d 2: Game Development for iOSによると、iPhone4の最大テクスチャーサイズが2048x2048とのことですので、恐らく同じサイズのテクスチャーを使うとiPhone4では不具合が発生すると思います。
なので、実運用上はテクスチャーの縦のサイズを半分にし、上下に並べて配置する必要があります。
現在は同じz位置で画面左右に同じスプライトを置いていますが、テクスチャーを半分にカットしたスプライトを上下に並べる形になります。

それでは動画をご覧ください。

壁のように見える部分はよく見ると4重にスクロールしています。各レイヤーの色をcolorプロパティで少しずつ変えているのでより立体感が出ていると思います。
プログラムはこのようになっています。
長いですが、各レイヤー数x左右分同じ処理を並べているだけですので、やっている事は単純です。
バックグラウンドの速度を1とし、1.2倍、1.4倍、1.6倍、1.8倍の速度でスクロールするフォアグラウンドレイヤーを重ねています。この速度はparallaxRatioという引数で指定します。
前景用の各サイズのテクスチャーを用意するとプログラムサイズが大きくなってしまうので、背景の長さと同じサイズのテクスチャーをそれぞれ1.2倍、1.4倍、1.6倍、1.8倍にscaleプロパティで引き延ばして使います。上のレイヤーほど画質が落ちますが、気になるほどではありません。
テクスチャーのパターンがが各レイヤーで同期してパターンが見えると興醒めなのでflipX, flipYプロパティでテクスチャーを上下左右に反転して各レイヤーに使っています。
「左右にビル群の壁」のような表現をする場合は逆に同期させる事でそれっぽく見せられると思います。

- (id)initStage6
{
    if ((self = [super init])) {
        // スプライトの設定
        CCSprite* background = [CCSprite spriteWithFile:@"stage6.jpg"];
        CCSprite* foreground1L = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground1R = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground2L = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground2R = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground3L = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground3R = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground4L = [CCSprite spriteWithFile:@"stage6-FG.png"];
        CCSprite* foreground4R = [CCSprite spriteWithFile:@"stage6-FG.png"];
        
        //拡大率の設定
        foreground1L.scale = 1.2;
        foreground1R.scale = 1.2;
        foreground2L.scale = 1.4;
        foreground2R.scale = 1.4;
        foreground3L.scale = 1.6;
        foreground3R.scale = 1.6;
        foreground4L.scale = 1.8;
        foreground4R.scale = 1.8;
        
        //反転の設定
        foreground2R.flipY = YES;
        foreground2L.flipY = YES;
        foreground3L.flipX = YES;
        foreground3R.flipX = YES;
        foreground4L.flipY = YES;
        foreground4R.flipY = YES;
        foreground4L.flipX = YES;
        foreground4R.flipX = YES;
        
        //アンカーポイントを中央下端に
        background.anchorPoint = ccp(0.5, 0);
        foreground1L.anchorPoint = ccp(0.5, 0);
        foreground1R.anchorPoint = ccp(0.5, 0);
        foreground2L.anchorPoint = ccp(0.5, 0);
        foreground2R.anchorPoint = ccp(0.5, 0);
        foreground3L.anchorPoint = ccp(0.5, 0);
        foreground3R.anchorPoint = ccp(0.5, 0);
        foreground4L.anchorPoint = ccp(0.5, 0);
        foreground4R.anchorPoint = ccp(0.5, 0);
        
        // 色合いの設定したのレイヤーは暗くする
        background.color = ccc3(180, 180, 180);
        foreground1L.color =ccc3(200, 200, 200);
        foreground1R.color =ccc3(200, 200, 200);
        foreground2L.color =ccc3(220, 220, 220);
        foreground2R.color =ccc3(220, 220, 220);
        foreground3L.color =ccc3(240, 240, 240);
        foreground3R.color =ccc3(240, 240, 240);

        CCParallaxNode* paraNode = [CCParallaxNode node];
        [self addChild:paraNode];

        CGSize winSize = [CCDirector sharedDirector].winSize;
        
        // 位置決めの基準点は画面中央下端
        CGPoint winCenterX = ccp(winSize.width/2.0, 0);
        [paraNode addChild:background z:0 parallaxRatio:ccp(0, 1) positionOffset:ccpAdd(winCenterX,ccp(0, 0))];
        [paraNode addChild:foreground1L z:1 parallaxRatio:ccp(0,1.2) positionOffset:ccpAdd(winCenterX,ccp(-150, 0))];
        [paraNode addChild:foreground1R z:1 parallaxRatio:ccp(0,1.2) positionOffset:ccpAdd(winCenterX, ccp(150, 0))];
        [paraNode addChild:foreground2L z:2 parallaxRatio:ccp(0,1.4) positionOffset:ccpAdd(winCenterX, ccp(-180, 0))];
        [paraNode addChild:foreground2R z:2 parallaxRatio:ccp(0,1.4) positionOffset:ccpAdd(winCenterX, ccp(180, 0))];
        [paraNode addChild:foreground3L z:3 parallaxRatio:ccp(0,1.6) positionOffset:ccpAdd(winCenterX, ccp(-210, 0))];
        [paraNode addChild:foreground3R z:3 parallaxRatio:ccp(0,1.6) positionOffset:ccpAdd(winCenterX, ccp(210, 0))];
        [paraNode addChild:foreground4L z:4 parallaxRatio:ccp(0,1.8) positionOffset:ccpAdd(winCenterX, ccp(-240, 0))];
        [paraNode addChild:foreground4R z:4 parallaxRatio:ccp(0,1.8) positionOffset:ccpAdd(winCenterX, ccp(240, 0))];
        id move = [CCMoveBy actionWithDuration:60 position:ccp(0, -1400)];
        [paraNode runAction:move];
    }
    return self;
}