인스펙터창에서 특수한 값을 받아야 하는 경우가 있다.



24b0d121e09c28a8699fe8b115ef0469933af7b4


ex) enum에 따라서 길이가 달라지는 배열의 값 할당


물론 위와 같이 하면 스크립트 할당 기준으로 잘 작동하지만 스크립트를 할당한 상태에서 이후 추가 수정에는 적용이 안된다.


[ContextMenu] 를 통해 맵핑 기능을 제작해도 되지만 좀 번거롭다는 단점이 있다.



이럴때 Editor를 활용해주면 된다.




24b0d121e09c28a8699fe8b115ef046c67f82c4b




간단하게 설명하면 인스펙터창의 프로퍼티를 설정한다고 생각하면 된다.







24b0d121e09c28a8699fe8b115ef046ece4dc3f5



사용법도 간단하다.


Editor를 상속받으면 된다. [CustomEditor]가 없으면 오류가 나니 무조건 붙혀주자.


typeof 안에는 인스펙터를 수정하고 싶은 class를 넣어주면 된다.




OnInspectorGUI에서 우리가 보는 인스펙터창을 어떤식으로 표시 할지를 세팅한다.




DrawEefaultInspector는 class 자체에서 기본적으로 보이는 인스펙터창 요소들이라고 생각해주면 된다.


필요에 따라 추가, 제거해주면 된다.




a17d2cad2f1b782a99595d5fe291afd27e686a79caf4eb707ae0384cb1d95c2b747889e6bad427e65a32f74a213fc9a5d7398b7879f7f70b2a3a8bf9fed05a263cfa04

(결과물)



위에 Click Layer는 시각적으로 확인하기 위해서 표시한 값이 들어가는 변수이고


아래 Target Layers는 Editor를 이용해 직접 제작한 커스텀 프로퍼티다.


값이 아주 잘 들어가는 걸 확인할 수 있다.





24b0d121e09c28a8699fe8b115ef046c64f12947

(최종 결과물)






코드상으론 GUILayout를 활용해 표현한다.

간단하게 Button, Box 이런 GUI 요소들을 표현하는데 사용한다.


관련 내용은 직접 찾아서 사용하자.

이번엔 여기서 사용 된 부분만 설명할 것이다.




private SerializedProperty _property;

    private TargetType[] _targetType;

    private bool _toggle;

    private GUIStyle _style;


    void OnEnable()

    {

        ClickManager clickManager = (ClickManager)target;

        // 프로퍼티를 찾아준다.

        _property = serializedObject.FindProperty(nameof(clickManager.ClickLayer));

        // End는 식별용 요소임으로 제외시켜준다.

        _targetType = ((TargetType[])Enum.GetValues(typeof(TargetType)))

                       .Where(t => t != TargetType.End)

                       .ToArray();

        // 저장된 토글 오픈 여부를 가져온다.

        _toggle = EditorPrefs.GetBool(FoldoutKey, true);

    }


    public override void OnInspectorGUI()

    {

        // 기본 인스펙터창을 그린다.

        DrawDefaultInspector();


        // 시각적으로 보기 편할려고 공백 추가.

        GUILayout.Space(10);


        // 토글 안의 박스를 표현하기 위해 Style을 선언한다. (Window가 해당 형식)

        // 마진을 따로 건드려 주는 이유는 window style에 기본적으로 존재하는 margin을 제거해주기 위함이다.

        if (_style == null)

        {

            _style = new GUIStyle(GUI.skin.window)

            {

                padding = new RectOffset(0, 0, 5, 5),

                margin = new RectOffset(0, 0, 0, 0)

            };

        }


        // 동기화 시켜준다.

        serializedObject.Update();


        // Foldout 영역을 생성한다.

        // 아래 EndFoldout까지는 전부 토글 안에 들어가게 된다.

        // 영역 전개라고 생각하면 편하다.

        bool newToggle = EditorGUILayout.BeginFoldoutHeaderGroup(_toggle, "Target Layers");

        // 토글의 오픈 여부를 기록하고 불러온다.

        // OnEnable에서 값을 불러오며 PlayerPrefs랑 똑같다고 생각하면 된다.

        // 저장 된 _toggle값을 가져와서 달라지면 저장하고.. 그런 로직이다.

        if (newToggle != _toggle)

        {

            _toggle = newToggle;

            EditorPrefs.SetBool(FoldoutKey, _toggle);

        }


        // 만약 토글이 열렸다면 내부 요소들을 렌더링해준다.

        if (_toggle)

        {

            // BeginVertical.. 이후 요소들을 세로로 정렬하겠다. 라는 뜻이다.

            // 이후의 요소들은 전부 세로로 정렬된다.

            EditorGUILayout.BeginVertical(_style);

            // 들여쓰기 정도를 의미한다. indent가 높아질수록 들여쓰기가 커진다.

            // 걍 간단하게 tab 횟수라 생각하자.

            EditorGUI.indentLevel++;

            // enum 내부 요소와 길이를 맞춰준다.

            SyncSize();

            // 토글 내부 요소들을 그려준다.

            // 하단 함수 참고

            DrawElements();

            // 다 그렸으니 들여쓰기를 빼준다.

            EditorGUI.indentLevel--;

            // 세로 정렬을 끝낸다.

            EditorGUILayout.EndVertical();

        }

        // 내부 요소를 전부 그렸으니 토글 그룹을 닫아준다.

        EditorGUILayout.EndFoldoutHeaderGroup();


        // 지금까지의 변경사항을 적용해준다.

        serializedObject.ApplyModifiedProperties();

    }


    void SyncSize()

    {

        // _arraySize로 배열 길이를 컨트롤 가능하다.

        if (_property.arraySize != _targetType.Length)

            _property.arraySize = _targetType.Length;

    }


    void DrawElements()

    {

        // enum 갯수 만큼 작동한다.

        for (int i = 0; i < _targetType.Length; i++)

        {

            // _property에서 기존의 값을 가져와서 세팅하며

            SerializedProperty elem = _property.GetArrayElementAtIndex(i);

            // PropertyField라는 GUI 형식을 통해 내부 요소를 제작해준다.

            // 이러한 요소들은 직접 찾아서 사용하자 다 설명하기엔 너무 많고 복잡하다.

            EditorGUILayout.PropertyField(elem, new GUIContent(_targetType[i].ToString()));

        }

    }



주석으로 각 줄마다 설명을 달아뒀다.







아무튼 이런 기능들을 통해 개발의 편의성을 크게 늘릴 수 있으니 적절히 활용해보자.


헹가래



아직 나도 좀 미숙해서 이상한 부분있으면 지적해주면 ㄳ