CocosCreatorでマップにオブジェクトを配置する

# はじめに
[前回の記事](https://qiita.com/raichi/items/f4fa68d155e73105fd8b)では、TiledMapEditorのマップを表示する方法を紹介しました。
今回は、TiledMapEditorのオブジェクトをCocosCreatorで活用する方法を紹介します。

<font color="red">2018.11.19 追記
CocosCreator 2系におけるtsxファイルの読み込み方法を追記。</font>

### ■オブジェクトとは
敵キャラやアイテムなど、マップ上に配置するオブジェクトを指します。

実際の使い方として、
　・TiledMapEditorで敵キャラの配置位置を定義
　・CocosCreator上でその位置に敵キャラを配置する
これによって、アクションゲームやシューティングゲームのように、マップ上の好きな位置に敵キャラを出現させられるようになります。


### ■動作環境
Windows10
CocosCreator 1.8.1
TiledMapEditor 1.1.2

# 今回目指す内容
前回の記事で作成したマップ上に、卵と目玉焼きのオブジェクトを配置させてみます。

![map.png](https://qiita-image-store.s3.amazonaws.com/0/94561/0e338e25-dffc-a2dc-b479-ae7e95503bbd.png)

# オブジェクト配置位置の設定（TiledMapEditor側）
前回作成したマップにオブジェクトレイヤーを作成します。
TiledMapEditorを起動し、前回作成したmap.tmxを読み込みます。
「レイヤー」パネル上で右クリックし、New→ObjectLayerの順番で選択します。
![tiled01.png](https://qiita-image-store.s3.amazonaws.com/0/94561/bd9d32e2-b9e8-c2c1-5362-0f76baef1319.png)

作成したオブジェクトレイヤーの名前に「object1」と設定し、object1が一番上になるように並べます。
![tiled02.PNG](https://qiita-image-store.s3.amazonaws.com/0/94561/ddfbec9e-a6cb-3c84-352c-bddee9773828.png)

object1を選択したまま、「オブジェクト」パネルに切り替え、以下の要領でオブジェクトの配置位置を定義します。
①「四角形を追加」ボタンをクリック
②配置位置を１マス分囲む
③名前に「egg」（卵）または「fried」（目玉焼き）を設定

ここでは例として、卵×3個、目玉焼き×2個を配置しました。
![tiled03.png](https://qiita-image-store.s3.amazonaws.com/0/94561/0de672dd-b5ae-c752-1d72-412214e7b813.png)

map.tmxを保存します。
map.tmxをテキストエディタで開くと、objectgroup要素と、その子要素である5個のobject要素が含まれています。

```xml:map.tmx
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" tiledversion="1.1.2" orientation="orthogonal" renderorder="right-up" width="16" height="16" tilewidth="48" tileheight="48" infinite="0" nextobjectid="6">
 <tileset firstgid="1" source="mapchip1.tsx"/>
 <tileset firstgid="4" source="mapchip2.tsx"/>
 <layer name="background" width="16" height="16">
  <data encoding="base64">
   AQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAgAAAAIAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAwAAAAMAAAADAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAADAAAAAwAAAAMAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAACAAAAAQAAAAEAAAABAAAAAQAAAAEAAAADAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAQAAAAEAAAADAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAEAAAADAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAQAAAAEAAAABAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAQAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAEAAAADAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAQAAAAEAAAADAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAMAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAIAAAABAAAAAQAAAAEAAAADAAAAAwAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAQAAAAMAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAgAAAAEAAAABAAAAAQAAAAEAAAADAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAA==
  </data>
 </layer>
 <layer name="foreground" width="16" height="16">
  <data encoding="base64">
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
  </data>
 </layer>
 <objectgroup name="object1">
  <object id="1" name="egg" x="240" y="192" width="48" height="48"/>
  <object id="2" name="egg" x="192" y="336" width="48" height="48"/>
  <object id="3" name="egg" x="384" y="96" width="48" height="48"/>
  <object id="4" name="fried" x="528" y="336" width="48" height="48"/>
  <object id="5" name="fried" x="480" y="480" width="48" height="48"/>
 </objectgroup>
</map>
```

# オブジェクトの準備（CocosCreator側）

卵と目玉焼きのオブジェクトは以下の内容で準備します。

* 1枚の画像（スプライトシート）から卵と目玉焼きのスプライトを作成
* スプライトはプレハブ化し、JavaScriptからインスタンス化しやすくする

## スプライトシートの登録
卵と目玉焼きのオブジェクトの元となるスプライトシートをCocosCreatorに登録します。
assets配下にimagesフォルダを作成し、以下のegg.pngとegg.plistの2つのファイルをimagesフォルダにドラッグアンドドロップで格納します。

egg.png
![egg.png](https://qiita-image-store.s3.amazonaws.com/0/94561/a2271304-44b4-8af1-759d-1939a006d96e.png)

```xml:egg.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>frames</key>
        <dict>
            <key>egg.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{48,48}</string>
                <key>spriteSourceSize</key>
                <string>{48,48}</string>
                <key>textureRect</key>
                <string>{{0,0},{48,48}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
            <key>fried.png</key>
            <dict>
                <key>aliases</key>
                <array/>
                <key>spriteOffset</key>
                <string>{0,0}</string>
                <key>spriteSize</key>
                <string>{48,48}</string>
                <key>spriteSourceSize</key>
                <string>{48,48}</string>
                <key>textureRect</key>
                <string>{{48,0},{48,48}}</string>
                <key>textureRotated</key>
                <false/>
            </dict>
        </dict>
        <key>metadata</key>
        <dict>
            <key>format</key>
            <integer>3</integer>
            <key>pixelFormat</key>
            <string>RGBA8888</string>
            <key>premultiplyAlpha</key>
            <false/>
            <key>realTextureFileName</key>
            <string>egg.png</string>
            <key>size</key>
            <string>{96,48}</string>
            <key>smartupdate</key>
            <string>$TexturePacker:SmartUpdate:f24dd16a36cb967ab31283c90d88cb9b:7e6eb2f500b8f853a3009c2536f4fe55:59d5ac319192a000c02d94f2d55cf236$</string>
            <key>textureFileName</key>
            <string>egg.png</string>
        </dict>
    </dict>
</plist>
```

assets内はこのようになります。
![assets2.PNG](https://qiita-image-store.s3.amazonaws.com/0/94561/27fdb0d1-b02f-280b-8fbb-969d9144d3b1.png)

## プレハブの作成

以下の要領でスプライトを作成し、プレハブ化します。

①Assetsパネル内のeggをNode Treeパネルにドラッグアンドドロップ
②Node Treeのeggを選択し、Anchor X:0 Y:0を設定
![cocos01.png](https://qiita-image-store.s3.amazonaws.com/0/94561/53e74d54-b8b2-aa64-6957-e7ca1e34e79f.png)

③assets配下にprefabsフォルダを作成し、Node Treeのeggをprefabsフォルダにドラッグアンドドロップ
④ドラッグアンドドロップしたら、Node Treeのeggは不要となるため削除します。
![cocos02.png](https://qiita-image-store.s3.amazonaws.com/0/94561/7bf64634-9bc7-7011-c716-224648af5041.png)

※friedも同様の手順でプレハブ化します。

## プレハブの使用準備
map.js上からプレハブを使用できるよう、map.jsにpropertiesの設定を追記します。

```javascript:map.js
/* global cc */
cc.Class({
    extends: cc.Component,

    properties: {
        EggPrefab: {
            default: null,
            type: cc.Prefab
        },
        FriedPrefab: {
            default: null,
            type: cc.Prefab
        },
    },

    :
```

Node Treeパネルのmapノードを選択するとプロパティパネル状にEggPrefabとFriedPrefabの設定項目が現れるので、それぞれAssetsパネル内のeggプレハブとfriedプレハブをドラッグアンドドロップして設定します。
![cocos03.png](https://qiita-image-store.s3.amazonaws.com/0/94561/0effa9f1-c476-e084-8ce4-fcb412a92954.png)

## オブジェクトの配置
プレハブの準備が整ったので、TiledMapEditorで設定した位置にオブジェクトとしてプレハブを配置します。
前回作成したmap.jsに処理を追記します。
（loadmap関数内の★★★でマークした処理、およびspawnObjects関数すべてを追記）

```javascript:map.js
/* global cc */
cc.Class({
    extends: cc.Component,

    properties: {
        EggPrefab: {
            default: null,
            type: cc.Prefab
        },
        FriedPrefab: {
            default: null,
            type: cc.Prefab
        },
    },

    // use this for initialization
    onLoad: function () {
        this.loadMap();
    },
    
    //
    // Mapを読み込む
    //
    loadMap: function () {
        var self = this;
        cc.loader.load(cc.url.raw("resources/mapchip1.tsx"), function (err, result) {
            cc.loader.load(cc.url.raw("resources/mapchip2.tsx"), function (err, result) {
                cc.loader.loadRes("map", function (err, map) {
                    var component = self.node.addComponent(cc.TiledMap);
                    component.tmxAsset = map;
                    
                    // マップ読み込み完了後、看板の座標のcategoryプロパティ値を出力
                    cc.log("category: ", self.getCategory(6, 7));

                    // ★★★オブジェクトを表示させない★★★
                    component.getObjectGroup("object1").enabled = false;

                    // ★★★オブジェクトの一覧を出力する★★★
                    self.spawnObjects();
                });
            });
        });
    },
    
    //
    // 指定した座標のcategory値を返す
    //
    getCategory: function (x, y) {
        var layer = this.node.getComponent(cc.TiledMap).getLayer("foreground");
        var gid = layer.getTileGIDAt(cc.p(x, y));
        var properties = this.node.getComponent(cc.TiledMap).getPropertiesForGID(gid);
        
        if (properties === undefined || typeof properties == "number") return 0;
        return properties.category;
    },

    //
    // ★★★マップ上にオブジェクトを配置する★★★
    //
    spawnObjects: function () {
        // オブジェクト群を取得する
        var objects = this.node.getComponent(cc.TiledMap).getObjectGroup("object1").getObjects();

        // オブジェクト名とプレハブ名の対応表を作成する
        var nameMap = { egg: this.EggPrefab, fried: this.FriedPrefab };

        // オブジェクトを生成して配置する
        for (var object of objects) {
            var prefab = cc.instantiate(nameMap[object.name]);
            prefab.x = object.offset.x;
            prefab.y = this.node.height - prefab.height - object.offset.y;
            this.node.addChild(prefab);
        }
    },
});
```

<font color="red">CocosCreator 2系はtsxファイルの読み込み方法が変更となったため、cc.loader.loadを実行している箇所は以下に読み替えてください。</font>

```
        cc.loader.loadRes("mapchip1", function (err, tsx) {
            cc.loader.loadRes("mapchip2", function (err, tsx) {
```

object1は非表示にしないと、以下のように四角形の細い線が表示されてしまうようです。
![cocos04.PNG](https://qiita-image-store.s3.amazonaws.com/0/94561/121f7682-7870-0d94-ba04-96404dd098f1.png)

以下の設定によって、object1を非表示にします。

```javascript
// ★★★オブジェクトを表示させない★★★
component.getObjectGroup("object1").enabled = false;
```

spawnObjects()関数では、object1のオブジェクト群を取得し、オブジェクトの位置にプレハブを配置しています。
注意点としては、TiledMapEditorでは原点(0, 0)が左上なのに対し、CocosCreatorは原点(0,0)が左下であるため、Y座標の値を変換する必要があります。

CocosCreatorを実行し、下の画面のように卵と目玉焼きが表示されれば成功です。
![map.png](https://qiita-image-store.s3.amazonaws.com/0/94561/238dffa9-9c2d-3df2-d461-673b56fed461.png)