ゼニガネブログ

ゲーム開発のための小ネタなど。現在はUnityメインでやってます

【Unity】エディタ拡張で MenuItem Attribute の priority を使って 階層化・入れ子 状態でも良い感じに並べる

エディタ拡張の MenuItem の priority 自体はおそらく非常に有名で自分も↓のような形で昔から使っていました。

[MenuItem("OkaneGames/Viewer/HogeViewer", priority = 1000)]

しかし、↓のような Viewer 以下での HogeViewer の位置Setting以下でのFirstSetup の位置は並べ替えが出来ても OkaneGames 以下での Viewer と Setting の並び順のコントロール方法がわかりませんでした。

[MenuItem("OkaneGames/Viewer/HogeViewer", priority = 1000)]
[MenuItem("OkaneGames/Setting/FirstSetup", priority = 0)]

また MenuItem を↓のように Window 以下で階層型にした時に、 OkaneGames の部分がUnityエディタのメニューと混ざって変な位置に並んでしまいます。
※エディタのバージョンにもよりますが、最近の物であれば SearchAssetStore の間に来ると思います

[MenuItem("Window/OkaneGames/HogeViewer")]
[MenuItem("Window/OkaneGames/EditorSetting")]

これらがあまり気持ちよくないので、並び替える方法を調べたところ無事解決できたので載せておきます。

 

何も難しい事はなく、知っているかどうかだけのレベルなので↓のソースさえ見ればだいたい理解できると思います。

 

あと知っておくと役に立ちそうなのはここらへんです。
priority が低い方が上に来て
・同値はエラーにならず自動解決
priority の値同士の間が11以上空くとセパレーターが入る
・同じ名前で別の値が入っている場合もエラーにはならないがどちらの値が採用されるかは不定
・キャッシュが強烈で priority 変更後の反映タイミングはだいたいエディタ再起動後

 

もし自作メニューの並び順を完全にコントロールしたい場合は、各エディタ拡張ごとで MenuItem を書くのはやめて public 関数だけ用意し、その public 関数のラッパーがひたすら羅列されたコントロール専用の.csファイルを作るほうが一箇所で priority のチェックが出来る分楽かもしれません。
ただし、リファクタリング時の対象が増える、元々 private で良かった物が public になってしまう等宜しくない点はあるので、必要に応じて選択しましょう。

 

ちなみに MenuItem の名前が重複した場合はエラーにならず画像のような警告が出ます。

 

【追記 2023/03/21】
AnythingBookmark をなるべく古いバージョンでも動かせるように検証していた時に気が付いた事があったので追記します。

    [MenuItem("Window/OkaneGames/", priority = Int32.MaxValue)]
    [MenuItem("Window/OkaneGames/AnythingBookmark")]
    [MenuItem("OkaneGames/AnythingBookmark")]
    private static void CreateWindow(){}

↑の記法(Window以下でpriority)が有効なのはUnity2021.2以降です。
Unity2017.1 ~ Unity2021.1 では画像のように表示されます。

priority が効かない点と見た目以外の問題はなく、普通にクリックが出来てウィンドウも開きます。
ただ積極的に放置する理由もなく大した労力ではないので下記のようにしておくのが良いでしょう。

#if UNITY_2021_2_OR_NEWER
    [MenuItem("Window/OkaneGames/", priority = Int32.MaxValue)]
#endif

 

MenuItem 自体の機能についてはこちらの記事が非常によくまとまっていて最高です。

kan-kikuchi.hatenablog.com

 

// ***********************************************************

以降は調べた流れなどのどうてもよい情報なので興味のある方だけどうぞ。

真っ先に UnityEditor.MenuItem のソースコードを調べたところ下記のようなコメントが見つかっただけで特に参考になる情報は見つかりませんでした。

priority: The order by which the menu items are displayed.

次に、エディタで出来てるんだからUnity公式リポジトリで調べたら一発でしょ!
と思い落としたソースでGrep検索をしたところ↓のちょっと珍しい物が見つかったぐらいで階層型になっている部分は引っかかりませんでした。

Editor\Mono\EditorApplication.cs(480,40)
[MenuItem("File/New Scene %n", priority = 150)]
Editor\Mono\Commands\GOCreationCommands.cs(112,57)
[MenuItem("GameObject/Create Empty Parent %#g", priority = 0)]

もう仕方ないので、クラスの実装的に↓のように書いたら多分いけるんじゃ?と思って書いてみたらいけました。

[MenuItem("Hoge/Fuga/", priority = 1000)]
[MenuItem("Hoge/Piyo/", priority = 1)]

何の参考にもならない解決方法で申し訳ないです。

 

更に余談になりますが、今現在はどうなのかわかりませんが、アセットストアでリリースしようとする時は、Window 以下だけに設定しないとリジェクトされたような記憶があります。


※アセットストアで出さずにSDKを自社サイトで管理してるタイプはだいたいルートにあって Window 以下で探す必要がなく数が非常に使いやすくて好きです
※ Window 以下を強制してルートに許可を出さないのは大量にアセットを追加した際に、Unityの重要なメニューが隠れてしまうのを防ぐ為なのかもしれません


アセットストアでリリースされている有名所のアセットでも priority 未設定が割りと多いイメージで Search と AssetStore の間に大量に並び埋もれています。
priority を弄ったアセットを審査に出した事がないのでどうなるかはわかりませんが、見やすくなる priority に変更を検討してもよいかもしれません。

 

アセットストアで出す気がなくて全てを自由に使える俺様が Window の Int32.MaxValue は貰っていくぜ!(ゲス顔


嘘です。皆 Int32.MaxValue に設定しても、エラーも起きず特に問題ないですし、なんならUnityの物と混ざらずに一番下にまとまって表示される分効率が上がってむしろ世界的に平和になるかもしれません。