エンジニア

別々のレイヤーに存在するスプライトを連携して動かそう

投稿日:2013年11月15日 更新日:

今回のエンジニアブログを担当する大原です。
今回は最近、開発時に困った「別レイヤーにいるスプライトを連携させて座標配置を行う」処理について書きたいと思います。

スプライトやレイヤーなどのCCNodeから継承したクラスは、親ノードにaddChildして繋げて使うと思います。
しかし、それぞれのPotisionは、各親ノードからの相対座標となります。
そのため、違う繋がりのノードを連携して動かそうとすると、一度親までたどって連携させたいスプライトまでの移動座標を計算しないといけません。

cocos2d-xにはそれを簡単に行ってくれる関数があります。

以下にその関数を使ったコードを書きました。

まずは、各オブジェクトの宣言です。

// on "init" you need to initialize your instance  
bool HelloWorld::init()
{
    //////////////////////////////  
    // 1. super init first  
    if ( !CCLayer::init() )
    {
        return false;
    }

    CCSize WindowSize = CCDirector::sharedDirector()->getWinSize();
    CCPoint center = CCSizeMake(WindowSize.width * 0.5f, WindowSize.height * 0.5f);

    //動くスプライトを置く背景  
    m_BaseGround = CCLayerColor::create (ccc4(0xFF,0x3F,0x3F,0xFF), 400, 200);
    m_BaseGround->ignoreAnchorPointForPosition(false);
    m_BaseGround->setAnchorPoint(ccp(0.5f,0.5f));
    m_BaseGround->setPosition(ccpAdd(center,ccp(0,150)));
    this->addChild(this->m_BaseGround);

    //親分(動くスプライト)  
    m_OyabunSprite = CCSprite::create("Icon.png");
    m_OyabunSprite->setPosition(ccp(0,200));
    m_BaseGround->addChild(m_OyabunSprite);
    CCLabelTTF *OyabunLabel = CCLabelTTF::create("親分","ヒラギノ角ゴ ProN W3",32);
    m_OyabunSprite->addChild(OyabunLabel);

    //子分(動くスプライトにつられて動く)  
    m_kobunSprite = CCSprite::create("Icon-Small.png");
    this->addChild(m_kobunSprite);
    CCLabelTTF *kobunLabel = CCLabelTTF::create("スパイ","ヒラギノ角ゴ ProN W3",32);
    m_kobunSprite->addChild(kobunLabel);

    //親分に合わせて動くスパイを置く背景  
    m_SpyGround = CCLayerColor::create (ccc4(0x3F,0x3F,0xFF,0xFF), 400, 200);
    m_SpyGround->ignoreAnchorPointForPosition(false);
    m_SpyGround->setAnchorPoint(ccp(0.5f,0.5f));
    m_SpyGround->setPosition(ccpAdd(center,ccp(0,-150)));
    this->addChild(this->m_SpyGround);

    //スパイ(親に合わせて動く)  
    m_SpySprite = CCSprite::create("Icon-Small.png");
    m_SpyGround->addChild(m_SpySprite);
    CCLabelTTF *spyLabel = CCLabelTTF::create("スパイ","ヒラギノ角ゴ ProN W3",32);
    m_SpySprite->addChild(spyLabel);

    //スケジュールの設定(30分の1秒)  
    this->schedule(schedule_selector(HelloWorld::spriteMove),1/30);

    return true;
}

親分と子分が動くフィールドとスパイが動くフィールドを別として用意し、

画面->ベースフィールド->親分スプライト
画面->子分スプライト
画面->スパイフィールド->スパイスプライト

というかたちで繋げます。

以下が実際の移動ロジックになります。

/**  
 * 1秒間隔で呼ばれるスケジュール  
 */
void HelloWorld::spriteMove()
{
    //親の移動 5ドットずつ  
    this->m_OyabunSprite->setPosition(ccpAdd(m_OyabunSprite->getPosition(), ccp(5,-2.5)));
    //移動しすぎたら、戻してあげる  
    if(this->m_OyabunSprite->getPosition().x > m_BaseGround->getContentSize().width)
    {
        m_OyabunSprite->setPosition(ccp(0,200));
    }
    //ワールド座標に変換する  
    CCPoint worldPosition = m_OyabunSprite->getParent()->convertToWorldSpace(m_OyabunSprite->getPosition());

    //子の移動  
    this->m_kobunSprite->setPosition(ccpAdd(worldPosition,ccp(0,-100)));

    //スパイの移動  
    CCPoint localPosition = m_SpySprite->getParent()->convertToNodeSpace(ccpAdd(worldPosition,ccp(0,-300)));
    this->m_SpySprite->setPosition(localPosition);

}

convertToWorldSpaceを使うと各ノードからの相対座標から画面上の絶対座標が取得できます。
コレを使って画面上に追加された子分の座標を移動しています。
さらに、convertToWorldSpaceを使い一度変換された絶対座標から移動させたいスパイの相対座標を取得しています。

convertToWorldSpaceやconvertToWorldSpaceを使うときは移動させたいオブジェクトの親座標に対し関数を読んであげる必要があります。
実際に動かすと以下のように動くと思います。

blog2013_11_15_1

blog2013_11_15_2

blog2013_11_15_3

注意として、convertToWorldSpaceやconvertToNodeSpaceが行えるのは、各オブジェクトの座標情報が更新された後のタイミングです。
つまり、ハードウェアの画面回転イベントや、スリープイベントで割り込みで処理する場合は、まだcocos2d-x上での座標情報が更新されていないため、かえってくる値がおかしくなります。

採用情報

ワンダープラネットでは、一緒に働く仲間を幅広い職種で募集しております。

-エンジニア
-

© WonderPlanet Inc.