第8章 MenuItem - エディター拡張入門

第8章 MenuItem

知らないとエディター拡張では何もできないと言っても過言ではないほど重要な MenuItem について解説します。この章では MenuItem でできることを紹介していきます。「実際にどう使っていくか」というのはこの章でも軽く説明はしますがこの章以外でも「嫌というほど」扱っていきますので自然と理解していくでしょう。

8.1 MenuItem とは

MenuItem は「Unity エディターの上側にあるメニューバーやコンテキストメニューに項目を追加するための機能」です。

「独自に追加した〜」という部分が追加されたメニュー項目

図8.1: 「独自に追加した〜」という部分が追加されたメニュー項目

「スクリプトに追加した項目」という部分が追加されたコンテキストメニュー項目

図8.2: 「スクリプトに追加した項目」という部分が追加されたコンテキストメニュー項目

8.2 MenuItem の使いドコロ

メニューはなんらかのトリガーとなっています。Unity 標準では

  • ゲームオブジェクトの生成 ( GameObject/Create Empty )
  • シーンの作成 ( File/New Scene )
  • ウィンドウの表示 ( Window/Inspector )

などがメニューとしてすでに登録されています。

ユーザーが MenuItem を使用して独自のメニューを追加する目的としては、任意のタイミングで Editor スクリプトを実行するときです。目的別に取り上げるなら

  • 独自ウィンドウの表示
  • AssetBundle の作成
  • アセットの作成
  • 選択したアセットに対してなんらかのアクションを行う

などが上げられます。これらを実行するための入口(トリガー)となるのが MenuItem です。

8.3 MenuItem を使ってメニューを表示する

まずは簡単にメニューを追加してみます。MenuItem は Attribute として提供されており、static メソッドに付加することで機能します。

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("CustomMenu/Example")]
    static void Example ()
    {
    }
}
CustomMenu のメニュー項目と Example の子メニュー項目が追加された

図8.3: CustomMenu のメニュー項目と Example の子メニュー項目が追加された

もちろん既存のメニューに子メニューを追加することもできます。

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("Assets/Example")]
    static void Example ()
    {
    }
}
Assets メニューに Example メニューを追加

図8.4: Assets メニューに Example メニューを追加

子メニューからさらに子メニューを作成することも可能です。

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("CustomMenu/Example/Child1/Grandchild")]
    static void Example1 ()
    {
    }

    [MenuItem("CustomMenu/Example/Child2/Grandchild")]
    static void Example2 ()
    {
    }
}

階層の深さは特に制限はありませんが操作性を考えて3階層まで

図8.5: 階層の深さは特に制限はありませんが操作性を考えて3階層まで

8.4 実行できない MenuItem を作成する

MenuItem で追加したメニューが実行されてしまうと都合が悪い場合もあります。そこで MenuItem で追加したメニューを実行できないようにする機能があります。

Child2メニューの文字が灰色になっており、実行できないようになっている

図8.6: Child2メニューの文字が灰色になっており、実行できないようになっている

MenuItem の第2引数に isValidateFunction が存在し、これはメニューを表示した時に実行可能かどうかのチェックを行うためのものです。isValidateFunction に true を設定することでメソッドは Validate メソッドになります。さらに Validate メソッドは戻り値が bool 型のメソッドとなり、戻り値として true を返すと実行可能、false を返すと実行不可となります。

また、Validate メソッドは単独では動作しません。必ず isValidateFunction が false のメソッドを用意する必要があります。isValidateFunction のデフォルトが false なので引数を省略しても問題ありません。

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("CustomMenu/Example/Child1")]
    static void Example1 ()
    {
    }

    //isValidateFunction が false
    [MenuItem("CustomMenu/Example/Child2")]
    static void Example2 ()
    {
    }

    //isValidateFunction が true
    [MenuItem("CustomMenu/Example/Child2", true)]
    static bool ValidateExample2 ()
    {
        //今回は false 固定にして実行できないようにする
        return false;
    }
}

8.5 MenuItem の表示順を変更する

メニューの表示順を指定することが可能です。MenuItem の第3引数 priority で指定します。

Example メニューが一番下から一番上に移動している

図8.7: Example メニューが一番下から一番上に移動している

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("Assets/Example", false, 1)]
    static void Example ()
    {
    }
}

priority の仕様

priority の数値を小さく設定するほど上部に表示されるようになります。

priority が左から<b>1</b>, <b>20</b>, <b>40</b>, <b>1000</b> の時

図8.8: priority が左から1, 20, 40, 1000 の時

また priority の値を11飛ばしにするとメニュー項目の間にセパレート(区切り線)を設けることができます。

Example2と Example3の間に区切り線ができている

図8.9: Example2と Example3の間に区切り線ができている

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("CustomMenu/Example1", false, 1)]
    static void Example1 ()
    {
    }

    [MenuItem("CustomMenu/Example2", false, 2)]
    static void Example2 ()
    {
    }

    [MenuItem("CustomMenu/Example3", false, 13)]
    static void Example3 ()
    {
    }
}

8.6 MenuItem にチェックを入れる

Menu.GetCheckedMenu.SetChecked を使用することで子メニューにチェックを入れることができます。

チェックをつけることで<b>有効中</b>であることがわかる

図8.10: チェックをつけることで有効中であることがわかる

using UnityEditor;

public class NewBehaviourScript
{
    [MenuItem("CustomMenu/Example")]
    static void Example ()
    {
        var menuPath = "CustomMenu/Example";
        var @checked = Menu.GetChecked (menuPath);
        Menu.SetChecked (menuPath, !@checked);
    }
}

8.7 ホットキー(ショートカットキー)を実装する

MenuItem で追加したメニューはホットキーで実行することが可能です。第1引数のメニューパスの最後に「半角スペース + 修飾子キー + 任意の文字」の文字列をつけることにより実装できます。

using UnityEditor;
using UnityEngine;

public class NewBehaviourScript
{
    //command(ctrl) + shift + g で実行
    [MenuItem("CustomMenu/Example %#g")]
    static void Example ()
    {
        Debug.Log ("実行されました");
    }
}

表8.1: 修飾子キーを表す特殊文字

特殊文字修飾&特殊キー
%Ctrl(Windows)または command(MacOSX)
#Shift
&Alt(Windows)または option(Mac OS X)
_修飾子キー無し
F1 ... F12ファンクションキー
HOMEHome キー
ENDEnd キー
PGUPPageUp キー
PGDNPageDown キー
KP0 ... KP90から9までの数字キー
KP..
KP++
KP--
KP**
KP//
KP==

ファンクションキーのみのショートカットは作成できない

ファンクションキーのみ( "_F1" など)の作成はできません。

8.8 CONTEXT

各コンポーネントの歯車で表示されるコンテキストメニューにメニュー項目を追加できます。

歯車をクリックすると表示されるコンテキストメニュー

図8.11: 歯車をクリックすると表示されるコンテキストメニュー

表示するには決まりがあり、メニューパスの冒頭に「CONTEXT/」を追加します。そして「CONTEXT/コンポーネント名/メニュー名」とすることでコンテキストメニューに表示されるようになります。

using UnityEditor;

public class NewBehaviourScript
{
    //Transform にメニューを追加
    [MenuItem("CONTEXT/Transform/Example1")]
    static void Example1 () { }

    //コンポーネント(すべて)にメニューを追加
    [MenuItem("CONTEXT/Component/Example2")]
    static void Example2 () { }

    //ExampleScript(スクリプト)にメニューを追加
    [MenuItem("CONTEXT/ExampleScript/Example3")]
    static void Example3 () { }
}

コンテキストメニューに対してもホットキーは適用できますが、コンテキストメニューが表示されていないと実行されません。

MenuCommand

コンテキストメニューのみ MenuCommand を引数としてコンポーネント情報を取得できます。

using UnityEditor;
using UnityEngine;

public class NewBehaviourScript
{
    [MenuItem("CONTEXT/Transform/Example1")]
    static void Example1 (MenuCommand menuCommand)
    {
        //実行した Transform の情報が取得できる
        Debug.Log (menuCommand.context);
    }
}
上記コードを Main Camera の Transform で実行したもの

図8.12: 上記コードを Main Camera の Transform で実行したもの

第7章 EditorWindow 第9章 CustomEditor