WonderPlanet DEVELOPER BLOG

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

【Cocos2d-x】ビルドなしでリソース更新しよう!

こんにちは、デザイナーの上松です。
ちょっと前から、ゲームの画像などの「リソース」を反映させる時に問題が起きていました。
Xcodeのキャッシュですぐに更新してくれなかったり、プログラマーさんを介さないと画像が反映できない点です。
今日紹介するアイディアを使えば、

・確実にリソースを反映できる
・ビルドしなくても(実行中でも)差し替え反映できる
・操作を覚えればグラフィッカーやデザイナが開発ソフトを入れなくても更新できる(!)

この3つのことができるようになります!

ファイルの読み込み先を変える

Cocos2d-xでは、ゲームのリソースを、CCFileUtilにあるSearchPathに設定されているフォルダからとってくるようになっています。
また、最初の状態はアプリ内のResourcesフォルダが設定されています。

このSearchPathは配列(ベクタ)になっており、先頭のフォルダから順に検索していき、なければエラーを返すような仕組みになっているようです。

ということで、このSearchPathの先頭に、任意のフォルダを指定すれば良さそうな感じがします。

データの書き込み先

読み込み先の変え方はわかりました。
つぎに、どこへデータを書き込むのかを考えましょう。
以下の3つのフォルダのどれかになります。

・Documentsフォルダ
・Library/Cacheフォルダ
・tmpフォルダ

基本的にデバッグ用ですのでどこでもOKです。
今回はgetWritablePathで簡単に取得できる、Documentsを使用しましょう。

データを書き込む

iOS端末の場合

iOS端末へ読み書きできるソフトを使うと便利です。
iFunBoxや、iExplorer等です。
有料版をお持ちでしたらiExplorerのマウント機能が私のおすすめです! 外付けディスクのようにFinderから書き込むことができます。
そうでなければ、iFunBoxをおすすめします。
どちらのソフトも、フォルダをドラッグアンドドロップして書き込むことができます。

iOSシミュレータの場合

あなたの現在のプロジェクトの、AppDelegate::applicationDidFinishLaunchingの中で、下のコードを動かして、パスを取得しましょう。

CCLOG("%s", CCFileUtils::sharedFileUtils()->getWritablePath().c_str());  

私の場合は、こんな感じで出力されました。

/Users//Library/Application Support/iPhone Simulator/7.1-64/Applications//Documents/

このフォルダへFinderで移動して、書き込みます。
Macの中のフォルダなのでソフトを使わなくても書き込めます。
rsyncで同期をとるようにするとより便利です。

実際に読み出してみる

AppDelegate::applicationDidFinishLaunchingで、シーンが作られる前に、下のようなコードを書いておきます。

bool AppDelegate::applicationDidFinishLaunching()  
{  
    // initialize director  
    CCDirector *pDirector = CCDirector::sharedDirector();  
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());  
  
    // turn on display FPS  
    pDirector->setDisplayStats(true);  
  
    // set FPS. the default value is 1.0/60 if you don't call this  
    pDirector->setAnimationInterval(1.0 / 60);  
  
    /* 即時プレビューができる仕込み */  
    // サーチパスの取得  
    vector paths = CCFileUtils::sharedFileUtils()->getSearchPaths();  
  
    // イテレータ生成  
    vector::iterator it = paths.begin();  
  
    // パスを挿入  
    paths.insert(it, CCFileUtils::sharedFileUtils()->getWritablePath() );  
    CCLOG("Path is here: %s", CCFileUtils::sharedFileUtils()->getWritablePath().c_str() );  
  
    // パスを反映  
    CCFileUtils::sharedFileUtils()->setSearchPaths( paths );  
    /* 仕込み終わり */  
  
    // create a scene. it's an autorelease object  
    CCScene *pScene = HelloWorld::scene();  
  
    // run  
    pDirector->runWithScene(pScene);  
  
    return true;  
}  

vectorとstringのincludeと、using namespace stdをお忘れなく!

この状態で起動します。
おなじみのHelloWorldシーンです。

1

ここで、以下のように色を変更したココスくんの画像を、Documents内に保存してみます。

2

アプリを終了して立ち上げ直すと・・・

3

反映されましたね!これで、ビルドやクリーンをしなくてもリソースだけ転送できるようになりました。

もっとリアルタイムにしたい

ここまででは、毎回起動し直さなければいけません。
動かしながら反映できればもっと便利そうです。

シーンに更新機能をつける

あくまでデバッグ機能ですので、シーンを再生成する方法で実現しましょう。

・画像などのキャッシュのクリア
・現在のシーンの新しいインスタンスを作る
・初期化する
・現在のシーンと差し替える

コードにするとこんな感じでしょうか。
電源ボタンのアクションで、アプリを終了する代わりに下記の処理を入れてみます。

//テクスチャ類のキャッシュ  
CCTextureCache::sharedTextureCache()->removeAllTextures();  
  
//自身の新しいシーンを生成  
CCScene* newScene = HelloWorld::scene();  
  
//新しいシーンの設定する  
//newScene->hoge( init_dict )  
  
//シーンを入れ替える  
CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(0.6f, newScene));  

メニューボタンを押すとフェードアウト/インして、更新されるようになりました。
ボタン画像をエリザベスにしても問題なく動きます。
また、調整も、直接Photoshopから書き込みできるためさくさくです!
(シミュレーターやマウントした場合です)

4

5

ただ、この機能をすべてのシーンにつけるのは大変そうです。
なので、よく使うメニューや、辿り着きにくいシーンなどに限定して実装したり、最初からデバッグ用シーンを作ってしまう方が良いのではと思います。

まとめ

これで、SearchPathに書き込みできるフォルダを追加して、ビルドなしでリソース反映ができるようになりました。
CCBIファイルを使った画面作りや、Luaを使ってプログラムを書いている場合は、もっと相性が良さそうですね。