ホーム < ゲームつくろー! < IKD備忘録

Cocos2d-x
ロゴ表示レイヤーを作ってみる

(2015. 1. 19)


 空っぽ環境で真っ黒なアプリ画面。まずここに出すはやっぱりロゴでしょう(^-^)。



@ ロゴレイヤー

 サンプルを見る限りですが、Cocos2d-xはシーン(Scene)の下にレイヤー(Layer)をぶら下げるのがセオリーのようです。HelloWorldでそんな事をしてましたし。右にならえで同じ事をしてみます。

 まずロゴ表示レイヤーであるLogoLayerクラスをcocos2d::Layerクラスから派生させましょう。で、次のようにヘッダーと.cppを実装します:

LogoLayer.h
#ifndef __IKD_COCOS_MYGAME_LOGOLAYER_H__
#define __IKD_COCOS_MYGAME_LOGOLAYER_H__


#include "cocos2d.h"

class LogoLayer : public cocos2d::Layer {
public:
    virtual bool init();

    CREATE_FUNC( LogoLayer );
};

#endif
LogoLayer.h
#include "LogoLayer.h"

USING_NS_CC;

bool LogoLayer::init() {

    return true;
}

とりあえず初期化メソッドであるinitメソッドを宣言し、生成用のメソッドを作るマクロであるCREATE_FUNCマクロでcreateメソッド等を作成します。.cppの方はinitメソッドを空実装しているだけです。

 シーンが開始したら最初にこのロゴレイヤーが再生されるように、WakeUpSceneFactoryクラス(前章参照)のcreateメソッド内にガリッと書きましょう:

WakeUpSceneFactory::createメソッド
cocos2d::Scene* WakeUpSceneFactory::create() {

    auto scene = Scene::create();

    LogoLayer *logoLayer = LogoLayer::create();
    scene->addChild( logoLayer, 1, "logoLayer" );

    return scene;
}

Layerはcreateメソッドで作ります。そうしないとシーン再生時にinitメソッドが呼ばれなくなりますし、自動削除が働かないのでメモリリークにもなります。作成したレイヤーをシーンに登録(addChild)してます。名前も付けておきました。

 これでinitメソッド内にブレークポイントを付けてアプリを実行すると、initメソッドが呼ばれる事が確認できます。



A ロゴをただ表示

 実際のロゴ表示はフェードイン/アウトなど凝った事をするわけですが、まずは単純に用意したロゴの絵を画面中央に表示する叩き台な実装をしてみましょう。絵を表示するだけならばSpriteクラスを使うのが楽です。LogoLayer::initメソッド内でSpriteオブジェクトを作り、そのままLayerに登録します:

LogLayer::initメソッド
bool LogoLayer::init() {

    auto sprite = Sprite::create( "logo.png" );

    this->addChild( sprite, 0, "logo" );

    return true;
}

この状態でアプリを実行してみましょう:

お!左下に出ました。Cocos2d-xの原点は画面の左下にあるようです。また描画するスプライトの原点は絵の中心のようです。このロゴを画面の真ん中に置くには、画面の縦横のサイズの半分の位置にずらせば良いと言うことです。で、画面サイズはDirectorクラスのgetVisibleSizeメソッドで取得できます:

LogoLayer::initメソッド
bool LogoLayer::init() {

    Size display = Director::getInstance()->getVisibleSize();

    auto sprite = Sprite::create( "logo.png" );
    sprite->setPosition( display.width / 2, display.height / 2 );

    this->addChild( sprite, 0, "logo" );

    return true;
}

DirectorはシングルトンなのでgetInstanceメソッドでインスタンスを取得しgetVisibleSizeメソッドを呼び出すと画面のサイズを取得できます。その半分のサイズをスプライトに設定すれば画面の中央にロゴが出ます:



B フェードイン・アウトがやっぱいいよね

 ロゴの表示は「ぱっ!」といきなり表示する事もありますが、フェードイン・アウトでふわ〜んと表示させると雰囲気が出ます。フェードをさせると言う事はロゴのα値を時間をかけて上げ下げするという事です。スプライトのα値はsetOpacityメソッドで指定できます:

LogoLayer::initメソッド
bool LogoLayer::init() {

    Size display = Director::getInstance()->getVisibleSize();

    auto sprite = Sprite::create( "logo.png" );
    sprite->setPosition( display.width / 2, display.height / 2 );

    sprite->setOpacity( 64 );

    this->addChild( sprite, 0, "logo" );

    return true;
}

これだけで半透明なスプライト描画になります:

さて、こうなると後はこの透過度を時間と共に上げ下げする仕組みです。レイヤー内に毎フレーム呼び出されるメソッドがあればそこで値を変えて透過度を設定し直せば実現できます。んーと、あーあった、Nodeクラスのupdateメソッドです。LayerクラスはNodeクラスを継承しているのでこのメソッドをオーバーライドすれば毎フレーム呼ばれるはずです:

LogoLayer.h
class LogoLayer : public cocos2d::Layer {

    cocos2d::Sprite *logo;

public:

    virtual bool init();
    virtual void update( float delta );

    CREATE_FUNC( LogoLayer );
};
LogoLayer.h
bool LogoLayer::init() {

    Size display = Director::getInstance()->getVisibleSize();

    logo = Sprite::create( "logo.png" );
    logo->setPosition( display.width / 2, display.height / 2 );

    this->addChild( logo, 0, "logo" );

    return true;
}

void LogoLayer::update( float delta ) {

    logo->setOpacity( 64 );
}

よーし実行…って、あれ〜updateメソッドが呼ばれません…なんで…?

 なるほど…どうやらLayerのupdateメソッドが呼ばれるようになるには「Layerの初期化」と「スケジューラへの登録」が必要のようです。まず、initメソッド内でLayer::initメソッドを呼びます。次に自身のscheduleUpdateメソッドを呼ぶとスケジューラに自分自身が登録され、updateメソッドが毎フレーム呼ばれるようになります:

LogoLayer.h
bool LogoLayer::init() {

    if ( Layer::init() == false )
        return false;

    this->scheduleUpdate();

    Size display = Director::getInstance()->getVisibleSize();

    logo = Sprite::create( "logo.png" );
    logo->setPosition( display.width / 2, display.height / 2 );

    this->addChild( logo, 0, "logo" );

    return true;
}

void LogoLayer::update( float delta ) {

    logo->setOpacity( 64 );
}

これで改めてupdateメソッドにブレークポイントを置いてみると、今度はちゃんと呼ばれていました。



C ロゴフェードイン・アウト

 あまり綺麗な方法ではありませんが、ロゴをフェードイン・アウトさせるコードを以下に示します:

LogoLayer.h
#ifndef __IKD_COCOS_MYGAME_LOGOLAYER_H__
#define __IKD_COCOS_MYGAME_LOGOLAYER_H__


#include "cocos2d.h"

class LogoLayer : public cocos2d::Layer {

    typedef void (LogoLayer::*State)( float delta );

    cocos2d::Sprite *logo_;
    float curOpacity_;
    float curTime_;

    State state;

    void fadeIn( float delta );
    void wait( float delta );
    void fadeOut( float delta );
    void end( float delta );

public:

    LogoLayer();
    virtual ~LogoLayer();
    virtual bool init();
    virtual void update( float delta );

    CREATE_FUNC( LogoLayer );
};

#endif

LogoLayer.cpp
#include "LogoLayer.h"

USING_NS_CC;

LogoLayer::LogoLayer() : logo_(), curOpacity_(), curTime_(){
}

LogoLayer::~LogoLayer() {
}

bool LogoLayer::init() {

    if ( Layer::init() == false )
        return false;

    this->scheduleUpdate();

    Size display = Director::getInstance()->getVisibleSize();

    logo_ = Sprite::create( "logo.png" );
    logo_->setPosition( display.width / 2, display.height / 2 );
    logo_->setOpacity( 0.0f );

    this->addChild( logo_, 0, "logo" );

    state = &LogoLayer::fadeIn;

    return true;
}

void LogoLayer::update( float delta ) {
    (this->*state)(delta );
    logo_->setOpacity( 255 * curOpacity_ );
}

void LogoLayer::fadeIn( float delta ) {

    const float fadeInTime = 1.0f;

    curTime_ += delta;

    if ( curTime_ <= fadeInTime ) {
        curOpacity_ = curTime_ / fadeInTime;
        return;
    }

    curOpacity_ = 1.0f;
    curTime_ = 0;
    state = &LogoLayer::wait;
}

void LogoLayer::wait( float delta ) {

    const float waitTime = 1.5f;

    curTime_ += delta;

    if ( curTime_ >= waitTime ) {
        curTime_ = 0.0f;
        state = &LogoLayer::fadeOut;
    }
}

void LogoLayer::fadeOut( float delta ) {

    const float fadeOutTime = 1.0f;

    curTime_ += delta;

    if ( curTime_ <= fadeOutTime ) {
        curOpacity_ = 1.0f - curTime_ / fadeOutTime;
        return;
    }

    curOpacity_ = 0.0f;
    curTime_ = 0;
    state = &LogoLayer::end;
}

void LogoLayer::end( float delta ) {
}

クラスのメソッドで簡単な状態遷移を起こしています。1秒かけてフェードインし、1.5秒待って、また1秒かけてフェードアウトさせています。この辺りはCocos2d-xにうまい機構がありそうですが、それはまた追々見つけていきます。