Notice
Recent Posts
Recent Comments
Link
12-26 00:51
«   2024/12   »
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
Tags
more
Archives
Today
Total
관리 메뉴

킹머핀의 제작 일지

UNT) 커스텀 에디터 만들기 본문

Unity

UNT) 커스텀 에디터 만들기

KingMUffin 2021. 4. 14. 17:22
 

커스텀 에디터 - Unity 매뉴얼

애플리케이션 개발을 가속화하려면 자주 사용하는 컴포넌트에 대한 커스텀 에디터를 만드십시오. 이 페이지에서는 단순한 스크립트를 생성하여 게임 오브젝트가 항상 특정 지점을 바라보도록

docs.unity3d.com

졸작을 위해 유니티 커스텀 에디터를 제작하려 한다. 원래는 커스텀 창을 먼저 만드려 했지만, 게임오브젝트 컴포넌트 값을 제어할 수 있어야 하고, 창 자동 스크롤 바를 만드는 것부터 못 하겠더라. ㅎㅎㅎ 그건 나중에 필요하면 하자.

위의 매뉴얼에서 볼 수 있듯이, GUILayoutEditorGUILayout 클래스로 GUI를 추가할 수 있다. 그런데, 이 둘은 무슨 차이지? 게다가, GUI도 있고 EditorGUI도 있다. 이 넷은 무슨 차이지?? 대충 알아봤다.

 

GUI / GUILayout / EditorGUI / EditorGUILayout

 

IMGUI 레이아웃 모드 - Unity 매뉴얼

IMGUI 시스템을 사용할 때 UI를 정렬하고 구성하는 데 사용할 수 있는 고정 및 자동의 두 가지 모드가 있습니다. 지금까지 이 가이드에 제공된 모든 IMGUI 예제에는 고정 레이아웃이 사용되었습니다

docs.unity3d.com

우선 GUIGUILayout 클래스의 차이는 레이아웃 자동 생성 여부다. 그렇다면 EditorGUIEditorGUILayout도 마찬가지일 터. 에디터 창을 만들 때는 UI의 크기와 범위가 일정할 수록 가시성이 좋으므로 GUILayout이나 EditorGUILayout을 더 자주 사용하면 편할 것이다.

 

EditorGUI, EditorGUILayout, GUI, GUILayout... pshhh... WHEN TO USE WHAT?! - Unity Answers

 

answers.unity.com:443

GUILayoutEditorGUILayout 클래스의 차이는 에디터 최적화 여부다. 아래 이미지를 보면 EditorGUILayout으로 생성한 토글 버튼의 위치는 정확히 에디터 창 폭(가로 길이)의 절반에 걸쳐 정렬된다.

위의 출처

그런데 토글 버튼처럼 Lable과 Content로 된 GUI를 제외한 나머지는 둘이 차이가 있는지 모르겠다.. (있으니까 따로 만든걸까?)

그런데 EditorGUILayout에는 없는 GUI가 GUILayout에는 있는 경우가 있다. 예를 들면, 버튼이다. 그래서 GUILayout만 사용하거나 둘 다 사용해서 에디터 창을 만들 수 있을 것이다. 나는 둘 다 사용하기로 했다.

 

GUI vs GUILayout vs EditorGUI vs EditorGUILayout and When to Use Them - Unity

The Unity GUI API classes are fairly similar, so it’s worth clarifying their use cases. GUI vs EditorGUI The difference between GUI and EditorGUI is…

www.tangledrealitystudios.com


 

[Unity3D] 커스텀 에디터(2) - EditorGUILayout

유니티 커스텀 에디터 공부 두번째 - EditorGUILayout 커스텀 에디터는 시작하기 전에 익혀야할 내용들이 너무 많다. 어떻게 구현할 지 설계하기전에 대략적으로 어떤 내용들이 있는 지 살펴보는 것

debuglog.tistory.com

이 블로그를 참고하면서 시작하면 좋다.

 

[에디터 확장 입문] 번역 1장 에디터 확장에 사용하는 폴더

[에디터 확장 입문] http://anchan828.github.io/editor-manual/web/part2-beginner.html 1장 에디터 확장...

blog.naver.com

이 글은 전문적인 글의 번역본이며 양도 많아서 활용법을 배우기 좋다.


연결한 스크립트의 필드 가져오기

나는 에디터(개발자)와 런타임(사용자)가 조작할 수 있는 옵션을 만드려 한다. 그래서 에디터에서만 작동하는 에디터 상속 스크립트에 모든 필드를 넣을 수는 없다. 그렇다면 Monobehaviour을 상속받는 스크립트를 게임오브젝트에 장착하고, 그 필드를 에디터 상속 스크립트에서 제어할 필요가 있다.

다른 스크립트에서 필드를 가져올 방법은 크게 두 가지다.

 

1. 아래 코드는 맨 위의 출처에서 가져옴. 게임오브젝트에 장착한 스크립트에서 선언한 필드의 이름을 가져온다.

장점은, SerializedProperty and SerializedObject are classes for editing properties on objects in a completely generic way that automatically handles undo and styling UI for Prefabs.

단점은, 필드 하나 당 SerializedProperty 객체를 하나씩 일일이 할당해야 한다는 점.

[CustomEditor(typeof(LookAtPoint))]
//[CanEditMultipleObjects]
public class LookAtPointEditor : Editor
{
    SerializedProperty lookAtPoint;

    void OnEnable()
    {
        lookAtPoint = serializedObject.FindProperty("lookAtPoint");
    }
...

 

2. 아래 코드는 이 출처에서 가져옴.

장점은, 해당 스크립트 객체 하나만 생성해주면 모든 공용 필드를 가져올 수 있다.

단점은, SerializedProperty의 장점이 없다.

[CustomEditor (typeof(MyPlayerAlternative))]
public class MyPlayerEditorAlternative : Editor
{

    public override void OnInspectorGUI()
    {
        MyPlayerAlternative mp = (MyPlayerAlternative)target;

        mp.damage = EditorGUILayout.IntSlider ("Damage", mp.damage, 0, 100);
...

 

나는 Prefab overrides가 당장은 필요하지만, 그것도 나중에 미리 정의한 프로파일을 불러오는 기능을 만들 것이기 때문에 2번 방법을 사용하기로 했다.

 

조작한 GUI 값을 적용하기

커스텀 에디터의 GUI를 조작하면 연결한 다른 스크립트의 변수 값을 변경하는 코드를 작성하려 한다.

인스펙터를 조작할 때 매 프레임마다 OnInspecterGUI 함수가 호출되는데, 이 때 GUI에 변경사항이 있는지 확인할 수 있다. 예를 들어, 버튼을 눌렀는지를 다음과 같이 확인할 수 있다.

if (GUILayout.Button("This, is, BUTTon."))
{
	DoSomething();
}

이렇게 개별 GUI에 대응하지 않아도 되는 방법도 있다. 아래의 두 ChangeCheck 함수 사이에서 어떠한 변경사항이라도 있다면 일괄 적용하는 방식이다.

// 출처 : docs.unity3d.com/kr/2020.3/ScriptReference/EditorGUI.BeginChangeCheck.html

EditorGUILayout.LabelField("New value", labelText);

// Start a code block to check for GUI changes
EditorGUI.BeginChangeCheck();

sliderValue = EditorGUILayout.Slider(sliderValue, 0, 1);

// End the code block and update the label if a change occurred
if (EditorGUI.EndChangeCheck())
{
	labelText = sliderValue.ToString();
}

그래서 난 이 둘 중 한 방법으로 모든 GUI를 조작하면 연결한 스크립트의 함수를 호출해서 다른 원하는 스크립트의 변수 값을 변경하려 했다.

근데, 생각해보니 위 코드처럼 슬라이더를 조작한 값을 저장하기 위해 변수를 사용한다면, 변수 대신 속성(Property)을 사용하면, 번거롭게 함수를 일일이 정의하지도 호출하지도 않아도 된다!

(사실 속성이 정확히 어떤 용도인지는 아직도 잘 모르겠으나 훨씬 간편해지므로 사용하기로 했다. 내가 아는 것은 캡슐화가 간편하고 함수를 대체할 수 있다는 정도다.)

근데 문제가 생겼다. 해당 스크립트와 연결된 게임오브젝트와 아직 연결되지 않았다면 OnInspecterGUI가 업데이트될 때마다 오류가 나타난다. 표시와 저장이 동시에 이루어지기 때문이다. 해결 방법은?

  1. 조건문으로 인스펙터에 표시만 하고 값을 적용하지는 않는다. (쉽고 무식한 방법)
  2. 번거롭게 함수를 일일이 정의하고 ChangeCheck와 조건문을 이용해서 호출한다.
  3. 아예 표시하지 않는다. (가장 쉽고 간단하지만 덜 맘에 드는 방법)

1번도 나쁘지 않지만, 역시 쉬운 방법으로 가자. 3번 결정.

막상 적용하고 나니 오히려 좋아보인다. ㅎㅎ&

 

(나는 이제 다음으로 XR Interacter Toolkit의 Locomotion System을 이해해야 한다.)