【Unity】2Dタイルマップ15 配置したTileをJsonにセーブ/ロードする
Unity 2020.2.1f1 Personal(2021年3月)
前回の続きです!
今回は配置したTileをJsonデータとして保存する、保存したJsonデータからTileを復元する機能を実装します。
TilePaletteとTilemapの準備
まずはTilePaletteにTileを登録し、Tilemapを作成します。
TilePaletteの作成・Tileの登録・IsometricなTilemapの作成はこちらの過去記事をご参照下さい。
今回は以下の2つのTileを使います。
field_block.png
stone_block.png
TilePaletteに登録
TilemapのCell LayoutはIsometricで作成、特別な設定は必要ありません。
Isometricで作成
Tilemapの作成が完了したら適当にTileを配置します。
Tileなしにも対応させるので、いくつかわざとTileを抜いています。
適当にTileを配置
TileとTileの頭文字を保存したScriptable Objectを作成する
Tileの情報をJsonに保存する際データを軽くするため、またデータを見やすくしたいです。
ということでTileとTileの頭文字を保存したScriptable Objectを作成していきます。
Scriptable Objectについてはこちらの過去記事をご参照下さい。
まずはTileScriptableObject.csを作成します。
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using UnityEngine.Tilemaps; [CreateAssetMenu] public class TileScriptableObject : ScriptableObject { public List<TileStore> tileDataList = new List<TileStore>(); } [Serializable] public class TileStore { public string head; public Tile tile; }
TileScriptableObject.cs
作成しましたら、ProjectビューからCreate > Tile Scriptable Objectを選択しScriptable Objectを作成します。
作成したScriptable Objectを選択し、InspectorビューにてTilePaletteに登録したTileを登録します。
Head: f Tile:field_block
Head: s Tile:stone_block
以上でScriptable Objectの作成は完了です。
Tileの位置情報をJsonに保存する
続いてTileをJsonに保存していきます。
Unity標準のJsonUtilityを使っていきます。
JsonUtilityについてはこちらの過去記事をご参照下さい。
それではControllerという名前の空オブジェクトを作成し、TileSaveController.csというScriptを作成し取り付けます。
using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Tilemaps; public class TileSaveController : MonoBehaviour { [SerializeField] Tilemap tilemap; [SerializeField] TileScriptableObject tileSB; const string SAVE_FILE = "tilemap.json"; const string DATA_DIR = "Assets/StreamingAssets/data/"; static string saveDataPath = Path.Combine(DATA_DIR + SAVE_FILE); private void Start() { Save(); } public void Save() { var data = new SaveTilemapData(); tilemap.CompressBounds(); var b = tilemap.cellBounds; string str = ""; for (int y = b.min.y; y < b.max.y; y++) { for (int x = b.min.x; x < b.max.x; x++) { if (tilemap.HasTile(new Vector3Int(x, y, 0))) { str += tileSB.tileDataList.Single(t => t.tile == tilemap.GetTile(new Vector3Int(x, y, 0))).head + ","; } else { str += " ,"; } } str = str.TrimEnd(','); data.mapData.Add(str); str = ""; } string json = JsonUtility.ToJson(data, true); if (!Directory.Exists(DATA_DIR)) { Directory.CreateDirectory(DATA_DIR); } StreamWriter writer = new StreamWriter(saveDataPath, false); writer.WriteLine(json); writer.Flush(); writer.Close(); } [Serializable] public class SaveTilemapData { public List<string> mapData = new List<string>(); } }
TileSaveController.cs
Inspectorビューによりtilemapに作成したTilemapを、tileSBに作成したScriptableObjectを登録します。
TileMapとScriptableObjectを登録
Jsonに保存するSaveTilemapDataクラスを作成し、Tileの位置情報を保存する文字列List(mapData)を用意します。
xとyを組み合わせたfor文を回し、全てのTileが収まる範囲のTile情報をカンマ区切りで保存しています。
指定した位置にTileがある場合はtileSB.tileDataListからLinqのSingleメソッドを活用し、同じTileのhead情報(アルファベット1文字)を抽出し追加します。
指定した位置にTileがない場合はスペースを追加します。
全ての情報をmapDataに保存したらJsonUtility.ToJsonメソッドでJson化し、指定したディレクトリに保存します。
ProjectビューのAssets/StreamingAssets/data/にtilemap.jsonが作成されているはずです。
{ "mapData": [ " , , , , ,f,f,f,f,f,f, ", " , ,f,f,f,f,f,f,f,f,f, ", " ,f,f,f,s,f,f,s,f,f,s,f", "f,f, ,f,s,f,f,s,f,f,s,f", "f,f, ,f,s,f,f,s,f,f,s,f", "f,f, ,f,s,f,f,s,f,f,s,f", "f,f,f,f,s,s,s,s,s,s,s,f", "f,f,f, ,f,f,f,f,f,f,f,f", "f,f,f, ,f,f,f,f,f,f,f, ", "f,f,f,f,f,f,f,f,f,f, , ", " ,f,f,f, , , , , , , , " ] }
tilemap.json
Tileがある場所はTileScriptableObjectのheadが、Tileがない場所はスペースが記入されたJsonが保存されました。
Tileの位置情報保存については以上です。
Tileの位置情報をロードする
最後に保存したJsonを活用してTileをロードしていきます。
まずはSceneビューにて配置したTileを全て削除します。
Tileを削除
続いてTileSaveController.csにLoadメソッドを追加します。
using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.Tilemaps; public class TileSaveController : MonoBehaviour { [SerializeField] Tilemap tilemap; [SerializeField] TileScriptableObject tileSB; const string SAVE_FILE = "tilemap.json"; const string DATA_DIR = "Assets/StreamingAssets/data/"; static string saveDataPath = Path.Combine(DATA_DIR + SAVE_FILE); private void Start() { // ***** 修正 ***** // Save(); Load(); // ***** 修正終了 ***** } public void Save() { var data = new SaveTilemapData(); tilemap.CompressBounds(); var b = tilemap.cellBounds; string str = ""; for (int y = b.min.y; y < b.max.y; y++) { for (int x = b.min.x; x < b.max.x; x++) { if (tilemap.HasTile(new Vector3Int(x, y, 0))) { str += tileSB.tileDataList.Single(t => t.tile == tilemap.GetTile(new Vector3Int(x, y, 0))).head + ","; } else { str += " ,"; } } str = str.TrimEnd(','); data.mapData.Add(str); str = ""; } string json = JsonUtility.ToJson(data, true); if (!Directory.Exists(DATA_DIR)) { Directory.CreateDirectory(DATA_DIR); } StreamWriter writer = new StreamWriter(saveDataPath, false); writer.WriteLine(json); writer.Flush(); writer.Close(); } // ***** 追加 ***** public void Load() { tilemap.ClearAllTiles(); FileStream stream = File.Open(saveDataPath, FileMode.Open); StreamReader reader = new StreamReader(stream); var json = reader.ReadToEnd(); reader.Close(); stream.Close(); SaveTilemapData data = JsonUtility.FromJson<SaveTilemapData>(json); for (int y = 0; y < data.mapData.Count; y++) { string[] xlist = data.mapData[y].Split(','); for (int x = 0; x < xlist.Length; x++) { if (xlist[x] == " ") continue; tilemap.SetTile(new Vector3Int(x, y, 0), tileSB.tileDataList.Single(t => t.head == xlist[x]).tile); } } } // ***** 追加終了 ***** [Serializable] public class SaveTilemapData { public List<string> mapData = new List<string>(); } }
TileSaveController.cs
Loadメソッドでは保存したjsonデータをJsonUtility.FromJsonにてSaveTilemapDataクラスに復元しています。
データをfor文で回し、Tilemap.SetTileメソッドでTileを配置していきます。
セーブの時と同様、LinqのSingleメソッドでScriptableObjectと一致したTileを配置します。
スペースが来た時はcontinueを行い配置を回避します。
セーブしたTileの位置情報通りに配置されます
今回は以上になります。
ありがとうございました〜。