こういう実装で,
public class ConditionalDisableAttrTest : MonoBehaviour { public bool flag = false; [FlagConditionalDisableInInspector("flag")] public string editableStrIfTrue = "a"; }
こういう感じに編集可/不可を制御でき,
こうすれば,
public class ConditionalDisableAttrTest : MonoBehaviour { [System.Serializable] public class TestClass { public Vector3 p; public Quaternion q; } [Header("Flag control")] public bool flag = false; [FlagConditionalDisableInInspector("flag", conditionalInvisible: true)] public TestClass invisibleClsIfFalse; }
表示/非表示も制御できる(自作クラス/構造体でもOK)
というのを実現するネタです.
こういうことをしたくて「Unity インスペクタ 変数 動的」とかでググると,CustomEditorを使う方法が出てきます.
- 【Unity】インスペクタの表示を動的に変更する - Qiita
- インスペクタで設定できる値を動的に変更する【Unity】【エディタ拡張】 - (:3[kanのメモ帳]
- コガネブログ(旧)[Unity]Inspectorに無効にできるグループを表示する
が,これだとCustomEditorで対応したクラスでしか使えない = 他のクラスの変数に使い回しができなくて,ちょろっと使いたいときに不便な感じです.
(プロダクトレベルとかで,しっかり作り込む場合はこちらの方がいいと思いますが)
ということで,今回はCustomEditorは使わずに,任意のクラスの変数に適用できるAttributeを使って実装しています.
正直Unityのエディタ拡張は全然詳しくないので,バグってたらすみません.
一通りの型で使えることは確認しましたが,漏れもあるだろうし,基本正常系しか確認できてません.他のAttributeとの組合せ時とかも未検証です(免責事項)
この記事ではbool型変数での制御だけなのですが,boolに加えて,intやenum,string,float変数などでも制御したい方は,こちらの記事に解説がありますので,ご覧ください
使い方
まず,AttributeとEditor拡張のクラスを用意します.その後,編集不可にしたり非表示にしたい変数に対してAttributeをつけます.
というだけです.順番に見ていきます.
AttributeとEditor拡張のクラスの作成
以下に貼ったコードのファイルを作成します.
1つ目(FlagConditionalDisableDrawer.cs
)をEditorフォルダに作ってください.2つ目(FlagConditionalDisableInInspectorAttribute.cs
)は普通にScriptフォルダなどでOKです.
gist8646f03f00a855ab3b8f21ecb1179f3c
アトリビュートの利用
あとは,任意のMonoBehaviourなクラスに,
- 表示制御用のbool変数(以下でいう
flag
メンバ変数)を用意 flag
変数によって編集不可にしたり非表示させたりしたい変数(以下でいうeditableStrIfTrue
)を用意FlagConditionalDisableInInspector
アトリビュートをつけ,第一引数に制御用フラグの変数名(この例だと"flag")をstringで設定
してください
public class ConditionalDisableAttrTest : MonoBehaviour { public bool flag = false; [FlagConditionalDisableInInspector("flag")] public string editableStrIfTrue = "a"; }
このConditionalDisableAttrTest
をUnityのHierarchyに配置すると,flag
がtrueだとeditableStrIfTrue
が編集可に,falseだと編集不可になると思います.
挙動を逆にしたい場合,つまり,flag
がfalseのときに編集可にしたい場合は,AttributeのtrueThenDisable
引数をtrueに設定してください.
[FlagConditionalDisableInInspector("flag", trueThenDisable: true)] public Rect editableRectIfFalse;
また,編集可/不可ではなく,表示/非表示を切り替えたい場合は,conditionalInvisible
をtrueに設定してください.trueThenDisable
も併用可能です.
[FlagConditionalDisableInInspector("flag", conditionalInvisible: true)] public float visibleFloatIfTrue = 0f; [FlagConditionalDisableInInspector("flag", trueThenDisable: true, conditionalInvisible: true)] public Vector3 visibleVec3IfFalse;
この4つを並べると,以下のようになると思います.
こうなると期待通りの挙動です🎉
簡単な解説
Attributeを使ってInspector上の表示のさせ方を変えるEditor拡張については,PropertyDrawerの使い方を解説しているUnity公式, docs.unity3d.com
もしくは「PropertyDrawer Editor拡張」とかでググって出てきたブログ記事などをご参考ください.
今回のネタでややこしいのは,制御用変数のステートの取得です.
つまり,これ↓のeditableStrIfTrue
のEditor表示制御している処理で,どうやってflag
変数の値を取得するか,というところです
public bool flag = false; [FlagConditionalDisableInInspector("flag")] public string editableStrIfTrue = "a";
で,これをやってるのが,FlagConditionalDisableDrawer
の以下の箇所です.
var attr = base.attribute as FlagConditionalDisableInInspectorAttribute; var prop = property.serializedObject.FindProperty(attr.FlagVariableName);
property
,つまりeditableStrIfTrue
のSerializedPropertyから,これが所属しているSerializedObjectをたどり,そのSerializedObjectが持つPropertyを変数名を使って取得しています.
変数名は,Attributeの第一引数で設定した値がattr.FlagVariableName
に格納されているので,それを使います.
とまぁこれだけっちゃこれだけです.UnityのReferenceとにらめっこしつつ見つけた感じです.
あとは,非表示時にInspectorの変数表示位置を詰めたりするために,GetPropertyHeight
に手を入れてるぐらいですかね.
非表示の場合(ConditionalInvisible
がtrueかつ条件に適合する場合)は,高さをSpacing分だけ詰めてあげると,見た目に変なスペースができなくて良さげです.表示する場合はEditorGUI.GetPropertyHeight
でpropertyに適した高さを返します.
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { var attr = base.attribute as FlagConditionalDisableInInspectorAttribute; var prop = property.serializedObject.FindProperty(attr.FlagVariableName); if(attr.ConditionalInvisible && IsDisable(attr, prop)) { return -EditorGUIUtility.standardVerticalSpacing; } return EditorGUI.GetPropertyHeight(property, true); }
まぁコード行数もめちゃ短いので,特段解説することもないですね.