제2장 표준에서 사용하는 에디터 확장기능

2019. 7. 27. 23:16유니티/(Old)에디터 확장

 

Unity エディター拡張入門

Web版無償公開中!Unityエディター拡張の入門書!

anchan828.github.io

유니티 에디터는 이미 내부에서 에디터 확장 기능을 사용하며, 확장에 대해서 여러가지 기능을 제공하고 있습니다. 사용자는 속성(Attribute)을 지정하는 것으로 확장에 관련된 기능을 사용할 수 있으며, 자유롭게 커스텀 마이징을 할 수 있습니다. 이 장에서는 유니티 에디터에서 제공해주는 기능을 사용해서 「에디터의 확장이 이런 것이며 이런 것이 가능하구나」와 같은 기본 개념을 잡아가는 것을 목표로 합니다.

2.1 인스펙터의 외관 변경

Range

int, float, long, double과 같은 수치를 슬라이드로 변경하기 위한 기능입니다.

개발 때 「특정 값」을 설정할 때 「변수 근처에 마우스로 드래그」를 하여 값을 설정하는 경우가 있습니다. 하지만 이런 식으로 표시하는 것은 불편합니다. 이를 개선하기 위하여 인스펙터에 조작성을 설정하는 것 입니다.

그림 2.1 : 좌 디폴트, 우 Range로 변경한 인스펙터

그림2.1: 좌 디폴트, 우 Range로 변경한 인스펙터

using UnityEngine;
using System.Collections;
 
public class NewBehaviourScript : MonoBehaviour
{
    [Range(110)]
    public int num1;
 
    [Range(110)]
    public float num2;
 
    [Range(110)]
    public long num3;
 
    [Range(110)]
    public double num4;
}

Multiline / TextArea

디폴트로는 한 행만 제공해주는 TextField 이지만, 복수 행을 제공하는 TextArea로 변경할 수 있습니다. Muitiline과 TextArea는 거의 같은 기능을 제공하지만 Muitiline은 폭에 맞추어서 자동개행을 하지 않고 스크롤 바가 표시되지 않기에 특별한 이유가 없다면 TextArea를 사용하는 것을 추천합니다.

그림 2.2 : 위가 Multiline, 밑이 TextArea는 스크롤 바가 표시됩니다.

using UnityEngine;
using System.Collections;
 
public class NewBehaviourScript : MonoBehaviour
{
    [Multiline(5)]
    public string multiline;
 
    [TextArea(35)]
    public string textArea;
}

2.2 인스펙터에서 다루는 기능을 추가

ContextMenuItem

인스펙터에 표시되는 변수에 컨텍스트 메뉴를 추가합니다. 순서가 필요한 요소를 자동화하거나 다른 변수도 같이 값을 변경할 경우 컨텍스트 메뉴를 사용하면 효율적입니다. 혹은 컴포넌트 단위로 값을 리셋할 수 있는 「Reset」기능이 있습니다만 각 변수에 대응하는 리셋의 기능이 없으므로 ContextMenuItem으로 처리하면 효율적입니다.

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    [ContextMenuItem("Random""RandomNumber")]
    [ContextMenuItem ("Reset""ResetNumber")]
    public int number;
 
    void RandomNumber()
    {
        number = Random.Range(0100);
    }
 
    void ResetNumber()
    {
        number = 0;
    }
}

첫 번째 매개인자는 컨텍스트 메뉴 아이템의 이름이고 두 번째 매개인자는 호출할 함수명입니다.

컨텍스트 메뉴를 클릭하면 Random의 이름을 가진 항목이 생성되고 Random을 클릭하면 RandomNumber을 호출합니다.

ColorUsage

색상 변경에는 색상 변경기를 사용합니다. ColorUsage는 색상 변경기로 알파 사용을 유효/무효, HDR 용 색상 처리기로 변경할 수도 있습니다.

그림 2.4 : 왼쪽부터 디폴트, 알파 없음, HDR 컬러

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    public Color color1;
 
    [ColorUsageUsage(false)]
    public Color color2;
 
    [ColorUsage(truetrue080.125f, 3)]
    public Color color3;
}

첫 번째 매개인자가 true이면 알파 사용, false이면 알파를 사용하지 않는 것을 의미합니다.

두 번째 매개인자가 true이면 HDR 사용, false이면 HDR를 사용하지 않는 것을 의미합니다.

세 번째 매개인자와 네 번째 매개인자는 Brightness의 최소, 최대 값을 의미합니다.

다섯 번째 매개인자와 여섯 번째 매개인자는 Exposure의 최소, 최대 값을 의미합니다.

2.3 인스팩터의 외관을 갖추기

프로퍼티에 직접적으로 작용하지는 않지만 외관을 알기 쉽게 하거나 꾸밀 수 있습니다.

그림 2.5 : 헤더를 표시하는 것으로 단락을 구분지어 표시함으로써보기 좋게 표시합니다.

using UnityEngine;
using System;
 
public class NewBehaviourScript : MonoBehaviour
{
    [Header("Player Settings")]
    public Player player;
 
    [Serializable]
    public class Player
    {
        public string name;
 
        [Range(1100)]
        public int hp;
    }
 
    [Header("Game Settings")]
    public Color background;
}

Space

세로에 여백을 주는 것이 가능합니다. 프로퍼티 사이에 여백을 줄 때 편리합니다.

그림 2.6

using UnityEngine;
 
public class NewBehaviourScript : ​MonoBehaviour
{
    [Space(16)]
    public string str1;
 
    [Space(48)]
    public string str2;
}

Tooltip

프로퍼티에 대한 설명을 인스펙터 위에 확인하고 싶을 경우 사용합니다.

그림 2.7 : Tooltip에 마우스 커서를 올리면 툴 팁이 표시됩니다.

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    [Tooltip("이것은 툴 팁입니다.")]
    public long tooltip;
}

HideInInspector

HideInInspector는 인스펙터 창에 변수를 표시하지 않을 때 사용합니다.

그림 2.8 : 원래 public 변수는 인스펙터에 표시되지만 str2는 표시되지 않습니다.

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    public string str1;
 
    [HideInInspector]
    public string str2;
}

2.4 인스팩터를 더 편리하게 하기

RequireComponent

「반드시 하나의 컴포넌트를 부착하지 않으면 안된다」 와 같은 제한을 걸기 위한 속성입니다.

스크립트를 작성할 때 반드시 필요한 컴포넌트가 종종 있습니다. 예를 들면 Animator에 대한 스크립트를 작성할 경우 반드시 Animator 컴포넌트가 필요합니다. 필수인 컴포넌트를 부착하는 것을 잊어버렸을 경우 당연히 에러가 발생합니다. 이 에러를 사전에 방지하기 위하여 RequireComponent를 사용합니다.

RequireComponent가 붙은 스크립트를 부착하면 자동적으로 RequireComponent로 지정한 컴포넌트를 부착합니다. 이미 붙어있는 경우라면 아무 것도 하지 않습니다.그리고 지정한 컴포넌트를 소거할 경우 「소거할 수 없습니다」와 같은 내용의 다이얼 로그를 표시합니다.

그림 2.9 : Animator 컴포넌트를 소거하려고 하면 다이얼 로그가 표시됩니다.

using UnityEngine;
 
[RequireComponent(typeof(Animator))]
public class NewBehaviourScript : MonoBehaviour
{
    Animator animator;
 
    void Awake()
    {
        animator = GetComponent<Animator>();
    }
}

DisallowMultipleComponent

하나의 게임 객체에 같은 컴포넌트를 여러 개 부착하는 것을 금지하는 속성입니다.

하나만 부착하면 되는데 실수로 같은 컴포넌트를 2개 이상 부착해버린 경우가 있습니다. 2개 이상 부착된 상태로 게임을 재생하면 제대로 동작하지 않습니다. 「코드는 제대로 작성되어있는데 제대로 동작하지 않네」와 같은 원인 불명의 상태에 빠지게 됩니다. 이 문제를 해결하기 위해서 DisallowMultipleComponent를 사용합니다.

그림 2.10 : 같은 스크립트를 부착하려고 하면 다이얼 로그가 표시됩니다.

상속 관계에 있는 클래스에서도 DisallowMultipleComponent의 효과가 발휘됩니다.

using UnityEngine;
 
[DisallowMultipleComponent]
public class Base : MonoBehaviour
{
}
 
public class NewBehaviourScript : Base
{
}

FormerlySerializedAs

변수명을 변경하고 싶을 경우 새로운 변수명에 데이터를 이동을 처리하는 속성입니다.

인스펙터에 표시되는 Serialize데이터는 변수명을 경로로 삼아서 보존합니다. 여기서 변수명을 변경하면 Serialize된 데이터는 초기화가 됩니다.이 것은 경로부터 값을 참조하지 않기 떄문입니다. 변수명이 변경되는 것을 일반적으로 알아내는 것은 어려우므로 FormerlySerializedAs를 사용하여 해결합니다.

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    [SerializeField]
    string hoge;
}

이 스크립트를 컴파일되면 인스펙터에 값을 입력합니다.

hogo에서 fuge로 변수명을 변경합니다.

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    [SerializeField]
    string fuge;
}

이러면 인스펙터에 입력한 문자열은 보존되지 않고 아무것도 표시되지 않습니다.

그렇기에 FormerlySerializedAs를 사용하는 것으로 보존할 수 있습니다.

 

using UnityEngine;
using UnityEngine.Serialization;
public class NewBehaviourScript : MonoBehaviour
{
    [SerializeField]
    [FormerlySerializedAs("hoge")]
    string fuge;
}

AddComponentMenu

Component 메뉴에 메뉴 항목을 추가합니다.

밑의 그림에서 관련된 스크립트들은 전부 Component/Scripts 메뉴 안에 정리되어있습니다. 범용적인 스크립트 컴포넌트를 작성할 경우 하나의 카테고리에 정리하고 싶을 경우가 있습니다. 예를 들면 UI에 대한 스크립트를 작성할 경우 입니다.

그림 2. 11 : TweenColor/Position/Rotation/Scale.cs를 작성한 상태

이 때는 AddComponentMenu를 사용하면 별도의 메뉴로 작성할 수 있습니다. AddComponentMenu를 사용한 스크립트는 다른 장소의 메뉴가 작성되어 Component/Scripts 메뉴의 안에서 삭제할 수 있습니다.

using UnityEngine;
 
[AddComponentMenu("MyUI/Tween Color")]
public class TweenColor : MonoBehaviour
{
}

그림 2.12 : My UI밑으로 이동한 목록입니다.

2.5 게임 개발을 편하게 하기

ExecuteInEditMode

게임 실행 중이 아니라도 MonoBehaviour을 계승한 컴포넌트의 주요 함수를 불러올 수 있습니다. 불러올 타이밍은 게임 객체가 갱신될 때 입니다. 씬 에셋을 더블 클릭해서 씬을 불러왔을 때는 Awake 와 Start 함수가 인스펙터에서 컴포넌트의 변수를 갱신하거나 Update 함수를 불러옵니다. 혹은 OnGUI에 구현된 GUI가 에디터의 GUI 화면 싸이클의 토대로 표시됩니다. 이와 같이 ExecuteInEditMode를 속성으로 둔 스크립트는 게임이 실행 중이 아니라도 함수를 갱신하도록 합니다.

using UnityEngine;
 
[ExecuteInEditMode]
public class NewBehaviourScript : MonoBehaviour
{
    [Range(010)]
    public int number;
 
    void Awake()
    {
        Debug.Log("Awake");
    }
    
    void Start()
    {
        Debug.Log("Start");
    }
 
    void Update()
    {
        Debug.Log("Update");
    }
}

ContextMenu

컴포넌트의 컨텍스트 메뉴에서 함수를 실행할 수 있습니다. ContextMenuItem과 비슷하지만, 컨텍스트 메뉴의 등장하는 장소가 다릅니다.

그림 2.13 : 컴포넌트의 톱니바퀴를 클릭 혹은 컴포넌트의 타이틀을 오른쪽 클릭

using UnityEngine;
 
public class NewBehaviourScript : MonoBehaviour
{
    [Range(010)]
    public int number;
 
    [ContextMenu("RandomNumber")]
    void RandomNumber()
    {
        number = Random.Range(0100);
    }
 
    [ContextMenu("ResetNumber")]
    void ResetNumber()
    {
        number = 0;
    }
}

SelectionBase(2017 버전에서는 제대로 동작이 하지 않습니다.)

씬 뷰로 객체를 선택했을 때 「선택된 게임 객체를 지정한다」 「게임 객체의 선택순서를 결정한다」와 같은 경우에 사용합니다.

보통 씬 뷰에서 객체를 선택할 때는 게임 객체의 Root가 선택됩니다.

그림 2.14 : 씬 뷰에서 Cube를 마우스로 클릭한 직후의 상태

다음 스크립트를 기술하고 하위 자식의 게임 객체에 부착합니다.

using UnityEngine;
 
[SelectionBase]
public class NewBehaviourScript : MonoBehaviour
{
}

그러면 루트의 게임 객체를 클릭하면 하위 자식의 게임 객체가 선택됩니다.

그림 2.15 : 큐브를 마우스로 클릭한 직후의 상태

그리고 한 번 더 게임 객체를 클릭하면 루트의 게임 객체가 선택됩니다. 이와 같이 SelectionBase 속성을 부착한 게임 객체로 최하층의 자식 요소 순으로 선택할 수 있습니다. 순서에 따라 선택해 가면 SelectionBase 속성을 부착한 게임 객체가 존재하지 않는 경우 루트를 선택하도록 되어있습니다.

그림 2.16 : 클릭하는 것으로 하위 순으로 선택됩니다.

 

'유니티 > (Old)에디터 확장' 카테고리의 다른 글

제 5장 SerializedObject에 대해서  (0) 2019.07.28
제4장 ScriptableObject  (0) 2019.07.28
제3장 데이터 보존  (0) 2019.07.28
제 1장 에디터 확장에서 사용되는 폴더  (0) 2019.07.27
개요  (0) 2019.07.27