コガネブログ

平日更新を目標にUnityやC#などのゲーム開発アレコレを書いていきます

【Unity】【Odin - Inspector and Serializer】Attribute を使用したデザイン(翻訳)

目次

メモ

はじめに

  • Odin ではクラスのメンバにいくつかの属性を適用するだけで
    可読性や操作性に優れたカスタムエディタを作成することができます

Attribute(属性)の使用

  • Unity には、既に Inspector の見た目をカスタマイズできる属性がいくつか用意されています
  • 例えば、Unity 独自の RangeAttribute を使用すると
    数値を範囲内に収めることができ、値を調整するためのスライダーも提供されます
using UnityEngine;

public class Example : MonoBehaviour
{
    [Range( -10, 10 )] public float value;
}

f:id:baba_s:20170724140346p:plain

  • Odin では Unity のすべての属性をサポートしており、さらに、独自の属性を追加します
  • 例えば、Odin の ValueDropdown 属性を使用することで、
    任意の値のみ選択できるドロップダウンメニューを提供することができます
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    private string[] list = 
    {
        "フシギダネ"    , 
        "ヒトカゲ"       , 
        "ゼニガメ"       , 
    };

    [ValueDropdown( "list" )] public string value;
}

f:id:baba_s:20170724140356p:plain

  • Unity が用意している属性のほとんどは、値の設定方法を変更するだけですが、
    Odin の属性の多くは、値の設定方法を変更する以外にもさまざまなカスタマイズが可能です
    どのような種類の属性が存在するか、何ができるかを次の項から紹介していきます
  • Odin のすべての属性を知りたい場合は下記のドキュメントを参照してください
    http://sirenix.net/odininspector/documentation/sirenix/odininspector/assetlistattribute

表示順

  • PropertyOrderAttribute を使用することで、パラメータの表示順を簡単に変更できます
  • 引数に渡した数値が低いものから順に表示されます
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [PropertyOrder( 2 )] public int second;
    [PropertyOrder( 1 )] public int first ;
}

f:id:baba_s:20170724140408p:plain

  • Odin では変数だけではなくプロパティとメソッドもサポートしています
    通常、これらのメンバは別々に表示されますが、PropertyOrderAttribute を使用すると
    簡単にあらゆるメンバの表示順を変更できます
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [PropertyOrder( 2 )] public int second;

    [Button]
    [PropertyOrder( 1 )]
    public void First() { }
}

f:id:baba_s:20170724140418p:plain

グループ化

  • 非常に多くの変数を保持するスクリプトを作成したことはありますか?
  • Inspector 上のすべての変数を把握することに大変な思いをしたことはありますか?
  • この問題を解決するために、Odin にはグループ化の属性が存在しています

  • グループ化は、関連するプロパティをグループ化し、
    タイトル文を表示したり、折りたたみできるようにしたり、
    タブを付けたりすることができます

  • これらを使用することは簡単です。
    グループ化するメンバに同じ名前のグループ属性を設定するだけです
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [TabGroup( "グループ A" )] public int    a1;
    [TabGroup( "グループ A" )] public int    a2 { get; set; }
    [TabGroup( "グループ B" )] public float  b1;
    [TabGroup( "グループ B" )] public string b2;
}

f:id:baba_s:20170724140431p:plain

  • グループ化は表示順の制御もサポートしています
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [BoxGroup( "グループ A", order:2 )] public int   first ;
    [BoxGroup( "グループ B", order:1 )] public float second;
}

f:id:baba_s:20170724140448p:plain

  • グループ内の個々のプロパティに PropertyOrderAttribute を適用すると
    グループ内の表示順を調整することができます
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [BoxGroup( "グループ A" ), PropertyOrder( 3 )] public int third ;
    [BoxGroup( "グループ A" ), PropertyOrder( 1 )] public int first ;
    [BoxGroup( "グループ A" ), PropertyOrder( 2 )] public int second;
}

f:id:baba_s:20170724140458p:plain

メタ属性

  • 属性の中には、表示は拡張せず、
    Inspector で値が変更された時に呼び出されるモノがあります
  • ValidateInputAttribute を使用すると、値の整合性チェックを行うことができます
  • OnValueChangedAttribute を使用すると、
    値が変更された時にコールバック関数が呼び出されます
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [ValidateInput( "IsValid" )] public int value;

    private bool IsValid( int value )
    {
        return 0 < value;
    }

    [OnValueChanged( "OnUpdate" )] public GameObject go;

    private void OnUpdate()
    {
        Debug.Log( go.name );
    }
}

f:id:baba_s:20170724140742p:plain

  • このようなメタ属性を使用することで、ゲーム開発のルール化を促進することができます
  • また、RequiredAttribute や InfoBoxAttribute を使用して、
    不正な値に対して警告やエラーメッセージを表示することで、
    Inspector を使用するデザイナーを支援し、ミスを防ぐことができます
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [Required] public GameObject go;

    [InfoBox( "0 より大きな値を設定してください", "IsInvalid" )]
    public int value;

    private bool IsInvalid()
    {
        return value <= 0;
    }
}

f:id:baba_s:20170724141008p:plain

整合性チェック

  • 前述した項目で見られるように、
    Odin には値の整合性チェックを行うために属性を提供しています
using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [Required] public GameObject original;
    [AssetsOnly] public GameObject prefab;
    [SceneObjectsOnly] public GameObject sceneObject;
}

f:id:baba_s:20170724141534p:plain

using Sirenix.OdinInspector;
using UnityEngine;

public class Example : MonoBehaviour
{
    [Required, AssetsOnly, ValidateInput( "IsValid", "タグが Player のオブジェクトを設定してください" )]
    public GameObject prefab;

    private bool IsValid( GameObject go )
    {
        return go.tag == "Player";
    }
}

f:id:baba_s:20170724141429p:plain

  • 問題のあるオブジェクトを選択している間は、
    Inspector で警告やエラーを確認することができます
  • しかし、多くの場合、値が正常かどうかを確認するために、
    Inspector のオブジェクトを1つ1つ選択し、
    警告やエラーを確認するのは非常にメンドウです
  • Odin では、Scene Validator を使用して、
    特定のシーンですべてのオブジェクトが
    ルールに従っているかどうかを簡単に検証することができます
  • Unity メニューの「Window>Odin Inspector>Scene Validator」から実行できます

f:id:baba_s:20170724141734p:plain

  • Scene Validator はシーン内のすべてのオブジェクトを検査し、
    警告やエラーによってソートされた GameObject のリストを表示します
  • 関連するオブジェクトをウィンドウ内で直接確認して、
    その場で違反を修正することが可能です