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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "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 メソッドです。

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

1
2
3
4
5
6
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では、プロジェクトルートからのパスで記載しているのに
  • 読み込む時にはシーンファイルからのパスで読み込もうとしている

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

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

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

原因は、これです。

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

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

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

このバグの直し方

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

project.json

1
2
3
{
    "resourceDir": "res",
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 + "/";

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

ではではノシ