なぜ作ったのか
Unityでダンジョンや室内のレベルデザインをする際、こんな悩みはありませんか?
「もっとRPGツクールのような感覚で、ポチポチとマップを作りたい!」
既存のアセットでも似たようなものはありますが、「自分のプロジェクトに特化した、軽量で融通の利くツール」が欲しくなり、Unityのエディタ拡張(EditorWindow)で自作しました。(unityの操作に慣れていない人向けに作っています)
本記事では、作成したツール「build Master」の機能紹介と、それを実現するためのエディタ拡張の実装を共有します。
成果物 build Master
エディタウィンドウ上のグリッドをマウスでドラッグするだけで、シーン上に床や壁が生成されます。
こだわった機能
グリッドペイント: 左クリックで配置、ドラッグで連続塗りつぶし。
3レイヤー管理: 「床」「壁」「ドア」を個別に管理し、重なりを考慮。
4x4 詳細編集: 1つのグリッドをさらに16分割して編集可能(穴の開いた壁とか)。
オートスケーリング: ただのCubeをPrefabにするだけで、自動で引き伸ばして配置。
実装ポイント
ここからは、実際にどうやって実装したのか、C#のコードを交えて解説します。
1. データ構造:1マスを「16分割」で管理する
単に「壁がある/ない」の bool 管理だと、細かい表現ができません。かといって最初から細かいグリッドにすると作業効率が落ちます。 そこで、1つのセグメント(マス)に対して、4x4=16個のサブ情報を保持するクラスを設計しました。
[System.Serializable]
public class SegmentData
{
// 4x4 = 16個のサブグリッドのPrefabインデックスを保持
// -1なら空、0以上ならPrefabID
public int[] segmentPrefabIndices = new int[16];
// 詳細モード(4x4)を使用しているかどうかのフラグ
public bool isDetailed = false;
// ドアの情報
public int doorPrefabIndex = -1;
public RectInt doorRect;
// ...(初期化や塗りつぶしメソッドなど)
}
これにより、普段は1マス一括塗りつぶし と こだわりの4x4編集 を同じデータ構造で無理なく管理しています。
2. EditorWindowでのお絵かき実装
EditorWindow の OnGUI メソッド内で、Event.current を監視してマウス入力を処理します。 特に「ドラッグして連続配置」する挙動は、以下のような処理で実現しています。
private void HandleGeneralInput(Event e, Rect gridArea)
{
if (gridArea.Contains(e.mousePosition))
{
// マウス座標からグリッドのインデックス(x, y)を計算
Vector2Int cellPos = GetCellFromPosition(e.mousePosition, gridArea);
// 左クリック (MouseDown)
if (e.type == EventType.MouseDown && e.button == 0)
{
if (e.alt) {
// Altキー押下ならスポイト(コピー)処理
} else {
// 塗り始めの処理(ドラッグモードの開始)
// 例: 今のモードが「壁塗り」なら DragMode.WallPainting に遷移
}
e.Use(); // 重要!イベントを消費して、裏側のシーンビュー等を操作しないようにする
}
// ドラッグ (MouseDrag)
else if (e.type == EventType.MouseDrag && e.button == 0)
{
// ここで連続配置処理!
// DragModeに応じてペイントや消しゴム処理を実行
PaintCell(cellPos);
e.Use();
}
}
}
Event.Use() を呼ぶことで、エディタウィンドウをクリックした瞬間にフォーカスが外れたり、意図しないイベントが走るのを防いでいます。
3. オートスケーリング(Prefabの自動変形)
このツールの最大の利点は専用のモデリングが不要な点です。 Unity標準のCubeで作った適当なPrefab(1m x 1m x 1m)でも、ツール側で計算してピタッと配置します。
具体的には、生成時にPrefabの持っている大きさを取得し、目標サイズとの比率を transform.localScale に適用しています。
// PrefabのBoundsを取得するヘルパー関数
private Bounds GetPrefabBounds(GameObject prefab)
{
// 一時的にインスタンス化して計算(計算後即破棄)
GameObject tempInstance = Instantiate(prefab);
// ... Rendererのboundsを計算 ...
DestroyImmediate(tempInstance);
return calculatedBounds;
}
// 生成時の処理(抜粋)
private void GenerateFloor(Transform parentRoom)
{
// ...
// 目標サイズ(グリッド設定から算出。例: 5.0f)
float targetWidth = tileSize_real;
float targetDepth = tileSize_real;
// 元のPrefabのサイズを取得
Bounds prefabBounds = GetPrefabBounds(prefab);
Vector3 baseSize = prefabBounds.size;
// 比率を計算
float scaleX = targetWidth / baseSize.x;
float scaleZ = targetDepth / baseSize.z;
// スケール適用
mergedObject.transform.localScale = new Vector3(
originalScale.x * scaleX,
originalScale.y, // 高さは維持
originalScale.z * scaleZ
);
}
まとめ
Unityのエディタ拡張は、最初は敷居が高く感じるかもしれませんが、ポイントを押さえれば自分好みのツールを作ることができます。自分はこのツールをVRのワールド構築に使用しています。
特に、今回のような「グリッドベース」のアプローチは、2D配列の管理だけで済むため、初心者の方のエディタ拡張入門としてもおすすめです。
unityを初めて触るような人でも取り扱えるようなツールになっているので使って下さると喜びます。
boothとgithubにあげています。
booth:https://inoue1213.booth.pm/items/7902268
github:https://github.com/mami1213/buildMaster