SRPGのマップエディタは、ステージ制作を効率化します。
タイル配置UIと保存機能を実装すれば、簡単にマップが作れます。
この記事では、実装方法を詳しく解説します。
✨ この記事でわかること
- タイル配置UIの実装
- マップデータの保存形式
- マップ読み込み処理の実装
- エディタ機能の拡張
- 実装例とコード

マップエディタは、まずグリッドベースのタイル配置から始めましょう。シンプルな構造が、拡張しやすくなります。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
タイル配置UIの実装

SRPGのマップエディタを作るうえで、最も重要な要素が「タイル配置UI」です。
なぜなら、ステージ制作の作業時間やストレスは、ほぼすべてこのUIの使いやすさで決まるからです。
もしタイル配置ができなければ、マップエディタとして成立しません。
また、配置操作が直感的でないと、ステージ制作そのものが苦痛になり、開発効率も大きく下がります。
ここでは初心者でも理解しやすいように、「なぜこの仕組みが必要なのか」を意識しながら、グリッドベースのタイル配置と、タイル選択UIの実装を順番に解説します。
なぜグリッドベースのタイル配置が重要なのか
SRPGでは、ユニットの移動や攻撃範囲、地形効果など、すべてがマス単位で管理されます。
そのため、マップも「自由配置」ではなく、グリッド(格子)構造で管理することが必要。
グリッドベースでタイルを配置できるようになると、次のようなメリットがあります。
- ユニットの移動計算や範囲判定が簡単になる
- マップデータを配列として扱えるため、保存・読み込みが容易になる
- 後から機能を拡張しやすい(地形効果、コスト、通行不可など)
つまり、この段階で正しいタイル配置の仕組みを作れるかどうかが、今後の開発の土台になります。
グリッドベースのタイル配置の実装
|
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
using UnityEngine; public class MapEditor : MonoBehaviour { public int mapWidth = 20; public int mapHeight = 20; public float tileSize = 1f; private TileType[,] mapData; public GameObject[] tilePrefabs; void Start() { mapData = new TileType[mapWidth, mapHeight]; InitializeMap(); } void InitializeMap() { for (int x = 0; x < mapWidth; x++) { for (int z = 0; z < mapHeight; z++) { mapData[x, z] = TileType.Plain; } } } public void PlaceTile(int x, int z, TileType tileType) { if (x < 0 || x >= mapWidth || z < 0 || z >= mapHeight) { return; } mapData[x, z] = tileType; Vector3 position = new Vector3(x * tileSize, 0, z * tileSize); Instantiate(tilePrefabs[(int)tileType], position, Quaternion.identity); } void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3 worldPos = hit.point; int x = Mathf.FloorToInt(worldPos.x / tileSize); int z = Mathf.FloorToInt(worldPos.z / tileSize); PlaceTile(x, z, TileType.Grass); } } } } public enum TileType { Plain, Grass, Forest, Mountain, Water } |
このコードでは、マウスでクリックした位置をグリッド座標に変換し、そのマスにタイルを配置しています。
ワールド座標をそのまま使わず、tileSizeで割ってマス番号に変換している点が重要です。
これにより、「どのマスに何が置かれているか」を正確に管理できるようになります。
なぜタイル選択UIが必要なのか
タイル配置ができるだけでは、実際のマップ制作では不十分です。
毎回コードを書き換えて配置するタイルを変えるのは、現実的ではありません。
そこで必要になるのが、UIからタイルを選択する仕組みです。
これがあることで、非エンジニアでも直感的にステージを作れるようになります。
タイル選択UIの実装
|
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 29 30 |
using UnityEngine; using UnityEngine.UI; public class TileSelector : MonoBehaviour { public MapEditor mapEditor; public Button[] tileButtons; private TileType selectedTile = TileType.Plain; void Start() { for (int i = 0; i < tileButtons.Length; i++) { int tileIndex = i; tileButtons[i].onClick.AddListener(() => SelectTile((TileType)tileIndex)); } } void SelectTile(TileType tileType) { selectedTile = tileType; Debug.Log($"選択されたタイル: {tileType}"); } public TileType GetSelectedTile() { return selectedTile; } } |
このUIを使うことで、「どのタイルを置くか」を事前に選択できるようになります。
実際の配置処理と組み合わせることで、実用的なマップエディタとして成立します。
タイル配置UIは、単なる機能のひとつではありません。
マップエディタの使い勝手と、ステージ制作の効率を決定づける最重要ポイントです。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
マップデータの保存形式

マップエディタを「便利なツール」にするために、必ず必要になるのがマップデータの保存機能です。
これができないと、せっかく作ったステージを毎回最初から作り直すことになります。
また、SRPGではステージ数が増えるのが前提です。
そのため、マップを安全に・分かりやすく・再利用できる形で保存できる仕組みは欠かせません。
ここでは初心者でも扱いやすいJSON形式を使って、マップデータを保存・読み込みする方法を解説します。
なぜJSON形式で保存するのか
マップデータの保存方法には、CSVやバイナリ形式など、いくつか選択肢があります。
その中でも、学習段階や個人開発に最も向いているのがJSON形式です。
JSON形式を使うメリットは、次の点にあります。
- テキスト形式なので中身を直接確認できる
- Unity標準の
JsonUtilityで簡単に扱える - デバッグ時に「何が保存されているか」が一目で分かる
特に初心者のうちは、ブラックボックスにならない保存形式を選ぶことが重要です。
マップデータの考え方
マップは見た目ではタイルが並んでいるだけですが、データとしては「どのマスに、どのタイルがあるか」を記録する必要があります。
そこで今回は、2次元配列のマップ情報を、1次元配列に変換して保存します。
これはJSONが多次元配列を扱いづらいためです。
一度1次元配列にして保存し、読み込み時に元の2次元配列へ戻す、という流れになります。
マップデータの保存処理
|
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 29 30 31 32 33 34 35 36 37 38 39 40 41 |
using UnityEngine; using System.IO; [System.Serializable] public class MapData { public int width; public int height; public int[] tiles; public MapData(int width, int height, TileType[,] mapData) { this.width = width; this.height = height; this.tiles = new int[width * height]; for (int x = 0; x < width; x++) { for (int z = 0; z < height; z++) { tiles[x + z * width] = (int)mapData[x, z]; } } } public TileType[,] To2DArray() { TileType[,] result = new TileType[width, height]; for (int x = 0; x < width; x++) { for (int z = 0; z < height; z++) { result[x, z] = (TileType)tiles[x + z * width]; } } return result; } } |
このクラスは、「マップの設計図」のような役割を持っています。
幅・高さ・タイル情報をひとまとめにして、保存できる形にしています。
ポイントは、保存用のデータ構造と、エディタ内部の構造を分けている点です。
これにより、将来マップ構造を変更しても対応しやすくなります。
マップの保存と読み込み処理
|
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public class MapSaveSystem : MonoBehaviour { public MapEditor mapEditor; public void SaveMap(string fileName) { MapData mapData = new MapData( mapEditor.mapWidth, mapEditor.mapHeight, mapEditor.mapData ); string json = JsonUtility.ToJson(mapData, true); string path = Path.Combine(Application.persistentDataPath, fileName + ".json"); File.WriteAllText(path, json); Debug.Log($"マップを保存しました: {path}"); } public void LoadMap(string fileName) { string path = Path.Combine(Application.persistentDataPath, fileName + ".json"); if (!File.Exists(path)) { Debug.LogError($"ファイルが見つかりません: {path}"); return; } string json = File.ReadAllText(path); MapData mapData = JsonUtility.FromJson<MapData>(json); mapEditor.mapData = mapData.To2DArray(); mapEditor.mapWidth = mapData.width; mapEditor.mapHeight = mapData.height; mapEditor.ReloadMap(); Debug.Log($"マップを読み込みました: {path}"); } } |
保存処理では、現在のマップ状態をJSONに変換し、ファイルとして書き出しています。
読み込み処理では、そのJSONを元にマップデータを復元しています。
この仕組みがあることで、「作る → 保存する → 後から修正する」という実際のゲーム開発に近いワークフローが可能になります。

保存機能は「完成度」を大きく左右します。
JSON形式なら中身が見えるので、初心者でも安心して扱えます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
マップ読み込み処理の実装

マップ読み込み処理は、保存したマップを復元します。
実装方法を紹介します。
マップの再構築
|
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 29 30 31 32 33 34 |
public class MapLoader : MonoBehaviour { public MapEditor mapEditor; public GameObject[] tilePrefabs; public void ReloadMap() { // 既存のタイルを削除 ClearMap(); // マップデータからタイルを配置 for (int x = 0; x < mapEditor.mapWidth; x++) { for (int z = 0; z < mapEditor.mapHeight; z++) { TileType tileType = mapEditor.mapData[x, z]; Vector3 position = new Vector3(x * mapEditor.tileSize, 0, z * mapEditor.tileSize); GameObject tile = Instantiate(tilePrefabs[(int)tileType], position, Quaternion.identity); tile.transform.SetParent(transform); } } } void ClearMap() { // 子オブジェクトを全て削除 foreach (Transform child in transform) { Destroy(child.gameObject); } } } |
このコードで、マップの再構築が実装できます。
保存したマップデータから、タイルを再配置します。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
エディタ機能の拡張

エディタ機能を拡張することで、使いやすくなります。
拡張方法を紹介します。
削除機能の実装
|
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 29 30 31 32 33 34 35 36 37 |
public class MapEditorExtended : MapEditor { public void DeleteTile(int x, int z) { if (x < 0 || x >= mapWidth || z < 0 || z >= mapHeight) { return; } mapData[x, z] = TileType.Plain; // タイルを削除 // 実装は省略 } void Update() { base.Update(); // 右クリックでタイルを削除 if (Input.GetMouseButtonDown(1)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3 worldPos = hit.point; int x = Mathf.FloorToInt(worldPos.x / tileSize); int z = Mathf.FloorToInt(worldPos.z / tileSize); DeleteTile(x, z); } } } } |
このコードで、削除機能が実装できます。
右クリックで、タイルを削除できます。
範囲選択機能
|
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
public class RangeSelection : MonoBehaviour { public MapEditor mapEditor; private Vector2Int startPos; private Vector2Int endPos; private bool isSelecting = false; void Update() { if (Input.GetMouseButtonDown(0)) { // 選択開始 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3 worldPos = hit.point; int x = Mathf.FloorToInt(worldPos.x / mapEditor.tileSize); int z = Mathf.FloorToInt(worldPos.z / mapEditor.tileSize); startPos = new Vector2Int(x, z); isSelecting = true; } } if (Input.GetMouseButtonUp(0) && isSelecting) { // 選択終了 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { Vector3 worldPos = hit.point; int x = Mathf.FloorToInt(worldPos.x / mapEditor.tileSize); int z = Mathf.FloorToInt(worldPos.z / mapEditor.tileSize); endPos = new Vector2Int(x, z); FillRange(startPos, endPos, TileType.Grass); isSelecting = false; } } } void FillRange(Vector2Int start, Vector2Int end, TileType tileType) { int minX = Mathf.Min(start.x, end.x); int maxX = Mathf.Max(start.x, end.x); int minZ = Mathf.Min(start.y, end.y); int maxZ = Mathf.Max(start.y, end.y); for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { mapEditor.PlaceTile(x, z, tileType); } } } } |
このコードで、範囲選択機能が実装できます。
ドラッグで範囲を選択し、一括でタイルを配置できます。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
実装例:完全なマップエディタ

実際に使える、完全なマップエディタの実装例を紹介します。
|
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
using UnityEngine; using UnityEngine.UI; public class CompleteMapEditor : MonoBehaviour { [Header("システム")] public MapEditor mapEditor; public TileSelector tileSelector; public MapSaveSystem saveSystem; public MapLoader mapLoader; [Header("UI")] public InputField fileNameInput; public Button saveButton; public Button loadButton; void Start() { saveButton.onClick.AddListener(SaveMap); loadButton.onClick.AddListener(LoadMap); } void SaveMap() { string fileName = fileNameInput.text; if (string.IsNullOrEmpty(fileName)) { fileName = "map_" + System.DateTime.Now.ToString("yyyyMMdd_HHmmss"); } saveSystem.SaveMap(fileName); } void LoadMap() { string fileName = fileNameInput.text; if (string.IsNullOrEmpty(fileName)) { Debug.LogError("ファイル名を入力してください"); return; } saveSystem.LoadMap(fileName); mapLoader.ReloadMap(); } } |
このコードで、完全なマップエディタが実装できます。
タイル配置、保存、読み込みを統合しています。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
よくある質問(FAQ)

Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる
あなたのオリジナルゲーム、今年こそ完成させませんか?
RPG・アクション・ホラー…Unityで本格ゲームを作りたい人のための学習サイトです。
実際に完成するゲームを題材に、
ソースコード・素材・プロジェクト一式をすべて公開。
仕事や学校の合間の1〜2時間でも、
「写経→改造」で自分のゲームまで作りきれる環境です。
まとめ

マップエディタは、グリッドベースのタイル配置から始めましょう。
シンプルな構造が、拡張しやすくなります。
✅ 今日から始める3ステップ
- ステップ1:グリッドベースのタイル配置を実装する(所要3時間)
- ステップ2:マップデータの保存機能を実装する(所要2時間)
- ステップ3:マップ読み込み機能を実装する(所要2時間)
本格的にUnityを学びたい方は、Unity入門の森で実践的なスキルを身につけましょう。
あなたのペースで、少しずつ進めていけば大丈夫です。
Unity入門の森を見る 初心者歓迎!動画×プロジェクト一式で本格ゲーム制作を学べる



コメント