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

Cocos2d-x
ロゴからタイトルシーンへ

(2015. 1. 28)


 前章でロゴ表示っぽい事ができました。ロゴが表示し終わったらコールバックも呼べてます。つまりロゴシーンの終了を取れていると言う事です。ロゴの次といえばタイトル画面が定番ですよね。ロゴからタイトルへのシーン遷移。Cocos2d-xではかなり簡単にできるようです。



@ Director::replaceSceneメソッド

 シーンを遷移させるにはDirector::replaceSceneメソッドを使います。シーンのどこででも良いのでこれを呼ぶだけ。簡単です。

Director::replaceSceneメソッド
/** Replaces the running scene with a new one. The running scene is terminated.
* ONLY call it if there is a running scene.
*/
void replaceScene(Scene *scene);

「現在動いているシーンを別のシーンに置き換えます。動いているシーンは終了します。現在動いているシーンがある時だけ呼んで下さい」というコメントがあります。引数はSceneクラスのオブジェクトポインタです。んではテストする為に仮のタイトルシーンクラスを作りましょう。



A タイトルシーン

 クラス自体は非常に単純:

タイトルシーン
class TitleScene {
public:
    static cocos2d::Scene* create();
};

Sceneさへ返せばいいわけです。で、どんなタイトルにしようかなぁ〜っというのは「どんなゲームにしようかなぁ」というお話に近い物があります(^-^;。なので、仮のゲームを考えてみました。こんな感じのタイトル画面にしようと思います:

「○×Evolution」という架空のゲーム名のタイトルを作ります。上のタイトル画面には、

これらのリソースがあります。

 まず背景。背景はもちろん画面サイズにピッタリ納める縦横ピクセルです。…お?そういえばCocos2d-xでの「画面サイズ」というのはどういう扱いになっているのでしょうか?昨今のゲームは色々な解像度がありますから、マルチに対応させようと思ったらこの辺りはしっかりと定めておく必要があります。

 調べてみると、Cocos2d-xでは「デザイン解像度」という仮想解像度を設定するようです:

cocos2d::Scene* WakeUpSceneFactory::create() {

    auto view = Director::getInstance()->getOpenGLView();
    view->setDesignResolutionSize( 960, 540, ResolutionPolicy::SHOW_ALL );

    auto scene = Scene::create();

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

    return scene;
}

デザイン解像度というのは、ゲーム制作上で想定する画面の大きさです。iPhoneとかAndroidなどは色々な解像度を持っています。全部に対応させようとするととっても大変。そこで「仮想的な解像度」でもってゲームを作り、実際の画面はそれを拡縮させて表示させようというのがデザイン解像度の意図です。

 上のようにDirectorさんのgetOpenGLViewメソッドで画面(CCEGLView)オブジェクトを得て、そのsetDesignResolutionSizeメソッドで仮想的な画面のサイズを決めます。今回は16:9な画面を想定する事にしました。第3引数にある「ResolutionPolicy::SHOW_ALL」というのは「縦横比を保ちつつ一番大きくなるように拡縮しますよ」というフラグです。他の宣言については「ResolutionPolicy列挙クラス」にあります。

 これをどこで設定するかですが、ゲームが始まってロゴ等の最初の画像が出る前がベストでしょう。上の自前クラスだとここですが、Cocos2d-xのデフォルトプロジェクトを使っているならAppDelegate::applicationDidFinishLaunchingメソッドでシーンを作る直前が良いと思います。



B MenuItemImageクラスでボタンの挙動を作る

 タイトルロゴと背景については前章のロゴと同じ出し方や制御が出来ます。一方、ボタンには「何もしていない時」「選択している最中」「決定した時」「使えない時」などいくつか状態が存在します。その為「通常時」「選択時」「決定時」「未使用時」などの絵が必要になります:

この手のGUIは世の中にたっくさん落ちていますし、素材屋さんが有償/無償で公開してくれいたりもします。ただ、適当に選ぶのでは無くて「作るゲームの雰囲気に沿ったリソース」ですべてをちゃんと統一するのが大切です。辛抱強く素材を探す、能力があれば自分で作る、難しいなら共同制作してくれるデザイナさんを頑張って探して依頼するなどして統一感のあるGUIにして下さい。

 さて、ボタンはゲームで沢山出てきます。ですから簡単に画面に設置できるようにすると便利ですよね。Cocos2d-xではボタンのような各種GUIを表すMenuItemというクラス群があり、その中のMenuItemImageクラスは「通常時」「選択時」「未使用時」の3態を表現できます。このクラスを使うとボタンを表現できます。

 まずMenuItemImageオブジェクトを作る時に状態を表す絵のファイル名を渡します:

// スタートボタン
auto startBtn = MenuItemImage::create(
    "button_normal.png",
    "button_select.png",
    "button_disable.png",
    CC_CALLBACK_1( TitleLayer::startBtnCallback, this )
);

startBtn->setPosition( display.width / 2, 200 );

MenuItemImage::createメソッドには幾つか引数が違う物がありますが、上のは「通常」「選択」「未使用」の絵を全部使う場合です。第4引数にはボタンが押された時に呼ばれるコールバックメソッドをCC_CALLBACK_1マクロで引き渡します。第1引数にクラス内のメソッド、第2引数に自分自身を渡しています。

 こうして作成したボタン(MenuItemImageオブジェクト)はMenuオブジェクトに登録します。メニューは次のように作ります:

auto menu = Menu::create();
menu->setPosition( Vec2::ZERO );
this->addChild( menu, 1 );

Menu::createメソッドでメニューを作成してレイヤーに登録しています(thisはLayerクラスのオブジェクト)。メニューは幾つかのMenuItemをまとめる人です。上のmenuオブジェクトにstartBtnを登録するには:

menu->addChild( startBtn );

とします。



C タイトルシーンの実装

 タイトル画面に背景、タイトルロゴ、ボタン(Start、Option)を並べたタイトルシーンの実装はこんな感じになりました:

#include "TitleLayer.h"


USING_NS_CC;

bool TitleLayer::init() {

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

    this->scheduleUpdate();

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

    // タイトルロゴ
    logo_ = Sprite::create( "title_logo.png" );
    logo_->setPosition( display.width / 2, 370 );
    logo_->setOpacity( 0.0f );
    logo_->setScale( 0.9f );

    CCSequence *seq = CCSequence::create(
        CCFadeIn::create( 0.5f ),
        NULL
    );
    logo_->runAction( seq );
    logo_->runAction( CCScaleTo::create( 0.5f, 1.0f ) );

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

    // 背景
    bg_ = Sprite::create( "title_bg.png" );
    bg_->setPosition( display.width / 2, display.height / 2 );
    this->addChild( bg_, 0, "title_bg" );

    // メニュー
    auto menu = Menu::create();
    menu->setPosition( Vec2::ZERO );
    this->addChild( menu, 1 );

    // スタートボタン
    auto startBtn = MenuItemImage::create(
        "button_normal.png",
        "button_select.png",
        "button_disable.png",
       CC_CALLBACK_1( TitleLayer::startBtnCallback, this )
    );
    startBtn->setPosition( display.width / 2, 200 );
    menu->addChild( startBtn );

    // オプションボタン
    auto optionBtn = MenuItemImage::create(
        "button_normal.png",
        "button_select.png",
        "button_disable.png",
        CC_CALLBACK_1( TitleLayer::optionBtnCallback, this )
    );
    optionBtn->setPosition( display.width / 2, 130 );
    menu->addChild( optionBtn );

    return true;
}

// スタートボタンが押された時のコールバック
void TitleLayer::startBtnCallback( Ref* pSender ) {
}

// オプションボタンが押された時のコールバック
void TitleLayer::optionBtnCallback( Ref* pSender ) {
}

 とりあえずこれでタイトル的な挙動は出来ました。ただ遷移はしません(コードに書いてないから)。タイトルでスタートボタンが押されたらゲームを開始させます。ゲーム用のシーンを作って遷移させれば良いだけです。本当は色々リファクタリングもしたいのですが、今は勢いに任せてさっさとゲーム本体部分の作成をしてしまいましょう。