카테고리 없음

20250409 TIL SO작성을 위한 커스텀 에디터 툴

note4973 2025. 4. 9. 21:12
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 메뉴로 쉽게 접근 가능