using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections.Generic;
public class RoomBuilderTool : EditorWindow
{
private string roomName = "NewRoom";
private RoomType roomType = RoomType.normal;
private Vector2Int roomSize = new Vector2Int(10, 10);
private GameObject root;
[MenuItem("Tools/Room Builder")]
public static void ShowWindow()
{
GetWindow<RoomBuilderTool>("Room Builder");
}
private void OnGUI()
{
GUILayout.Label("Room Preset 생성", EditorStyles.boldLabel);
roomName = EditorGUILayout.TextField("Room 이름", roomName);
roomType = (RoomType)EditorGUILayout.EnumPopup("Room 타입", roomType);
roomSize = EditorGUILayout.Vector2IntField("Room 크기", roomSize);
root = (GameObject)EditorGUILayout.ObjectField("Tile Root Object", root, typeof(GameObject), true);
if (GUILayout.Button("RoomPreset 생성"))
{
GenerateRoomPreset();
}
}
private void GenerateRoomPreset()
{
if (root == null)
{
Debug.LogError("Tile Root Object를 지정해주세요.");
return;
}
Dictionary<Vector2Int, TileInfoForRoom> tileInfos = new();
Transform[] allChildren = root.GetComponentsInChildren<Transform>();
foreach (Transform child in allChildren)
{
TileEditorMarker tile = child.GetComponent<TileEditorMarker>();
if (tile == null) continue;
Vector2Int pos = new Vector2Int(Mathf.RoundToInt(child.position.x), Mathf.RoundToInt(child.position.y));
if (tileInfos.ContainsKey(pos))
{
tileInfos[pos].position = pos;
tileInfos[pos].tileType = tile.tileType;
}
else tileInfos.Add(pos, new TileInfoForRoom(pos, tile.tileType));
}
foreach (Transform child in allChildren)
{
EnvironmentEditorMarker environment = child.GetComponent<EnvironmentEditorMarker>();
if (environment == null) continue;
Vector2Int pos = new Vector2Int(Mathf.RoundToInt(child.position.x), Mathf.RoundToInt(child.position.y));
if (tileInfos.ContainsKey(pos))
{
tileInfos[pos].position = pos;
tileInfos[pos].tileType = TileType.ground;
tileInfos[pos].environmentType = environment.envrionmeantType;
}
else tileInfos.Add(pos, new TileInfoForRoom(pos, TileType.ground, environment.envrionmeantType));
}
//향후 맵 오브젝트 추가 예정
RoomPreset preset = ScriptableObject.CreateInstance<RoomPreset>();
preset.roomName = roomName;
preset.roomType = roomType;
preset.roomSize = roomSize;
preset.tileInfo = tileInfos;
preset.RebuildTileListFromDictionary();
string path = "Assets/08_ScriptableObjects/RoomPresets";
if (!AssetDatabase.IsValidFolder(path))
AssetDatabase.CreateFolder("Assets/08_ScriptableObjects", "RoomPresets");
string assetPath = $"{path}/{roomName}_Preset.asset";
AssetDatabase.CreateAsset(preset, assetPath);
AssetDatabase.SaveAssets();
Debug.Log($"RoomPreset 생성 완료: {assetPath}");
}
}
room preset 작성을 씬창에서 하기위한 RoomBuilderTool을 구현해보았다
우선 커스텀 툴은 EditorWindow 를 상속받아 시작한다.
menuItem 의 위치를 정해준뒤 GetWindow<T>를 해준다
GUILayout.Label("Room Preset 생성", EditorStyles.boldLabel);
레이블 부터 붙여주고 시작한다.
roomName = EditorGUILayout.TextField("Room 이름", roomName);
roomType = (RoomType)EditorGUILayout.EnumPopup("Room 타입", roomType);
roomSize = EditorGUILayout.Vector2IntField("Room 크기", roomSize);
root = (GameObject)EditorGUILayout.ObjectField("Tile Root Object", root, typeof(GameObject), true);
이제 각 필드에 대한 정보를 EditioGUILayout을 통해 설정해준다. enum의경우 enumpopup으로 레이아웃을 설정하므로 반환값이 enum이기에 캐스팅이 필요하다.
EditorGUILayout.ObjectField(label, value, objectType, allowSceneObjects)
파라미터설명
| label | 인스펙터에 표시될 이름 (string) |
| value | 현재 필드에 들어 있는 값 (초기값 / 이전 선택 값) |
| objectType | 어떤 타입만 선택 가능할지 제한 (typeof(GameObject) 등) |
| allowSceneObjects | 씬에 있는 오브젝트를 허용할지 (true) 또는 프리팹 등 에셋만 (false) |
이후
if (GUILayout.Button("RoomPreset 생성"))
{
GenerateRoomPreset();
}
GUIlayout.Button은 버튼을 생성해주며 버튼이 눌렸을때 true를 반환해준다.
메서드의 기능은 일반적인 코드와 동일하다.
기능설명
| 타일 정보 수집 | TileEditorMarker 컴포넌트를 통해 타일 타입 수집 |
| 환경 정보 수집 | EnvironmentEditorMarker로 환경 타입 수집 |
| 좌표 자동 정렬 | child.position을 Vector2Int로 정리해서 저장 |
| 중복 좌표 처리 | Dictionary를 활용해 중복 위치는 갱신 |
| ScriptableObject 저장 | RoomPreset이라는 SO로 저장되며, Inspector에서 수정 가능 |
| 에디터 창 제공 | Tools > Room Builder 메뉴로 쉽게 접근 가능 |