56
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

【Unity】Tiled Map Editor で作成したデータを読み込む

001.png

高機能なレベルエディタ「Tiled Map Editor」で作成したデータを読み込む方法を紹介したいと思います。

1. Tiled Map Editor の出力設定

まずは出力設定を変更します。
メニューから「Preferences ...」を選択して、設定画面を開きます。
002.png
そして、レイヤーデータの保持方法を「CSV」に変更します。こうすると読み込み処理が簡単になるためです。

2. 拡張子を変更する

Tiled Map Editorで作成したデータは拡張子が「.tmx」となりますが、それを「.xml」に変更します。理由はUnityのテキストアセットは拡張子で判定をしているため、「.tmx」がテキストアセットと認識されません。そのためこの変更が必要となります。

※2014/1/14追記
AssetPostProcessorを使用して自動コピーを行うと、拡張時を*.tmxのまま運用することが可能です。詳細は「6. アセットの自動コピー」で説明しています。

3. プロジェクトへの登録

003.png
Projectビューに XMLファイル(Tiled Map Editorで作成したデータ)を登録します。Resources.Load()を使って読み込むので、「Resources」フォルダの下に配置する必要があります。ただここでは「Resources/Levels/test2」として配置しました。

なお読み込むレベルデータは以下のものとなります。

test2.xml
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="20" height="15" tilewidth="32" tileheight="32">
 <tileset firstgid="1" name="enemy" tilewidth="32" tileheight="32">
  <image source="enemy.bmp" trans="00ff00" width="512" height="128"/>
 </tileset>
 <layer name="タイル・レイヤー1" width="20" height="15">
  <data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,0,0,0,2,2,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,2,0,2,2,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
 </layer>
</map>

4. 読み込み処理

以下のスクリプトでレベルデータを読み込むことができます。

TMLLoader.cs
using UnityEngine;
using System.Collections;
using System.Text;
using System.Xml;
using System.IO;

public class TMXLoader : MonoBehaviour {

    // レイヤー格納
    public class Layer2D {
        public int width; // 幅
        public int height; // 高さ
        private int[] _vals = null; // マップデータ

        // 作成
        public void Create(int width, int height) {
            this.width = width;
            this.height = height;
            _vals = new int[width * height];
        }

        // 値の取得
        // @param x X座標
        // @param y Y座標
        // @return 指定の座標の値 (領域外を指定したら-1)
        public int Get(int x, int y) {
            if(x < 0 || x >= width) { return -1; }
            if(y < 0 || y >= height) { return -1; }
            return _vals[y * width + x];
        }

        // 値の設定
        // @param x X座標
        // @param y Y座標
        // @param val 設定する値
        public void Set(int x, int y, int val) {
            if(x < 0 || x >= width) { return; }
            if(y < 0 || y >= height) { return; }
            _vals[y * width + x] = val;
        }

        // デバッグ出力
        public void Dump() {
            print ("[Layer2D] (w,h)=("+width+","+height+")");
            for(int y = 0; y < height; y++) {
                string s = "";
                for(int x = 0; x < width; x++) {
                    s += Get (x, y) + ",";
                }
                print (s);
            }
        }
    }

    // レベルデータを読み込む
    void Start () {
        // レイヤー生成
        Layer2D layer = new Layer2D();
        // レベルデータ取得
        TextAsset tmx = Resources.Load ("Levels/test2") as TextAsset;

        // XML解析開始
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml (tmx.text);
        XmlNodeList mapList = xmlDoc.GetElementsByTagName("map");
        foreach(XmlNode map in mapList) {
            XmlNodeList childList = map.ChildNodes;
            foreach(XmlNode child in childList) {
                if(child.Name != "layer") { continue; } // layerノード以外は見ない

                // マップ属性を取得
                XmlAttributeCollection attrs = child.Attributes;
                int w = int.Parse(attrs.GetNamedItem("width").Value); // 幅を取得
                int h = int.Parse(attrs.GetNamedItem("height").Value); // 高さを取得
                // レイヤー生成
                layer.Create(w, h);
                XmlNode node = child.FirstChild; // 子ノードは<data>のみ
                XmlNode n = node.FirstChild; // テキストノードを取得
                string val = n.Value; // テキストを取得
                // CSV(マップデータ)を解析
                int y = 0;
                foreach(string line in val.Split('\n')) {
                    int x = 0;
                    foreach(string s in line.Split(',')) {
                        int v = 0;
                        // ","で終わるのでチェックが必要
                        if(int.TryParse(s, out v) == false) { continue; }
                        // 値を設定
                        layer.Set (x, y, v);
                        x++;
                    }
                    y++;
                }
            }
        }

        // デバッグ出力
        layer.Dump();

    }
}

上記例では、Layer2Dをローカル変数にしていますが、これをメンバ変数にすることで外部からレベルデータを取得できるようになると思います。

結果のログ

読み込んだ結果、以下のログが出ているのでちゃんと読めていますね。

[Layer2D] (w,h)=(20,15)
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,0,0,0,2,2,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,2,0,2,2,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

5. 課題

とりあえず敵の配置ができればいいかな、と思って作ったのでTiled Map Editorのすべての情報を取得していません。大きなところでは、

  • マップチップのサイズを取得していない
  • 複数レイヤーの読み込みに対応していない
  • オブジェクトレイヤーの読み込みに対応していない

ですね。

6. アセットの自動コピー (2015/1/14追記)

AssetPostProcessorを使うとアセットに変更があった際、自動で更新を行うことができます。これを利用して、.tmxに変更があった場合に、.xmlへコピーすることができます。例えば以下のようにコードを記述します。

#if UNITY_EDITOR // Unityエディタ上のみ有効

using UnityEditor;
using UnityEngine;

public class MyAssetPostprocessor : AssetPostprocessor
{
    /// <summary>
    /// すべてのアセットのインポートが終了した際に呼び出されます
    /// </summary>
    /// <param name="importedAssets">インポートされたアセットのパス</param>
    /// <param name="deletedAssets">削除されたアセットのパス</param>
    /// <param name="movedAssets">移動したアセットの移動後のパス</param>
    /// <param name="movedFromPath">移動したアセットの移動前のパス</param>
    private static void OnPostprocessAllAssets(
        string[] importedAssets, 
        string[] deletedAssets, 
        string[] movedAssets, 
        string[] movedFromPath)
    {
        foreach (var importedAsset in importedAssets)
        {
            if(IsTmxFile(importedAsset))
            {
                // TMXファイルなので拡張子を*.xmlにしてコピー
                var newAsset = importedAsset.Replace(".tmx", ".xml");
                // 古いXMLは削除
                AssetDatabase.DeleteAsset(newAsset);
                if(AssetDatabase.CopyAsset(importedAsset, newAsset))
                {
                    // コピー実行
                    Debug.Log ("Copy: " + importedAsset + " -> " + newAsset);
                }
            }
        }
    }

    /// TMXファイルかどうか調べる
    static bool IsTmxFile(string str)
    {
        return str.IndexOf(".tmx") > 0;
    }

}

#endif

このコードを任意のフォルダに配置することで、自動コピーをするようになるので、拡張子を*.tmxファイルのままで運用することが可能となります。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
56
Help us understand the problem. What are the problem?