본문 바로가기

프로그램/유니티 스크립트 소스

[유니티 스크립트 소스] 에디터 확장

728x90
반응형

Unity에서는 커스텀 인스펙터 및 Editor 창으로 에디터를 확장할 수 있으며, 커스텀 Property Drawers 로 인스펙터에 프로퍼티가 표시되는 방식을 지정할 수 있습니다. 
이 섹션에서는 이러한 기능을 사용하는 방법에 대해 설명합니다.

 

 

1. 에디터 창

 

애플리케이션에 커스텀 창을 얼마든지 생성할 수 있습니다. 

커스텀 창은 인스펙터, 씬 또는 기타 빌트인 창처럼 작동합니다. 이 방법은 게임의 하위시스템에 사용자 인터페이스를 추가하는 데 유용합니다.

 

[그림] 컷씬 액션 스크립팅에 사용되는 기능성 게임 인터렉티브에 의한 커스텀 에디터 인터페이스

 

  • 아래의 간단한 절차를 따라 커스텀 에디터 창을 만들 수 있습니다. 에디터 창에서 파생되는 스크립트를 작성합니다.
  • 코드를 사용하여 창이 자동으로 표시되도록 합니다.
  • 툴에 대한 GUI 코드를 구현합니다.

 

1.1 에디터 창에서 파생


커스텀 에디터 창을 만들기 위해서는 스크립트를 “Editor”라는 폴더 안에 저장해야 합니다. 

에디터 창에서 파생되는 이 스크립트에 클래스를 만든 다음, 내부 OnGUI 함수에 GUI 컨트롤을 작성합니다.

 

using UnityEngine;
using UnityEditor;
using System.Collections;

public class Example : EditorWindow
{
    void OnGUI () {
        // The actual window code goes here
       }
}

프로젝트 내 ‘Editor’ 폴더에 있는 MyWindow.js

 

 

1.2 창 표시


화면에 창을 표시하려면 창을 표시하는 메뉴 아이템을 생성합니다. 

이 작업은 MenuItem 프로퍼티를 통해 활성화되는 함수를 작성하여 수행합니다.

Unity의 기본 설정은 창을 재활용합니다 

따라서 메뉴 아이템을 다시 선택하면 기존 창이 표시됩니다. 

이 작업은 EditorWindow.GetWindow 함수를 사용하여 수행됩니다.

 

예제:

using UnityEngine;
using UnityEditor;
using System.Collections;

class MyWindow : EditorWindow {
    [MenuItem ("Window/My Window")]

    public static void  ShowWindow () {
        EditorWindow.GetWindow(typeof(MyWindow));
    }
    
    void OnGUI () {
        // The actual window code goes here
    }
}

MyWindow 표시



호출 간 포지션을 저장하거나 커스텀 레이아웃에 사용하는 등 도킹 가능한 스탠다드 에디터 창을 생성할 수 있습니다. 

생성한 창에 더 많은 컨트롤을 적용하려면 GetWindowWithRect를 사용할 수 있습니다.

 


1.3 창의 GUI 구현


창의 실제 콘텐츠는 OnGUI 함수를 구현하여 렌더링합니다. 

인게임 GUI(GUI 및 GUILayout)에 사용하는 것과 동일한 UnityGUI 클래스를 사용할 수 있습니다. 

또한 EditorGUI 및 EditorGUILayout 에디터 전용 클래스에 있는 GUI 컨트롤도 몇 개 더 제공됩니다. 

이 클래스는 일반 클래스에 이미 사용 가능한 컨트롤에 추가되므로 원하는 경우 믹스 앤 매치를 할 수 있습니다.


다음 C# 코드는 커스텀 에디터 창에 GUI 요소를 추가하는 방법을 나타냅니다.

 

using UnityEditor;
using UnityEngine;

public class MyWindow : EditorWindow
{
    string myString = "Hello World";
    bool groupEnabled;
    bool myBool = true;
    float myFloat = 1.23f;
    
    // Add menu item named "My Window" to the Window menu
    [MenuItem("Window/My Window")]
    public static void ShowWindow()
    {
        //Show existing window instance. If one doesn't exist, make one.
        EditorWindow.GetWindow(typeof(MyWindow));
    }
    
    void OnGUI()
    {
        GUILayout.Label ("Base Settings", EditorStyles.boldLabel);
        myString = EditorGUILayout.TextField ("Text Field", myString);
        
        groupEnabled = EditorGUILayout.BeginToggleGroup ("Optional Settings", groupEnabled);
            myBool = EditorGUILayout.Toggle ("Toggle", myBool);
            myFloat = EditorGUILayout.Slider ("Slider", myFloat, -3, 3);
        EditorGUILayout.EndToggleGroup ();
    }
}

위의 예제를 통해 생성된 창은 다음과 같습니다.

 

[그림] 제공된 예제를 사용하여 생성한 커스텀 에디터 창


자세한 내용은 에디터 창 페이지의 예제와 문서를 참조하십시오.

 

 

2. 프로퍼티 드로어


프로퍼티 드로어는 스크립트에서 속성을 사용하거나 특정 Serializable 클래스가 표시되어야 하는 방법을 제어하여 인스펙터(Inspector) 창 에 특정 컨트롤이 표시되는 방법을 커스터마이즈할 때 사용할 수 있습니다.

프로퍼티 드로어는 다음 두 가지 용도로 사용됩니다.

  • 모든 Serializable 클래스 인스턴스의 GUI 커스터마이즈
  • 커스텀 프로퍼티 속성 을 사용하여 스크립트 멤버의 GUI 커스터마이즈

 

2.1 Serializable 클래스의 GUI 커스터마이즈

 

커스텀 Serializable 클래스가 있는 경우 Property Drawer 를 사용하여 인스펙터 에서 클래스가 표시되는 방법을 제어할 수 있습니다. 

아래 스크립트 예제의 Serializable 클래스 Ingredient 를 상세히 살펴 보십시오. 참고: 해당 스크립트는 에디터 스크립트가 아닙니다. 

프로퍼티 속성 클래스는 일반 스크립트 파일에 삽입해야 합니다.

C#(예제):

 

using System;
using UnityEngine;

enum IngredientUnit { Spoon, Cup, Bowl, Piece }

// Custom serializable class
[Serializable]
public class Ingredient
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}

public class Recipe : MonoBehaviour
{
    public Ingredient potionResult;
    public Ingredient[] potionIngredients;
}

 

커스텀 프로퍼티 드로어를 사용하여 인스펙터 내에서 Ingredient 클래스가 표시되는 모습을 변경할 수 있습니다. 

커스텀 프로퍼티 드로어가 있는 경우와 없는 경우 인스펙터에서 Ingredient 프로퍼티가 어떻게 보여지는지 비교해 보십시오.

 

[그림] 커스텀 프로퍼티 드로어가 없는 경우(왼쪽)와 있는 경우(오른쪽)의 인스펙터 내 클래스

 

CustomPropertyDrawer 속성을 이용하여 프로퍼티 드로어를 Serializable 클래스에 연결하고 드로어인 Serializable 클래스의 타입을 전달할 수 있습니다.

C#(예제):

 

using UnityEditor;
using UnityEngine;

// IngredientDrawer
[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, property);

        // Draw label
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        // Don't make child fields be indented
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;

        // Calculate rects
        var amountRect = new Rect(position.x, position.y, 30, position.height);
        var unitRect = new Rect(position.x + 35, position.y, 50, position.height);
        var nameRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);

        // Draw fields - passs GUIContent.none to each so they are drawn without labels
        EditorGUI.PropertyField(amountRect, property.FindPropertyRelative("amount"), GUIContent.none);
        EditorGUI.PropertyField(unitRect, property.FindPropertyRelative("unit"), GUIContent.none);
        EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

 

 

2.2 프로퍼티 속성을 사용한 스크립트 멤버 GUI 커스터마이즈


Property Drawer 를 사용해 커스텀 Property Attributes 가 있는 스크립트에서 멤버가 보여지는 모습을 변경할 수도 있습니다. 

스크립트에서 부동 소수점과 정수를 특정 범위로 제한하고 인스펙터 에 슬라이더로 표시하려는 경우, 다음과 같이 RangeAttribute 라고 하는 빌트인 PropertyAttribute 를 사용할 수 있습니다.


C#(예제):

 

// Show this float in the Inspector as a slider between 0 and 10
[Range(0f, 10f)]
float myFloat = 0f;

 

원하는 PropertyAttribute__도 직접 만들 수 있습니다.

여기서는 RangeAttribute__의 코드를 예제로 사용합니다.

속성은 PropertyAttribute 클래스를 확장해야 합니다. 원할 경우 프로퍼티가 파라미터를 사용하고 퍼블릭 멤버 변수로 저장할 수 있습니다.

 

C#(예제):

 

using UnityEngine;

public class MyRangeAttribute : PropertyAttribute 
{
        readonly float min;
        readonly float max;
        
        void MyRangeAttribute(float min, float max)
        {
            this.min = min;
            this.max = max;
        }
}

 

이제 속성을 갖게 되었으므로 해당 속성이 있는 프로퍼티를 드로우하는 Property Drawer 를 만들어야 합니다.

드로어는 PropertyDrawer 클래스를 확장해야 하고, 어느 드로어의 속성으로 사용되는지 알리는 CustomPropertyDrawer 속성을 포함해야 합니다.

프로퍼티 드로어 클래스는 에디터 스트립트에서 에디터라는 폴더 안에 넣어야 합니다.

C#(예제):

 

using UnityEditor;
using UnityEngine;

// Tell the MyRangeDrawer that it is a drawer for properties with the MyRangeAttribute.
[CustomPropertyDrawer(typeof(MyRangeAttribute))]
public class RangeDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // First get the attribute since it contains the range for the slider
        MyRangeAttribute range = (MyRangeAttribute)attribute;

        // Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
        if (property.propertyType == SerializedPropertyType.Float)
            EditorGUI.Slider(position, property, range.min, range.max, label);
        else if (property.propertyType == SerializedPropertyType.Integer)
            EditorGUI.IntSlider(position, property, (int) range.min, (int) range.max, label);
        else
            EditorGUI.LabelField(position, label.text, "Use MyRange with float or int.");
    }
}

 

퍼포먼스와 관련한 이유로 EditorGUILayout 함수를 프로퍼티 드로어와 함께 사용할 수 없습니다.

 

 

3. 커스텀 에디터


애플리케이션 개발을 가속화하려면 자주 사용하는 컴포넌트에 대한 커스텀 에디터를 만드십시오. 

이 페이지에서는 단순한 스크립트를 생성하여 게임 오브젝트가 항상 특정 지점을 바라보도록 만드는 방법을 설명합니다.

  1. C# 스크립트를 만들고 이름을 "“LookAtPoint”라고 지정합니다.
  2. 스크립트를 열고 콘텐츠를 아래 코드로 교체합니다.
  3. 스크립트를 씬의 게임 오브젝트에 연결합니다.

 

//C# Example (LookAtPoint.cs)
using UnityEngine;
public class LookAtPoint : MonoBehaviour
{
    public Vector3 lookAtPoint = Vector3.zero;

    void Update()
    {
        transform.LookAt(lookAtPoint);
    }
}


Play 모드를 시작하면 스크립트를 연결한 게임 오브젝트가 이제 “Look At Point” 프로퍼티로 설정한 좌표를 향합니다.

에디터 스크립트를 작성하는 경우 애플리케이션이 실행되지 않을 때 편집 모드에서 스크립트가 실행되도록 만들면 유용할 때가 많습니다.

이렇게 하려면 아래와 같이 클래스에 ExecuteInEditMode 속성을 추가하십시오.

 

//C# Example (LookAtPoint.cs)
using UnityEngine;
[ExecuteInEditMode]
public class LookAtPoint : MonoBehaviour
{
    public Vector3 lookAtPoint = Vector3.zero;

    void Update()
    {
        transform.LookAt(lookAtPoint);
    }
}


이제 에디터에서 게임 오브젝트를 움직이거나 인스펙터에서 “Look At Point” 값을 변경하면 게임 오브젝트가 회전을 업데이트하여 월드 공간의 타겟 지점을 바라봅니다.

 

3.1 커스텀 에디터 생성


위의 예에는 편집 모드 중에 간단한 스크립트가 실행되도록 할 수 있는 방법이 나와 있지만, 이 방법만으로는 에디터 툴을 직접 만들 수 없습니다. 

다음 단계에서는 방금 작성한 스크립트를 위한 Custom Editor 를 만듭니다.

Unity에서 스크립트를 작성하는 경우 기본적으로 MonoBehaviour에서 상속하기 때문에 이 컴포넌트는 게임 오브젝트에 연결이 가능합니다. 

컴포넌트를 게임 오브젝트에 배치하면 모든 public 변수(정수, 플로트, 문자열 등)를 확인하고 편집하는 데 사용할 수 있는 기본 인터페이스가 인스펙터에 표시됩니다.

LookAtPoint 컴포넌트의 인스펙터는 기본적으로 다음과 같은 모습입니다.

 

[그림] 공용 Vector3 필드가 있는 기본 인스펙터

 

커스텀 에디터는 이 디폴트 레이아웃을 사용자가 선택하는 에디터 컨트롤로 대체하는 별도의 스크립트입니다.

LookAtPoint 스크립트에 대한 커스텀 에디터를 생성하려면 다음 단계를 따르십시오.

  1. 새로운 C# 스크립트를 만들고 이름을 “LookAtPointEditor”라고 지정합니다.
  2. 스크립트를 열고 콘텐츠를 아래 코드로 교체합니다.

 

//C# Example (LookAtPointEditor.cs)
using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(LookAtPoint))]
[CanEditMultipleObjects]
public class LookAtPointEditor : Editor 
{
    SerializedProperty lookAtPoint;
    
    void OnEnable()
    {
        lookAtPoint = serializedObject.FindProperty("lookAtPoint");
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(lookAtPoint);
        serializedObject.ApplyModifiedProperties();
    }
}

 

이 클래스는 Editor에서 상속되어야 합니다. CustomEditor 속성은 어떤 컴포넌트를 위한 에디터로 동작해야 하는지 Unity에 알립니다. 

CanEditMultipleObjects 속성은 사용자가 이 에디터로 여러 오브젝트를 선택하고 모두 동시에 변경할 수 있음을 Unity에 알립니다.

Unity는 인스펙터에 에디터를 표시할 때마다 OnInspectorGUI의 코드를 실행합니다. 여기에 아무 GUI 코드나 넣어도 OnGUI와 동일한 방식으로 동작하지만, 인스펙터 내에서 실행됩니다. 에디터는 검사 중인 게임 오브젝트에 액세스하기 위해 사용할 수 있는 타겟 프로퍼티를 정의합니다.

LookAtPoint 컴포넌트의 인스펙터는 새 에디터에서 다음과 같은 모습입니다.

 

 

 

이는 기존과 매우 유사한 모습입니다. (단, 에디터 스크립트가 표시할 인스펙터 코드를 추가하지 않으므로 “Script” 필드는 이제 존재하지 않습니다.)

하지만 이제는 인스펙터가 에디터 스크립트에 표시되는 방식을 제어할 수 있기 때문에 원하는 코드를 사용하여 인스펙터 필드를 레이아웃하고 값을 조정하거나 그래픽스 또는 다른 시각적 요소를 표시할 수도 있습니다. 실질적으로 터레인 시스템 및 애니메이션 임포트 설정 등 훨씬 복잡한 인스펙터를 포함한 Unity 에디터 내에 나타나는 모든 인스펙터는 커스텀 에디터를 생성하는 동안 액세스하는 동일한 API를 사용해 만들어집니다.

다음은 타겟 포인트가 게임 오브젝트 위에 있는지 아래에 있는지 나타내는 메시지를 표시하기 위해 에디터 스크립트를 확장하는 간단한 예입니다.

 

//C# Example (LookAtPointEditor.cs)
using UnityEngine;
using UnityEditor;

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

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

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(lookAtPoint);
        serializedObject.ApplyModifiedProperties();
        if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y)
        {
            EditorGUILayout.LabelField("(Above this object)");
        }
        if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y)
        {
            EditorGUILayout.LabelField("(Below this object)");
        }
    }
}

 

LookAtPoint 컴포넌트의 인스펙터는 다음과 같은 모습이며, 메시지는 타겟 지점이 게임 오브젝트의 위 또는 아래에 있는지 알려줍니다.

 


모든 IMGUI 커맨드에 완전히 액세스하여 에디터 창 안에서 카메라를 사용하여 씬을 렌더링하는 등 모든 인터페이스 타입을 드로우할 수 있습니다.

 

3.2 씬 뷰 추가 코드


씬 뷰에 코드를 더 추가할 수 있습니다. 이렇게 하려면 커스텀 에디터에서 OnSceneGUI를 구현하십시오.

OnSceneGUI는 씬 뷰에서 실행된다는 점을 제외하고 OnInspectorGUI와 동일하게 작동합니다. 원하는 편집 컨트롤을 직접 만들기 위해 Handles 클래스에 정의된 함수를 사용할 수 있습니다. 이 클래스의 모든 함수는 3D 씬 뷰에서 동작하도록 설계되었습니다.

 

//C# Example (LookAtPointEditor.cs)
using UnityEngine;
using UnityEditor;

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

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

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(lookAtPoint);
        if (lookAtPoint.vector3Value.y > (target as LookAtPoint).transform.position.y)
        {
            EditorGUILayout.LabelField("(Above this object)");
        }
        if (lookAtPoint.vector3Value.y < (target as LookAtPoint).transform.position.y)
        {
            EditorGUILayout.LabelField("(Below this object)");
        }
        
            
        serializedObject.ApplyModifiedProperties();
    }

    public void OnSceneGUI()
    {
        var t = (target as LookAtPoint);

        EditorGUI.BeginChangeCheck();
        Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);
        if (EditorGUI.EndChangeCheck())
        {
            Undo.RecordObject(target, "Move point");
            t.lookAtPoint = pos;
            t.Update();
        }
    }
}

 

2D GUI 오브젝트(GUI 또는 EditorGUI)를 추가하려면 Handles.BeginGUI() 및 Handles.EndGUI() 대상 호출에 래핑해야 합니다.

 

3.3 EditorGUI 정적 함수

 

정적변수 설명
BeginChangeCheck GUI 변경 사항을 확인하기 위해 새 코드 블록을 시작합니다.
BeginDisabledGroup 사용하지 않도록 설정할 수 있는 컨트롤 그룹을 만듭니다.
BeginFoldoutHeaderGroup 왼쪽에 접이식 화살표가 있는 레이블을 만듭니다.
BeginProperty 일반 GUI 컨트롤이 SerializedProperty에서 작동하도록 하는 데 유용한 속성 래퍼를 만듭니다.
BoundsField 경계를 입력하기 위한 중심 및 익스텐트 필드를 만듭니다.
BoundsIntField BoundsInt를 입력하기 위한 위치 및 크기 필드를 만듭니다.
CanCacheInspectorGUI SerializedProperty의 검사기 GUI를 캐시할 수 있는지 여부를 가져옵니다.
ColorField 색상을 선택하기 위한 필드를 만듭니다.
CurveField AnimationCurve를 편집하기 위한 필드를 만듭니다.
DelayedDoubleField double을 입력하기 위해 지연된 텍스트 필드를 만듭니다.
DelayedFloatField 부동 소수점을 입력하기 위해 지연된 텍스트 필드를 만듭니다.
DelayedIntField 정수를 입력하기 위해 지연된 텍스트 필드를 만듭니다.
DelayedTextField 지연된 텍스트 필드를 만듭니다.
DoubleField double을 입력하기 위한 텍스트 필드를 만듭니다.
DrawPreviewTexture 사각형 안에 텍스처를 그립니다.
DrawRect 현재 편집기 창 내에서 지정된 위치와 크기로 채워진 사각형의 색상을 그립니다.
DrawTextureAlpha 사각형 내에 텍스처의 알파 채널을 그립니다.
DropdownButton 마우스 아래로 반응하여 자신의 드롭 다운 콘텐츠를 표시하는 버튼을 만듭니다.
DropShadowLabel 그림자가 있는 레이블을 그립니다.
EndChangeCheck 코드 블록을 종료하고 GUI 변경 사항을 확인합니다.
EndDisabledGroup BeginDisabledGroup으로 시작하는 비활성화된 그룹을 종료합니다.
EndFoldoutHeaderGroup BeginFoldoutHeaderGroup으로 시작한 그룹을 닫습니다. 또한 보십시오: EditorGUILayout.BeginFoldoutHeaderGroup.
EndProperty BeginProperty로 시작된 속성 래퍼를 종료합니다.
EnumFlagsField 클릭할 때 enum 유형의 모든 값에 대한 옵션이 있는 메뉴를 표시합니다. 이름이 "Nothing"인 값 0에 대한 옵션과 "Everything"이라는 이름의 값 ~0(, 모든 비트 설정)에 대한 옵션이 항상 메뉴 맨 위에 표시됩니다. 0 ~0의 이름은 enum 유형에서 이러한 값을 정의하여 재정의할 수 있습니다.
EnumPopup 열거형 팝업 선택 필드를 만듭니다.
FloatField 부동 소수점을 입력하기 위한 텍스트 필드를 만듭니다.
FocusTextInControl 키보드 포커스를 명명된 텍스트 필드로 이동하고 콘텐츠 편집을 시작합니다.
Foldout 왼쪽에 접이식 화살표가 있는 레이블을 만듭니다.
GetPropertyHeight PropertyField 컨트롤에 필요한 높이를 가져옵니다.
GradientField 그라디언트를 편집하기 위한 필드를 만듭니다.
HandlePrefixLabel 일부 컨트롤에 대한 레이블을 만듭니다.
HelpBox 사용자에게 메시지가 있는 도움말 상자를 만듭니다.
InspectorTitlebar 검사기 창과 같은 제목 표시줄을 만듭니다.
IntField 정수를 입력하기 위한 텍스트 필드를 만듭니다.
IntPopup 정수 팝업 선택 필드를 만듭니다.
IntSlider 사용자가 드래그하여 최소값과 최대 사이의 정수 값을 변경할 수 있는 슬라이더를 만듭니다.
LabelField 레이블 필드를 만듭니다. (읽기 전용 정보를 표시하는 데 유용합니다.)
LayerField 레이어 선택 필드를 만듭니다.
LinkButton 클릭 가능한 링크 레이블을 만듭니다.
LongField 긴 정수를 입력하기 위한 텍스트 필드를 만듭니다.
MaskField 마스크에 대한 필드를 만듭니다.
MinMaxSlider 사용자가 최소와 최대 사이의 범위를 지정하는 데 사용할 수 있는 특수 슬라이더를 만듭니다.
MultiFloatField 같은 줄에 여러 부동 소수점을 입력하기 위한 텍스트 필드가 있는 다중 컨트롤을 만듭니다.
MultiIntField 같은 줄에 여러 정수를 입력하기 위한 텍스트 필드가 있는 다중 컨트롤을 만듭니다.
MultiPropertyField 같은 줄에 여러 속성 필드가 있는 다중 컨트롤을 만듭니다.
ObjectField 개체 필드를 만듭니다. 개체를 끌어다 놓거나 개체 선택기를 사용하여 개체를 선택하여 개체를 할당할 수 있습니다.
PasswordField 사용자가 암호를 입력할 수 있는 텍스트 필드를 만듭니다.
Popup 일반 팝업 선택 필드를 만듭니다.
PrefixLabel 일부 컨트롤 앞에 레이블을 만듭니다.
ProgressBar 진행률 표시줄을 만듭니다.
PropertyField 이것을 사용하여 편집기에서 SerializedProperty에 대한 필드를 만들 수 있습니다.
RectField 직류에 들어가기 위한 X, Y, W H 필드를 만듭니다.
RectIntField RectInt를 입력하기 위한 X, Y, W H 필드를 만듭니다.
SelectableLabel 선택 가능한 레이블 필드를 만듭니다. (복사하여 붙여 넣을 수 있는 읽기 전용 정보를 표시하는 데 유용합니다.)
Slider 사용자가 드래그하여 최소값과 최대값 사이의 값을 변경할 수 있는 슬라이더를 만듭니다.
TagField 태그 선택 필드를 만듭니다.
TextArea 텍스트 영역을 만듭니다.
TextField 텍스트 필드를 만듭니다.
Toggle 토글을 만듭니다.
ToggleLeft 토글이 왼쪽에 있고 레이블이 바로 오른쪽에 있는 토글 필드를 만듭니다.
Vector2Field 벡터2를 입력하기 위한 X Y 필드를 만듭니다.
Vector2IntField Vector2Int를 입력하기 위한 X Y 정수 필드를 만듭니다.
Vector3Field Vector3을 입력하기 위한 X, Y Z 필드를 만듭니다.
Vector3IntField Vector3Int를 입력하기 위한 X, Y Z 정수 필드를 만듭니다.
Vector4Field Vector4를 입력하기 위한 X, Y, Z W 필드를 만듭니다.

 

 

4. 트리 뷰


이 페이지의 정보는 독자가 IMGUI(Immediate Mode GUI) 개념에 관한 기본 지식이 있음을 전제로 합니다. 

IMGUI와 에디터 창의 커스텀화에 대한 내용은 에디터 확장과 IMGUI Unity 블로그를 참조하십시오.

TreeView는 펼치고 접을 수 있는 계층 구조적 데이터를 나타낼 때 사용하는 IMGUI 컨트롤입니다. 

다른 IMGUI 컨트롤 및 컴포넌트와 함께 사용할 수 있는 고도로 커스텀화 가능한 리스트 뷰를 생성할 때와 에디터 창을 위한 다중 열로 구성된 표를 생성할 때 TreeView를 사용합니다.

사용 가능한 TreeView API 함수에 대한 내용은 TreeView Unity 스크립팅 API 문서를 참조하십시오.

 

[그림] MultiColumnHeader와 SearchField를 포함한 TreeView의 예


TreeView는 트리 데이터 모델이 아님에 유의하십시오. 선호하는 모든 트리 데이터 구조를 사용하여 TreeView를 구성할 수 있습니다. 

C# 트리 모델 또는 Transform 계층 구조와 같은 Unity 기반 트리 구조가 될 수 있습니다.

TreeView의 렌더링은 행이라고 하는 펼쳐지는 아이템의 리스트을 결정하여 처리됩니다. 

각 행은 하나의 TreeViewItem을 나타냅니다. 

각 TreeViewItem은 부모와 자식 정보를 포함하며 TreeView에서 내비게이션(키와 마우스 입력)을 처리하는 데 사용됩니다.

TreeView는 숨겨져 있고 에디터에는 보이지 않는 단일 루트 TreeViewItem을 가지고 있습니다. 이 아이템은 다른 모든 아이템의 루트입니다.

 

4.1 중요 클래스와 메서드


TreeView 자체를 제외한 가장 중요한 클래스는 TreeViewItem과 TreeViewState입니다.

TreeViewState (TreeViewState)에는 에디터에서 TreeView 필드와 연결할 때 변경되는 상태 정보(예: 선택 상태, 확장 상태, 내비게이션 상태, 스크롤 상태)가 들어 있습니다. 

TreeViewState는 직렬화가 가능한 유일한 상태입니다. TreeView 자체는 직렬화되지 않습니다. 

대신에 구성되거나 다시 로드될 때 표시하는 데이터에서 재구성됩니다. 

EditorWindow 파생 클래스에 TreeViewState를 필드로 추가하면 스크립트를 다시 로드하거나 재생 모드를 사용할 때에도 사용자가 변경한 상태가 손실되지 않습니다(자세한 내용은 에디터 확장에 관한 문서 참조). TreeViewState 필드가 포함된 클래스 예제는 아래의 예제 1: 간단한 TreeView를 참조하십시오.

TreeViewItem (TreeViewItem)은 개별 TreeView 아이템에 관한 데이터를 포함하며 에디터의 트리 구조 모양을 만들 때 사용됩니다. 

각각의 TreeViewItem은 고유 정수 ID(TreeView의 모든 아이템 사이에서 고유함)로 구성되어야 합니다. 

ID는 선택 상태, 확장 상태, 내비게이션 상태를 위한 아이템을 트리에서 찾는 데 사용됩니다. 

트리가 Unity 오브젝트를 나타내는 경우, GetInstanceID를 각 오브젝트를 위한 TreeViewItem의 ID로 사용합니다. 

ID는 에디터에서 스크립트 재로딩 또는 플레이 모드 시작 시 TreeViewState에서 사용자 변경 상태(펼쳐진 아이템과 같은)를 유지시키기 위해 사용됩니다.

모든 TreeViewItems는 depth 프로퍼티를 가지며, 이 프로퍼티는 그려지는 순서를 나타냅니다. 

더 많은 정보를 보려면 아래 Initializing a TreeView 예제를 참조하십시오.

BuildRoot (BuildRoot)는 TreeView를 생성하기 위해 실행해야 하는 TreeView 클래스의 단일 추상 메서드입니다. 

이 메서드를 사용하여 트리의 루트 아이템 생성을 처리합니다. 

이 메서드는 트리 Reload가 호출될 때마다 호출됩니다. 

작은 데이터 세트를 사용하는 단순한 트리를 위해서는 BuildRoot의 루트 아이템에 TreeViewItems 트리 전체를 생성합니다. 

아주 큰 트리의 경우, 트리 전체를 재로드할 때마다 생성하는 것이 최상의 방법은 아닙니다. 

이 경우에는 루트를 생성한 다음에 BuildRows 메서드를 오버라이드하여 현재 행을 위한 아이템만 생성하도록 합니다. 

BuildRoot가 사용되는 예제를 보려면 아래의 예제 1: 간단한 TreeView를 참조하십시오.

BuildRows(BuildRows)는 기본 구현이 BuildRoot에 생성된 트리 전체를 기반으로 행 리스트 구축을 처리하는 가상 메서드입니다. 

BuildRoot에 루트만 생성되었다면, 이 메서드는 확장된 행을 처리하도록 오버라이드 되어야 합니다. 

더 많은 내용은 아래 TreeView 초기화를 참조하십시오.

 


이 다이어그램은 TreeView의 수명 주기 동안 BuildRoot와 BuildRows 이벤트 메서드의 순서와 반복을 요약합니다. 

BuildRoot 메서드는 Reload가 호출될 때마다 한 번 호출되는 점에 유의하십시오. 

BuildRows는 Reload(BuildRoot 직후) 시에 한 번 그리고 TreeViewItem이 확장되거나 접힐 때마다 호출되기 때문에 더 자주 호출됩니다.

 

 

4.2 TreeView 초기화


TreeView는 Reload 메서드가 TreeView 오브젝트로부터 호출되었을 때 초기화됩니다.

TreeView를 설정하는 방법은 두 가지가 있습니.

 

  1. 트리 전체 생성 - 모든 아이템을 위한 TreeViewItem을 트리 모델 데이터에 생성합니다. 이 방법이 기본값이며 설정에 더 적은 코드를 요구합니다. 트리 전체는 BuildRoot가 TreeView 오브젝트로부터 호출되었을 때 만들어집니다.
  2. 확장된 아이템만 생성 - 이 방법은 사용자가 BuildRows를 오버라이드하여 수동으로 보여지는 행을 컨트롤해야 하고 BuildRoot는 루트 TreeViewItem 생성을 위해서만 사용해야 합니다. 이 방법은 큰 데이터 세트 또는 자주 바뀌는 데이터에 가장 적합합니다.

 

첫 번째 방법은 작은 데이터 세트 또는 자주 바뀌지 않는 데이터에 사용합니다. 

두 번째 방법은 큰 데이터 세트 또는 자주 바뀌는 데이터에 사용합니다. 

트리 전체를 생성하는 것보다 확장된 아이템만 생성하는 것이 더 빠르기 때문입니다.

TreeViewItem을 설정하는 방법은 세 가지가 있습니다.

 

  • 시작할 때 TreeViewItem을 자식, 부모, 뎁스를 초기화하여 생성합니다.
  • TreeViewItem을 부모와 자식과 함께 생성한 다음 SetupDepthsFromParentsAndChildren을 사용하여 뎁스를 설정합니다.
  • TreeViewItem을 뎁스 정보만 가지고 생성한 다음 SetupDepthsFromParentsAndChildren을 사용하여 부모와 자식에 대한 참조를 설정합니다.

 

4.3 예제

 

아래 예제에서 프로젝트와 소스 코드를 보려면 TreeViewExamples.zip을 다운로드합니다.

 

4.3.1 예제 1: 간단한 TreeView

 

 

TreeView를 생성하려면 TreeView 클래스를 확장하는 클래스를 생성하고 추상 메서드 BuildRoot를 실행합니다. 

다음 예제는 단순한 TreeView를 생성합니다.

 

class SimpleTreeView : TreeView
{
    public SimpleTreeView(TreeViewState treeViewState)
        : base(treeViewState)
    {
        Reload();
    }
        
    protected override TreeViewItem BuildRoot ()
    {
        // BuildRoot is called every time Reload is called to ensure that TreeViewItems 
        // are created from data. Here we create a fixed set of items. In a real world example,
        // a data model should be passed into the TreeView and the items created from the model.

        // This section illustrates that IDs should be unique. The root item is required to 
        // have a depth of -1, and the rest of the items increment from that.
        var root = new TreeViewItem {id = 0, depth = -1, displayName = "Root"};
        var allItems = new List<TreeViewItem> 
        {
            new TreeViewItem {id = 1, depth = 0, displayName = "Animals"},
            new TreeViewItem {id = 2, depth = 1, displayName = "Mammals"},
            new TreeViewItem {id = 3, depth = 2, displayName = "Tiger"},
            new TreeViewItem {id = 4, depth = 2, displayName = "Elephant"},
            new TreeViewItem {id = 5, depth = 2, displayName = "Okapi"},
            new TreeViewItem {id = 6, depth = 2, displayName = "Armadillo"},
            new TreeViewItem {id = 7, depth = 1, displayName = "Reptiles"},
            new TreeViewItem {id = 8, depth = 2, displayName = "Crocodile"},
            new TreeViewItem {id = 9, depth = 2, displayName = "Lizard"},
        };
            
        // Utility method that initializes the TreeViewItem.children and .parent for all items.
        SetupParentsAndChildrenFromDepths (root, allItems);
            
        // Return root of the tree
        return root;
    }
}

 

이 예제에서, 뎁스 정보는 TreeView를 만들기 위해 사용됩니다. 

마지막으로 SetupDepthsFromParentsAndChildren을 호출하면 TreeViewItem의 부모와 자식 데이터가 설정됩니다.

TreeViewItem을 설정하는 방법에는 부모와 자식을 직접 설정하거나 다음 예제에서 보여주는 바와 같이 AddChild 메서드를 사용하는 방법이 있습니다.

 

protected override TreeViewItem BuildRoot()
{
    var root = new TreeViewItem      { id = 0, depth = -1, displayName = "Root" };
    var animals = new TreeViewItem   { id = 1, displayName = "Animals" };
    var mammals = new TreeViewItem   { id = 2, displayName = "Mammals" };
    var tiger = new TreeViewItem     { id = 3, displayName = "Tiger" };
    var elephant = new TreeViewItem  { id = 4, displayName = "Elephant" };
    var okapi = new TreeViewItem     { id = 5, displayName = "Okapi" };
    var armadillo = new TreeViewItem { id = 6, displayName = "Armadillo" };
    var reptiles = new TreeViewItem  { id = 7, displayName = "Reptiles" };
    var croco = new TreeViewItem     { id = 8, displayName = "Crocodile" };
    var lizard = new TreeViewItem    { id = 9, displayName = "Lizard" };

    root.AddChild(animals);
    animals.AddChild(mammals);
    animals.AddChild(reptiles);
    mammals.AddChild(tiger);
    mammals.AddChild(elephant);
    mammals.AddChild(okapi);
    mammals.AddChild(armadillo);
    reptiles.AddChild(croco);
    reptiles.AddChild(lizard);

    SetupDepthsFromParentsAndChildren(root);

    return root;
}

 

 

4.3.1.1 위의 SimpleTreeView 클래스를 위한 얼터너티브 BuildRoot 메서드


다음 예제는 SimpleTreeView를 포함하는 EditorWindow를 보여줍니다. 

TreeView는 TreeViewState 인스턴스와 함께 구축됩니다. 

TreeView를 구현하는 사람은 뷰 상태가 어떻게 처리되어야 하는지를 정해야 합니다. 

Unity의 다음 세션까지 상태를 유지할지 아니면 스크립트가 재로드(플레이 모드를 시작하거나 스크립트를 재컴파일할 때)되었을 때만 상태를 유지할지를 정해야 합니다. 

이 예제에서는 TreeViewState가 EditorWindow에서 직렬화되어 TreeView가 에디터를 닫았다가 다시 열었을 때 상태가 유지되도록 보장합니다.

 

using System.Collections.Generic;
using UnityEngine;
using UnityEditor.IMGUI.Controls;

class SimpleTreeViewWindow : EditorWindow
{
    // SerializeField is used to ensure the view state is written to the window 
    // layout file. This means that the state survives restarting Unity as long as the window
    // is not closed. If the attribute is omitted then the state is still serialized/deserialized.
    [SerializeField] TreeViewState m_TreeViewState;

    //The TreeView is not serializable, so it should be reconstructed from the tree data.
    SimpleTreeView m_SimpleTreeView;

    void OnEnable ()
    {
        // Check whether there is already a serialized view state (state 
        // that survived assembly reloading)
        if (m_TreeViewState == null)
            m_TreeViewState = new TreeViewState ();

        m_SimpleTreeView = new SimpleTreeView(m_TreeViewState);
    }

    void OnGUI ()
    {
        m_SimpleTreeView.OnGUI(new Rect(0, 0, position.width, position.height));
    }

    // Add menu named "My Window" to the Window menu
    [MenuItem ("TreeView Examples/Simple Tree Window")]
    static void ShowWindow ()
    {
        // Get existing open window or if none, make a new one:
        var window = GetWindow<SimpleTreeViewWindow> ();
        window.titleContent = new GUIContent ("My Window");
        window.Show ();
    }
}

 

 

4.3.2 예제 2: 다중 열 TreeView

 


이 예제는 MultiColumnHeader 클래스를 사용하는 다중 열의 TreeView를 보여줍니다.

MultiColumnHeader는 아이템 이름 변경, 다중 선택, (슬라이더 및 오브젝트 필드와 같은)일반 IMGUI 컨트롤을 사용한 아이템 및 커스텀 행 콘텐츠 순서 재구성, 열 정렬, 행 필터링 및 검색과 같은 기능을 지원합니다.

이 예제는 TreeElement 및 TreeModel 클래스를 사용하여 데이터 모델을 생성합니다. 

TreeView는 이 “TreeModel”로부터 데이터를 가져옵니다. 

이 예제에서 TreeElement 및 TreeModel 클래스는 TreeView 클래스의 기능을 보여주기 위해 만들어졌습니다. 

이들 클래스는 TreeView 예제 프로젝트(TreeViewExamples.zip)에 포함되었습니다. 

예제에서는 또한 트리 모델 구조가 어떻게 ScriptableObject로 직렬화되는지와 에셋으로 저장되는지를 보여줍니다.

 

[Serializable]
//The TreeElement data class is extended to hold extra data, which you can show and edit in the front-end TreeView.
internal class MyTreeElement : TreeElement
{
    public float floatValue1, floatValue2, floatValue3;
    public Material material;
    public string text = "";
    public bool enabled = true;

    public MyTreeElement (string name, int depth, int id) : base (name, depth, id)
    {
        floatValue1 = Random.value;
        floatValue2 = Random.value;
        floatValue3 = Random.value;
    }
}


다음 ScriptableObject 클래스는 트리가 직렬화되었을 때 에셋의 데이터가 보존되도록 보장합니다.

 

[CreateAssetMenu (fileName = "TreeDataAsset", menuName = "Tree Asset", order = 1)]
public class MyTreeAsset : ScriptableObject
{
    [SerializeField] List<MyTreeElement> m_TreeElements = new List<MyTreeElement> ();

    internal List<MyTreeElement> treeElements
    {
        get { return m_TreeElements; }
        set { m_TreeElements = value; }
    }
}

 

 

4.4 MultiColumnTreeView 클래스 구축

 

다음 예제는 다중 열 GUI를 어떻게 얻는지 보여주는 MultiColumnTreeView 클래스의 일부분을 보여줍니다.

소스 코드 전체는 TreeView 예제 프로젝트(TreeViewExamples.zip)에 있습니다.

 

public MultiColumnTreeView (TreeViewState state, 
                            MultiColumnHeader multicolumnHeader, 
                            TreeModel<MyTreeElement> model) 
                            : base (state, multicolumnHeader, model)
{
    // Custom setup
    rowHeight = 20;
    columnIndexForTreeFoldouts = 2;
    showAlternatingRowBackgrounds = true;
    showBorder = true;
    customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f; 
    extraSpaceBeforeIconAndLabel = kToggleWidth;
    multicolumnHeader.sortingChanged += OnSortingChanged;
            
    Reload();
}


위 코드 샘플의 커스텀 변경사항은 다음 사항을 조정합니다.

 

  • rowHeight = 20: 기본 높이(EditorGUIUtility.singleLineHeight의 16포인트를 기반으로 함)를 20으로 바꿔 GUI 컨트롤을 위해 더 많은 공간을 추가합니다.
  • columnIndexForTreeFoldouts = 2: 예제에서 이 값이 2로 설정되어 있기 때문에 폴드아웃 화살표는 세 번째 열에 보입니다. (위 그림 참조)이 값이 바뀌지 않으면, “columnIndexForTreeFoldouts”가 기본적으로 0이기 때문에, 폴드아웃은 첫 번째 열에 렌더링됩니다.
  • showAlternatingRowBackgrounds = true: 각 행이 구별될 수 있도록 교대로 다른 행 배경 컬러를 활성화합니다.
  • showBorder = true: TreeView를 마진으로 둘러 렌더링하여 얇은 테두리로 나머지 콘텐츠와 구분할 수 있게 합니다.
  • customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f: 센터가 행에 수직으로 폴드아웃 - 아래 GUI 커스터마이징를 참조하십시오.
  • extraSpaceBeforeIconAndLabel = 20: 토글 버튼이 보이도록 트리가 레이블하기 전에 공간을 만듭니다.
  • multicolumnHeader.sortingChanged += OnSortingChanged: 헤더 컴포넌트의 정렬이 언제 바뀌는지(헤더 열이 클릭된 경우)를 감지하도록 이벤트에 메서드를 지정하여 TreeView의 행이 정렬 상태를 반영하여 바뀌게 합니다.

 

 

4.5 GUI 커스텀화


디폴트 RowGUI 처리가 사용되면 TreeView는 폴드아웃과 레이블만 있는 위의 SimpleTreeView 예제처럼 보입니다. 

각 아이템을 위한 다수의 데이터 값을 사용할 때는 이러한 값을 시각화하기 위해 RowGUI 메서드를 오버라이드해야 합니다.

보호된 override void RowGUI(RowGUIArgs args)
다음 코드 샘플은 RowGUIArgs 구조의 인수 구조입니다.

 

protected struct RowGUIArgs
{
    public TreeViewItem item;
    public string label;
    public Rect rowRect;
    public int row;
    public bool selected;
    public bool focused;
    public bool isRenaming;

    public int GetNumVisibleColumns ()
    public int GetColumn (int visibleColumnIndex)
    public Rect GetCellRect (int visibleColumnIndex)
}

 

TreeViewItem을 확장할 수 있으며 추가 사용자 데이터(TreeViewItem으로부터 파생되는 클래스 생성)를 추가할 수 있습니다.

그런 다음 RowGUI 콜백 안에서 사용자 데이터를 사용할 수 있습니다.

예제는 아래에 있습니다.

override void RowGUI를 참조하십시오.

이 예제는 입력 아이템을 TreeViewItem<MyTreeElement>에 캐스트합니다.

열 처리와 관련된 메서드는 다음의 세 가지가 있습니다. 

GetNumVisibleColumns, GetColumn, and GetCellRect. TreeView가 MultiColumnHeader로 구축되었을 때만 위 메서드를 호출할 수 있으며, 그렇지 않은 경우 예외가 발생합니다.

 

protected override void RowGUI (RowGUIArgs args)
{
    var item = (TreeViewItem<MyTreeElement>) args.item;

    for (int i = 0; i < args.GetNumVisibleColumns (); ++i)
    {
        CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
    }
}

 

void CellGUI (Rect cellRect, TreeViewItem<MyTreeElement> item, MyColumns column, ref RowGUIArgs args)
{
    // Center the cell rect vertically using EditorGUIUtility.singleLineHeight.
// This makes it easier to place controls and icons in the cells.
    CenterRectUsingSingleLineHeight(ref cellRect);

    switch (column)
    {

        case MyColumns.Icon1:
            
            // Draw custom texture
GUI.DrawTexture(cellRect, s_TestIcons[GetIcon1Index(item)], ScaleMode.ScaleToFit);
            break;

        case MyColumns.Icon2:

//Draw custom texture 
            GUI.DrawTexture(cellRect, s_TestIcons[GetIcon2Index(item)], ScaleMode.ScaleToFit);
            break;

        case MyColumns.Name:

            // Make a toggle button to the left of the label text
            Rect toggleRect = cellRect;
            toggleRect.x += GetContentIndent(item);
            toggleRect.width = kToggleWidth;
            if (toggleRect.xMax < cellRect.xMax)
                item.data.enabled = EditorGUI.Toggle(toggleRect, item.data.enabled); 

            // Default icon and label
            args.rowRect = cellRect;
            base.RowGUI(args);
            break;

        case MyColumns.Value1:

// Show a Slider control for value 1
            item.data.floatValue1 = EditorGUI.Slider(cellRect, GUIContent.none, item.data.floatValue1, 0f, 1f);
            break;

        case MyColumns.Value2:

// Show an ObjectField for materials
            item.data.material = (Material)EditorGUI.ObjectField(cellRect, GUIContent.none, item.data.material, 
                                          typeof(Material), false);
            break;

        case MyColumns.Value3:

// Show a TextField for the data text string
            item.data.text = GUI.TextField(cellRect, item.data.text);
            break;
    }
}

 

 

4.6 TreeView 자주 묻는 질문


Q: TreeView 서브클래스에 함수 BuildRoot와 RowGUI가 있습니다. RowGUI가 빌드 함수에 추가된 모든 TreeViewItem을 위해 호출됩니까? 아니면 스크롤 뷰에서 화면에 보이는 아이템을 위해서만 호출됩니까?

A: RowGUI는 화면에 보이는 아이템을 위해서만 호출됩니다. 예를 들어, 10,000개의 아이템이 있다면, 화면에 보이는 20개 아이템만 RowGUI를 호출합니다.

 


Q: 화면에 보이는 행의 인덱스를 얻을 수 있습니까?

A: 네, GetFirstAndLastVisibleRows 메서드를 사용하면 됩니다.

 

Q: BuildRows에 만들어진 행의 리스트를 얻을 수 있습니까?

A: 네, GetRows 메서드를 이용합니다.

 

Q: 모든 오버라이드된 함수가 base.Method를 호출해야 됩니까?

A: 메서드가 사용자가 확장하고자 하는 기본 동작이 있는 경우에만 그렇습니다.

 

Q: 트리가 아닌 아이템 리스트만 만들고 싶습니다. 루트를 생성해야 합니까?

A: 네, 언제나 루트가 있어야 합니다. 루트 아이템을 생성한 다음 root.children = rows로 설정하여 빠르게 설정할 수 있습니다.

 

Q: 행에 토글을 추가했습니다. 왜 클릭했을 때 메시지가 해당 행으로 가지 않습니까?

A: 기본적으로, 마우스로 누를 때 행의 콘텐츠가 사용되지 않는 경우에만 행을 선택할 수 있습니다. 이 경우에는 토글이 이벤트를 사용합니다. 이 문제를 해결하려면 SelectionClick 메서드를 사용한 후 토글 버튼을 호출하십시오.

 

Q: 모든 RowGUI 메서드가 호출되기 전 또는 후에 사용할 수 있는 메서드가 있습니까?

A: 네, API 관련 문서 BeforeRowsGUI 및 AfterRowsGUI를 참조하십시오.

 

Q: 키 포커스를 API에서 TreeView로 돌리는 간단한 방법이 있습니까? 행에서 FloatField를 선택하면, 선택된 행은 회색으로 변합니다. 어떻게 다시 파란색으로 바꿀 수 있습니까?

A: 파란색은 어느 행에 키 포커스가 있는지를 나타냅니다. FloatField에 포커스가 있기 때문에, TreeView는 포커스를 잃게 되므로, 이것은 의도된 동작입니다. 필요한 경우에는 GUIUtility.keyboardControl = treeViewControlID로 설정합니다.

 

Q: 어떻게 id에서 TreeViewItem으로 전환할 수 있습니까?

A: FindItem 또는 FindRows를 사용합니다.

 

Q: 사용자가 TreeView에서 선택한 것을 바꿨을 때, 어떻게 콜백을 받습니까?

A: SelectionChanged 메서드를 오버라이드합니다(기타 유용한 콜백: DoubleClickedItem 및 ContextClickedItem).

 

 

 

728x90
반응형