Cocos StudioでのResourceパスに関するバグについて

木内智史之介(シャッチョー)
ミンカさんけっこんしてくださいおねがいします(ズザー SEGAさん、DIVAの筐体ください(ズザー

みなさん、Cocos Studio使っていますか?

最近、似たようなツールで「Cocos Creator」などを出してきたcocos2d-x勢ですが、 UI構築ツールとして、どちらを伸ばしていくつもりなんでしょうか?

このあたり、しっかり一つに絞って、「実用に耐えうるプロダクト」をどちらにしても世に送り出してほしいものです。

そんなCocos Studioの奇々怪々なバグに関して、今回紹介したいと思います。

画像が読み込めないよ!

「さて、ひとまず、背景だけ置いた適当なシーンをサクッと作成してみるか」
と意気揚々と作業を始めたんですよ。
けど、シーンをプレビューしてみても、ウンともスンとも言わない訳です。

画像を1枚、シーンに配置するだけの簡単な内容なものですから、

うわ~ヤダなァ~、気持ち悪いなァ~

なんて思うわけです。

まさか、ここから長い旅が始まるとは夢にも思わなかったんですね…。

画像が読み込めなかった構成

画像が読み込めなかった構成はこんな感じです。

RootSceneに画像を一枚置いているだけの構成で、なんも難しい事はしていません。
それでも、画像が表示されないんです。

問題が起きない構成

ちなみに、こんな構成なら問題は起きませんでした。

ちょwそしたらシーンやレイヤーファイルは全部rootに配置しろってことですか?

オラ、そんなCocos Studioいやだァ〜!

というわけで、なんとかこの画像の参照問題を解決したいと思います。

どうして画像が読み込めないのか?

どうして、root直下以外にシーンファイルを配置すると、パブリッシュした際に画像の参照関係が崩れるのでしょうか?
それを把握するためには、まず、パブリッシュされるシーンのjsonファイルを確認する必要性があります。

Scene/Root/RootScene.json

{
"ID": "af253853-3fe7-4be2-8d9a-b5615a70596a",
"Version": "3.10.0.0",
"Name": "RootScene",
"Content": {
"Content": {
"ObjectData": {
"Children": [
{
"FileData": {
"Type": "Normal",
"Path": "Scene/Root/image/UI_Board_backguound.png",
"Plist": ""
},
"Name": "UI_Board_backguound_1",
"ctype": "SpriteObjectData"
}
],
"Name": "Scene",
"ctype": "SingleNodeObjectData"
},
"UsedResources": [
"image/UI_Board_backguound.png"
],
}
},
"Type": "Scene"
}

※今回のバグと直接関係のない項目は取り除いて記載しています

うん、内容は、過不足なく必要な情報が揃っているように見えます。
じゃあ、このjsonデータを元に、使用リソースなどを読み込んだりするパーサ周りに関するバグだろうと目星を付けます。

パーサの確認

シーンファイルの解釈を行うパーサは timelineParser-2.x.js で、 実際パースを行うメソッドは、その中の parse メソッドです。

その中でパス関係の重要な決定をしている箇所のコードがこんな感じになっていました。

if(path !== undefined)
resourcePath = path;
else
// fileはjsonファイルのパスが渡ってきます
// res/Scene/Root/RootScene.json
resourcePath = this._dirname(file);

ここで決定される resourcePath に、先ほどのjsonデータの中の FileData.Path を繋げたパスが、参照リソースのURLとなります。

以上の情報を整理するとこんな変数内容になる

  • file:
    res/Scene/Root/RootScene.json
  • resourcePath:
    res/Scene/Root
  • 依存リソースのpath(FileData.Path):
    Scene/Root/image/UI_Board_backguound.png
  • 読み込むリソースのパス:
    res/Scene/Root/Scene/Root/image/UI_Board_backguound.png

おぉう??!
明らかに「読み込むリソースのパス」がおかしな事になってるじゃないか!

つまり、これって、

  • シーン.jsonでは、プロジェクトルートからのパスで記載しているのに
  • 読み込む時にはシーンファイルからのパスで読み込もうとしている

という事ですよね?
こりゃ、読み込めない訳だ…

どうしてこんな事になっちゃうの?

けど、どうしてこんな事になってるんでしょう?
素直に、プロジェクトルートからのパスとして解釈するように統一すればいいのに、と…。

原因は、これです。

// file = res/Scene/Root/RootScene.json
// resourcePath = res/Scene/Root
resourcePath = this._dirname(file);

ここで「res」という、cocos studioプロジェクトが直接把握できないパスを含んでいるため、そうせざるを得ない、という状況がありそうです。
つまり、手元にあるパス情報の中で、どこからがプロジェクト内のパスで、どこまでがプロジェクト外のパスなのか、判別不能、と。

せめて、Scene.jsonでシーン自身のプロジェクト内でのパス情報さえあれば、fileとの差分で割り出しもできるのに、含んでいないんですよね…。

このバグの直し方

というわけで、自分がおこなった修正はこんな感じです。

project.json

{
"resourceDir": "res",
}

project.jsonに、上記のように、パブリッシュ先のディレクトリを追加します。

timelineParser-2.x.js (parseメソッド内の一部)

var resourcePath;
if(path !== undefined)
{
resourcePath = path;
}
else if (cc.game.config && cc.game.config.resourceDir)
{
var dirs = (typeof cc.game.config.resourceDir == "string") ? [cc.game.config.resourceDir] : cc.game.config.resourceDir;
for (var i = 0; i < dirs.length; i++) {
var dir = dirs[i];
if (file.indexOf(dir) > -1) {
resourcePath = dir;
break;
}
}
if (! resourcePath) this._dirname(file);
}
else
{
resourcePath = this._dirname(file);
}
if (resourcePath.substr(-1) != "/") resourcePath = resourcePath + "/";

これで、シーンファイルをプロジェクトツリーのどこに配置しても、画像を正常にロードできるようになりました!

ではではノシ