in 소프트웨어 개발

유니티 에디터 확장 – 메뉴 항목들

원글: http://unity3d.com/kr/learn/tutorials/modules/intermediate/editor/menu-items?playlist=17090

번역: i_jemin@hotmail.com (@I_Jemin)

유니티 에디터에서 빌트-인 메뉴 처럼 보이고 동작하는 커스텀 메뉴들을 추가할수 있다. 이는 자주 사용되는 기능을 에디터 UI상에서 바로 접근하고 싶을 때 매우 유용하다. 이번 강의에서, 나는 어떻게 유니티 에디터에 새로운 메뉴 아이템들이 추가되는지, 그리고 여러 상황에서 현업에서는 어떻게 사용하는지 예시를 제공할 것이다.

메뉴 항목들을 추가

유니티에서 화면 상단의 툴바에 새로운 메뉴를 추가하려면, 당신은 반드시 에디터 스크립트 ( 프로젝트 하위의 Editor 라는 폴더 아래 어디든지 위치한 파일)를 생성해야 한다. 메뉴 항목들은 스크립트 코드에서 MenuItem 속성으로 지정된 정적 메소드로 작성된다.

예를 들어, 팀이나 회사에서 사용할 “도구” 메뉴를 (혹은 회사 이름으로 된 최상위 메뉴)를 추가하는 것은 흔한 일이다.

여기에, 하위에 (모든 PlayerPrefs 데이터를 지우는)옵션을 가진, 새로운 도구 메뉴를 만드는 예제가 있다:

코드 일부

using UnityEngine;
using UnityEditor;
 
public class MenuItems
{
    [MenuItem("Tools/Clear PlayerPrefs")]
    private static void NewMenuOption()
    {
        PlayerPrefs.DeleteAll();
    }
}

이것은 Tools 라는 새로운 에디터 메뉴를 추가하고, 그 아래에 Clear PlayerPrefs 라는 하위 메뉴 항목을 만든다.

또한 이미 존재하는 메뉴 아래 (예: Window 메뉴 아래)에 새 메뉴 항목을 추가할 수 있고, 또 여러 단계의 메뉴들을 만들어 항목을 더 구조화하고 조직화 할수 있다. (그러길 추천한다):

코드 일부

using UnityEngine;
using UnityEditor;
 
public class MenuItemsExample
{
    // 이미 존재하는 메뉴 아래 새 메뉴 항목을 추가한다
 
    [MenuItem("Window/New Option")]
    private static void NewMenuOption()
    {
    }
 
    // 여러 단계의 메뉴 집합을 만들고 메뉴 항목을 추가할 수 있다.
 
    [MenuItem("Tools/SubMenu/Option")]
    private static void NewNestedOption()
    {
    }
}

이는 아래와 같은 메뉴 항목을 생성한다:

단축키

파워 유저와 키보드 매니아들이 더 빠르게 작업하도록, 새 메뉴 항목들에게 단축키를 할당 할 수 있다. 단축키는 키들의 조합으로, 자동으로 기능을 실행한다.

지원 되는 키들 (물론 함께 조합될 수 있다):

  • % – CTRL (윈도우의 경우) / CMD (OS X 의 경우)
  • # – Shift
  • & – Alt
  • LEFT/RIGHT/UP/DOWN – 방향키들
  • F1…F2 – 펑션키들
  • HOME, END, PGUP, PGDN

키-열거에 포함되지 않은 문자 키들은 언더스코어 접두사를 붙여 더할 수 있다. (예: _g 는 “G” 단축키를 나타낸다).

단축키 문자 조합은 메뉴 항목 경로의 끝에 스페이스로 띄어 추가한 다음 ) 로 닫는다. 아래에 예가 있다:

코드 일부

// CTRL-SHIFT-A 조합의 단축키를 사용하는 새 메뉴를 추가한다
 
[MenuItem("Tools/New Option %#a")]
private static void NewMenuOption()
{
}
 
// CTRL-G 조합의 단축키를 사용하는 새 메뉴를 추가한다
 
[MenuItem("Tools/Item %g")]
private static void NewNestedOption()
{
}
 
// 단축키 G를 사용하는 새 메뉴를 추가한다
[MenuItem("Tools/Item2 _g")]
private static void NewOptionWithHotkey()
{
}

단축키가 추가된 메뉴 항목들은 그것을 실행하기 위한 키조합을 보여준다. 예를 들어, 위의 코드는 아래와 같은 메뉴를 만든다:

메모: 단축키 중복에 대한 검증 절차가 없다! 같은 단축키로 여러 메뉴 항목을 정의하여도 단 하나의 항목만이 단축키를 눌렀을 때 호출된다

특별 경로

보듯이, MenuItem 속성에 입력된 경로가 어떤 최상위 메뉴 아래 새로운 항목이 위치할지 제어한다.

유니트는 컨텍스트 메뉴로 동작하는 몇가지 “특별한” 경로가 있다 (오른쪽 클릭으로 접근할 수 있는 메뉴):

  • Assets – “Assets” 메뉴 아래 항목이 생긴다. 프로젝트 뷰에서 마우스 오른쪽을 눌렀을 때도 마찬가지다.
  • Assets/Create – 항목이 프로젝트 뷰에서 Create 버튼을 눌렀을 때 나오는 목록에 추가된다. (프로젝트에서 추가될 수 있는 새로운 타입을 더할 때 유용하다)
  • CONTEXT/ComponentName – 주어진 컴포넌트의 인스펙터에서 마우스 오른쪽을 눌렀을 때 항목이 띄어진다.

여기 특별 경로를 사용하는 몇가지 예가 있다:

코드 일부

// 프로젝트 뷰에서 애셋에 마우스 오른쪽을 눌러 접근할 수 있는 새로운 메뉴 항목을 만든다
 
[MenuItem("Assets/Load Additive Scene")]
private static void LoadAdditiveScene()
{
    var selected = Selection.activeObject;
    EditorApplication.OpenSceneAdditive(AssetDatabase.GetAssetPath(selected));
}
 
// Assets/Create 하위에 새로운 메뉴 항목을 만든다
 
[MenuItem("Assets/Create/Add Configuration")]
private static void AddConfig()
{
    // 설정값을 저장하기 위한 새 ScriptableObject를 생성하고 추가한다
}
 
// 리기드바디 컴포넌트에 마우스 오른쪽 클릭으로 접근할 수 있는 새 메뉴 항목을 더한다
 
[MenuItem("CONTEXT/Rigidbody/New Option")]
private static void NewOpenForRigidBody()
{
}

위 코드 단편들이 아래의 새 메뉴 항목들 생성했다:


Assets (프로젝트 뷰) 마우스 오른쪽 메뉴


Asset’s CREATE 버튼에서 가능한 새 항목이 추가됨


리기드바디의 컨텍스트 메뉴에서 가능해진 새 메뉴 항목

검증

몇 메뉴 항목들은 주어진 맥락에서만 의미가 있으며, 다른 곳에서는 사용할 수 없어야 한다. 검증 메소드를 추가하여 사용처에 따라 메뉴 항목을 활성/비활성화 할 수 있다.

검증 메소드는 정적 메소드이며, MenuItem 속성으로 지정되어 검증 조건에 참을 입력받는다.

검증 메소드는 검증하고자 하는 메뉴와 같은 메뉴 경로를 가져야 하며, 메뉴 항목을 활성화 할지, 그러지 않을지 결정하기 위해 불리언 값을 반환해야 한다

예를 들어, 검증 메소드는 프로젝트 뷰 안에서만 Texture 애셋에 마우스 오른쪽 메뉴를 쓸 수 있도록 하는데 사용할 수 있다:

Code snippet

[MenuItem("Assets/ProcessTexture")]
private static void DoSomethingWithTexture()
{
}
 
//같은 경로를 입력했다는 것에 주의해라, 또한 "true"를 두번째 입력에 지정한 것을 보라
[MenuItem("Assets/ProcessTexture", true)]
private static bool NewMenuOptionValidation()
{
    // 선택한 오브젝트가 Texture2D인 경우에만 true를 반환한다 (이외의 경우에는 메뉴 항목이 비활성화 된다)
    return Selection.activeObject.GetType() == typeof(Texture2D);
}

프로젝트 뷰에서 텍스쳐가 이닌 것에 마우스 오른쪽을 누르면, 해당 메뉴 항목이 비활성화 된다 (회색으로 표시된다):

우선순위 제어하기

메뉴항목에 우선순위를 할당해 (MenuItem 속성에 입력한다) 최상위 메뉴 아래 메뉴 항목들의 순서를 제어할 수 있다

또한 할당된 우선순위를 50 단위로 묶어, 메뉴 항목들이 자동으로 분류된다:

코드 일부

[MenuItem("NewMenu/Option1", false, 1)]
private static void NewMenuOption()
{
}
 
[MenuItem("NewMenu/Option2", false, 2)]
private static void NewMenuOption2()
{
}
 
[MenuItem("NewMenu/Option3", false, 3)]
private static void NewMenuOption3()
{
}
 
[MenuItem("NewMenu/Option4", false, 51)]
private static void NewMenuOption4()
{
}
 
[MenuItem("NewMenu/Option5", false, 52)]
private static void NewMenuOption5()
{
}

위 코드의 결과, 아래 처럼 메뉴의 항목들이 우선순위에 따라 두개의 그룹으로 나뉘었다:

프로퍼티를 사용하는 대부분의 빌트-인 메뉴 항목들 처럼, 이미 있는 유니티 메뉴 아래 메뉴 항목을 추가하고 분류하려면 일종의 “기능을 예측하는 작업”이 필요하다. 다른 방법으로는, 리플렉터 같은 도구를 사용하여, (UnityEditor.CreateBuildInWindows 같은) 유니티 코드 내부에서 에디터에서 메뉴 생성을 담당하는 소스 코드를 보는 것이다.

연관 클래스

하단에 새로운 메뉴를 생성하는 것과 연관된 몇가지 추가 클래스들의 리스트가 있다.

MenuCommand

인스펙터에 새로운 메뉴 항목을 (위의 예시처럼, “CONTEXT/Component”를 사용해서) 추가할 때, 때로는 실제 컴포넌트의 레퍼런스가 필요하다. (예: 그것의 데이터를 수정할 때)

이것은 새 메뉴 항목을 정의하는 정적 메소드에 MenuCommand 입력을 더하여 할 수 있다

코드 일부

[MenuItem("CONTEXT/RigidBody/New Option")]
private static void NewMenuOption(MenuCommand menuCommand)
{
    // 이 리기드바디 컴포넌트는 컨텍스트 필드를 사용하는 메뉴 커맨드로 부터 추출될 수 있다
    var rigid = menuCommand.context as RigidBody;
}

예제 코드에서 보듯이, 새로운 메뉴 항목을 실행할 때, 컨택스트 필드를 사용하여 의도한대로 사용될 컴포넌트에 접근할 수 있다.

ContextMenu

이 속성은 컨텍스트 메뉴 항목을 정의한다. “CONTEXT/…” 경로로 시작하며, MenuItem 속성과 동일하게 작업하면 된다

이 속성의 차이점은, 주어진 컴포넌트에 기본 컨텍스트를 정의하며, 그렇기에 MenuItem 과 비교해서, 다른 컴포넌트의 메뉴들을 “확장” 할 수 있다. (예를 들어 – 엔진에 포함된 기본 컴포넌트들)

예 – 자신의 데이터를 지우기 위한 컨텍스트 메뉴 항목을 노출하는 컴포넌트:

코드 일부

public class NameBehaviour : MonoBehaviour
{
    public string Name;
 
    [ContextMenu("Reset Name")]
    private static void ResetName()
    {
        Name = string.Empty;
    }
}

ContextMenuItem

이 속성은 컴포넌트 (MonoBehaviour의) 클래스의 필드에 더해져, 컨텍스트 메뉴를 더 깊은 방법으로 더해준다. 위에서 본 ContextMenu 속성이 컨텍스트 메뉴를 컴포넌트 단계에서 추가하는 반면에, 필드들을 이 속성으로 지정하는 경우 각자의 public 필드들에게 마우스 오른쪽 메뉴를 추가해준다.

이 속성은 메소드가 아니라 필드에 추가되는 것이므로, 두가지 입력을 받는다: 메뉴 항목의 표시 이름과 해당 메뉴 항목이 선택됬을 때 실행할 메소드 (인스턴스 메소드)의 이름.

예 – 컴포넌트의 필드를 임의값으로 초기화 하는 메소드를 추가하는 경우:

코드 일부

public class NameBehaviour : MonoBehaviour
{
    [ContextMenuItem("Randomize Name", "Randomize")]
    public string Name;
 
    private void Randomize()
    {
        Name = "Some Random Name";
    }
}

위 코드는 이 컴포넌트의 필드 이름위에 마우스 오른쪽을 눌렀을 때 해당 컨텍스트 메뉴를 만든다:

결론

이 글에서 보았듯이, 유니티 에디터를 커스텀 메뉴들로 확장하는 것은 쉽다.

자주 사용되는 기능을 구현하여 에디터에서 사용하게 하는 것은, 모든 규모의 팀에서 추천되며, 많은 시간을 아끼게 해준다.

관련 문서