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

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

擬似ラスタスクロールを実現しよう!

はじめまして。今回のエンジニアブログを担当する大原です。
今回はこの業界にいたら、どこかで出会うエフェクト効果、
擬似ラスタスクロールの実現方法について、書きたいと思います。

■ラスタスクロールとは?

海の中にいるようなゆらぎや、2Dなのに奥行きがあるように表現するなど、
画像を走査線単位で加工する技術です。
はるか昔からある技術で、レースゲームで奥行きの表現や曲がっている道路の表現したり、
暗黒なトンネル、炎のゆらぎなど、多種多様な所で使われます。

■なぜ”擬似”ラスタスクロールなのか?

昔のテレビ画面では、画面に電子銃を当て、走査線上を順番に光が通ることで絵を写してました。
その原理を応用し、走査線毎に書き始めをずらしてあげる事で、波打った表現をしてたのが、本当のラスタスクロールなのです。
しかし、cocos2d-xでは、現在どの走査線を表示させているという情報を取ることができません。
むしろ今時のディスプレイは走査線単位で表示処理を行なっているのでしょうか・・・
そのため、同様の表現をするために擬似的に表現するしかないのです。

■cocos2d-xで擬似ラスタスクロールを実現してみよう

cocos2d-xのHelloWorldで使われている「HelloWorld.png」を横に
波打たせてみたいと思います。

以下が実装ソースになります。

bool HelloWorld::init()  
{  
    if ( !CCLayer::init() )  
    {  
        return false;  
    }  
  
    m_pai = 0;  
  
    // ラスタスクロール対象テクスチャ  
    m_pRasta = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png");  
    m_pRasta->retain();  
  
    // Lineを管理する配列クラス  
    m_pTextureLines = CCArray::create();  
    m_pTextureLines->retain();  
  
    //<*POINT*>テクスチャを千切りにする  
    for(int line = 0 ; line < m_pRasta->getPixelsHigh() ; line++)  
    {  
        //1ライン毎にテクスチャ分割する  
        m_pTextureLines->addObject(  
            CCSprite::createWithTexture(  
                m_pRasta,  
                CCRectMake(0, line ,  
                           m_pRasta->getPixelsWide(),1 )  
            )  
        );  
    }  
  
    //スプライトを親に追加する  
    for(int i = 0 ; i < m_pTextureLines->count() ; i++)  
    {  
        this->addChild( static_cast<CCSprite*>(m_pTextureLines->objectAtIndex(i)) );  
    }  
  
    //ラスタスクロール移動イベント  
    this->schedule( schedule_selector(HelloWorld::gameLogic), 1.0 / 60.0 );  
  
    return true;  
}  

<POINT>
「HelloWorld.png」を千切りのように、画像の横幅*縦1ピクセルの矩形をスプライトとしてたくさん保持します。
CCSprite::createWithTexture()でテクスチャ内でスプライトにしたい領域を指定することができます。

/*  
 * ラスタスクロールイベント  
 */  
void HelloWorld::gameLogic()  
{  
    //画面サイズの取得  
    CCSize size = CCDirector::sharedDirector()->getWinSize();  
  
    m_pai+=5;   //角度加算  
  
    //角度を360位内に収める  
    if(m_pai>360)  
    {  
        m_pai -= 360;  
    }  
  
    double position_x = size.width/2;  
    double position_y = size.height/2 + 160;  
    double wave_x = 15; //波の最大値  
    double rad;         //ラジアン角を保持  
    double add_x;       //算出される加算移動値  
  
    //<*POINT*>Line毎にX座標を設定する  
    for(int y_index = 0 ;y_index < m_pTextureLines->count(); y_index++ )  
    {  
        //1Line毎の角度  
        rad = (M_PI/180) * ( m_pai + y_index * 5 );  
        //1Line毎のX加算値  
        add_x = wave_x * cos(rad);  
        //1Lineスプライト取り出し  
  
        CCSprite *sprite = static_cast<CCSprite*>(m_pTextureLines->objectAtIndex(y_index));  
        //1Lineスプライトの配置決定  
        sprite->setPosition(ccp(add_x + position_x,position_y - y_index));  
    }  
  
}  

<POINT>Line毎に角度を増やす事で、少しずつ座標がずれていく形になります。
なぜラジアン角でX座標の加算値を割り出しているかというと、なめらかな波曲線にするためです。単純に1Line毎に1ピクセルずつ加算するとノコギリのようなギザギザな波形になってしまい、見た目上あまりかっこ良くないからです。

これで、このような波打ったアニメーションになります。
2013_07_17_rasta_scroll

擬似ラスタスクロールは、スプライトをたくさん使う事になってしまうため、機種によっては表現しきれないかもしれません。そのため、テクスチャを動的に変更できるのであれば、テクスチャを加工したほうが、よりよいでしょう。