Unity3D
Unity
Asset
Odin-InspectorAndSerializer

インスペクタを便利にする「Odin - Inspector and Serializer 」紹介編

0.はじめに

 皆さん初めまして、きつねうどんです。今回はアセットアドカレ参加ということで、初めての記事を書くことになりました。
 記念すべき第一回目の記事は"Odin-Inspector and Serializer"の紹介をさせていただきます!
 今回は紹介ということで深くまでは入らず、二部構成という形にすることになりまして、ぱふもどきさんが後半の記事を担当してくださるので、8月31日のぱふもどきさんの記事をぜひ読んでくださいね!

1.まず「Odin - Inspector and Serializer」ってなんなの?

 エディタ拡張ってすごいめんどうですよね^^;
 Generic型の変数をインスペクタに表示したいだとか、便利な属性を追加したいだとかおもったことはありませんか? 僕は常々そう思ってるのですが、技術が足りなかったり時間が足りなかったりで結局やらずじまいで、エディタ拡張にはほとんど手を付けていませんでした。
 というか、エディタ拡張って本当に底なし沼なんですよね。こんな拡張が欲しいってやり始めたらゲーム制作に手が回らなくなってしまうことがしばしばあるんです。

 そこで面倒な拡張を用意してくれるのが"Odin-Inspector and Serializer"なんです!

 ↓アセットストアリンク
 Odin - Inspector and Serializer (AssetsStore) $45 (約5,000円 / 2018年8月)

0a15170a-a8f1-4343-a0d1-99718c9bee52.jpg

 このアセットを使うと……こんなのや、

1f09109df376be05def65fb2b55ae7f0.png

 こんなのが簡単に作れちゃいます!

c5f7c1e0acfb206210e1cfc1edf8559e.png

2.SerializedMonoBehaviourクラスを継承したら、MonoBehaviourの機能は使えないの?

 OdinでGenericクラスの変数をシリアライズしたいときは基本的にSerializedMonoBehaviourクラスを継承します。MonoBehaviourではDictionary型の変数がインスペクタに表示されないのです。

public class Hoge : SerializedMonoBehaviour と、こんな感じですね。

 Unityでは標準ではMonoBehaviourクラスから派生するメソッド、コルーチン、コンポネントを扱います。TransformだったりRigidBodyだったり、便利な機能が入っていて、ほとんどの人は「これを使わないなんてとんでもない!」なんて感想を抱くでしょう。
 しかし、Odinの機能を万全に扱うとすれば、SerializedMonoBehaviourクラスを継承しなければなりません
 僕も最初は「MonoBehaviourは使えないの? めっちゃ困るなあ」なんて思ってました。
 まあ、それは杞憂に終わったのですが

 SerializedMonoBehaviourクラスでもMonoBehaviourクラスの機能を十全に使うことができます

 SerializedMonoBehaviourは、MonoBehaviour拡張みたいなものだと思えばいいわけですね!

3.便利で多機能な数々の属性

 このアセットは本当に多くの属性を追加してくれます。
 僕が日ごろから使っているような物でだけでも二十種類もあるんです! 全部で軽く六十種類以上もあるとか……すべて使い切こなせる自信がありませんね^^;

 実際活用できる便利な属性を一部のものだけですが紹介します。

 と、その前にですが、Odinを使うときには必ず以下の名前空間を呼び出してください。
 using Sirenix.OdinInspector;
 これがないとOdinの機能を使うことができないので、必ず忘れないようにしてください。
 using Sirenix.OdinInspector;ですよ! 絶対に忘れないでくださいね!

 では気を取り直して、属性の紹介に移らせていただきます。

属性 効果     
[ShowInInspector] private 変数やプロパティをInspectorに表示してくれます
[Required] 参照が取れていないときにエラーメッセージを表示してくれます
[MinMaxSlider(min, max)] スライダーの最大値と最小値を設定できます。 ※Vector2でのみ使えます
[MinValue(min)]、[MaxValue(max)] 設定できる値を制限できます
[Wrap(x, y)] 数値をラップアラウンドしてくれます(x値に達すると、y値に戻すこと)
[Button("Label")] インスペクタにメソッドを呼び出してくれるボタンを表示してくれます
[BoxGroup("Hoge")] パラメータをグループ分けしてインスペクタに表示してくれます
[LabelText("Hoge")] インスペクタに表示する変数名を変更してくれます。(コード内での変数名は変わりません)
[EnableIf("BoolName")]、([DisableIf("BoolName")]) 指定された変数名のBool値を取得し、True (False)かどうかで項目を編集できるようにしてくれます
[ShowIf("BoolName")] 指定され変数名のBool値を取得し、Trueかどうかでインスペクタに表示非表示を切り替えてくれます
[PreviewField] メッシュやマテリアル、3Dモデルの形状などの情報をインスペクタで確認できるようになります

 これだけでもかなり便利なんです! Button属性とかはよくデバッグとかで使わせてもらってます。めっちゃ便利です。

 これだけでも十分便利なのですが、僕が本当にお勧めしたい理由は実はこれじゃないんです。それについては後に説明します!

21f9a7705fdcb9ff84f467a2251f7920.png       

たったこれだけのコードでも……

Unity.gif

 こんな風に自分でエディタ拡張しなくても、ボタンや表示非表示の切り替えなどを簡単に追加してくれます。

 

4.エディタウィンドウにも数々の拡張

 このアセットはインスペクタだけではなく、ver1.0.6.0からエディタウィンドウの拡張もできるようになりました。僕はエディタ拡張に苦手意識があるためあまり触ってませんが、サンプルを見るにかなり便利なものだというのがうかがえます。
 ↓サンプル画像。

e2c58a74ce20294993051ec2fc58ae04.png

2bf8e223536e1723edf82f6ca3093f6f.png

 こういうのが標準のEditorWindowよりもはるかに簡単に作れるようになるみたいです。
 こういうの見てると作ってみたくなりますね。こういう機能があれば開発期間もかなり短縮できそうです。

5.Genericクラスをインスペクタで参照できる!!!

 この機能にはかなり助けてもらっています!
 Dictionaryとか配列とかがインスペクタで編集できなくて、とても不便に感じていた時期が僕にもありました!
 でもこのアセットを入れるだけで面倒な拡張をせずに表示できるようになりました!
 属性も何も使わず、宣言するだけで表示してくれます! すごい!

Unity.gif

 Dictionaryや配列の表示以外にも、Listの表示も変わってたりします。インスペクタ上で順番が変更できたり、要素の削除、追加もできて便利だし、表示もかっこいい!
(※エディタのデザインが黒いのはZiosという違うアセットによるものです。)→Zios - GitHub

 ただ、悩みどころなのはDictionaryの要素数が多くなった時、特定の要素を見つけにくくなることですね。要素が15個以上になるとページに分かれて表示されるのですが、Dictionaryの表示だけがKeyの値でソートされているみたいで、何ページにあるのか分からなくなる時がちょくちょくあります。
(下の画像は要素が100個、7ページあって、一つの要素を見つけるのにかなり苦労しそうです笑)
fe42453225b94d8e5a145fc2fba3ee87.png

 不便なのをほっとくわけにもいかず、せっかくなのでDictionaryの検索をかけるサンプルを作ってみました。エディタ拡張じゃないのは許してください。本当に苦手なんです……
 記事の最後に貼っておきます。プログラミング歴は1年未満と結構短いので汚いコードでしたらすみません。

6.終わりに

 どうでしたでしょうか。僕なりにOdinの魅力を伝えたつもりなので、この記事を読んで「Odin買ってみたよ!」って人がいればうれしい限りです。まだまだ、魅力は伝え足りていないですけど。

 この記事は僕がアセットアドカレ2018の開催を聞きつけて「誰かのためになったらいいな」程度で執筆したものですが(決して50$分のバウチャー目当てではありませんとも、ええ)、今後も気が向いたら初心者なりに健忘録なんて書いてみようかな~なんて思っています。僕と同じような初心者の方が助かるような記事を書きたいな~なんて。まあ需要なんてなさそうですが
 それではここで筆を置かせてもらいます。ご閲覧ありがとうございました。
 ※8/31のぱふもどきさんの記事は実践に役立つ記事なので、ぜひ読んでくださいね!

#Dictionaryの検索機能のサンプル

 ここにサンプル置いておきますね。見にくかったり、冗長だったり、もっといい方法があったりしたらごめんなさい。

Example.cs
using System.Collections.Generic;
using UnityEngine;
//OdinInspectorに必要
using Sirenix.OdinInspector;
using System.Linq;
using System;

public class Example : SerializedMonoBehaviour {

    //表示順を変える
    [PropertyOrder(1)]
    public Dictionary<string, int> exampleDic;

    [BoxGroup("検索"), PropertyOrder(0)]
    public string serchKey;

    [Button("検索", ButtonSizes.Large), BoxGroup("検索")]
    public void Search()
    {
        if (string.IsNullOrEmpty(serchKey))
        {
            Debug.Log("検索する値が設定されていません");
            return;
        }

        if (!exampleDic.ContainsKey(serchKey))
        {
            Debug.Log(serchKey + "は見つかりませんでした");
            serchKey = null;
            return;
        }
        //インスペクタでの表示がKey順にソートされてるので、ここでKeyをソートしたリストを作る。
        List<string> Keys = exampleDic.Keys.ToList();
        Keys.Sort();

        //番号とページの計算
        int elementNum = Keys.IndexOf(serchKey) + 1;
        int page = (int)Math.Ceiling(elementNum / 15f);
        //ログの出力
        Debug.Log(serchKey + "の要素は" + elementNum + "項目目でみつかりました。" + page + "ページ目に存在します\n値は" + exampleDic[Keys[elementNum - 1]] + "です");
        serchKey = null;
    }    
}

Unity.gif