次のようにタグ名をインスペクターから選択させるようにさせたいの…
エディタ拡張を実装すれば可能だわ。EditorGUI.TagFieldメソッドを使うのがポイントよ。
タグ名をインスペクターから選択式で指定できるようにする方法の解説記事です。
結論を述べると、タグを表す独自の属性や構造体を実装し、Property Drawerとしてエディタ拡張を実装すれば可能です。
エディタ拡張からは、EditorGUI.TagFieldメソッドで専用UIを表示できます。
本記事では、次の2通りの方法でインスペクターからタグ名を選択式で指定できるようにする方法を解説します。
- 専用の属性とProperty Drawerを自作する
- 専用の構造体とProperty Drawerを自作する
- Unity 2023.1.16f1
目次 非表示
専用の属性とProperty Drawerを自作する
1つ目は、独自属性を実装して文字列フィールドをタグ専用のUIに置き換える方法です。
次のような流れで実装できます。
- PropertyAttributeクラスを継承した属性クラスを実装する
- 上記クラスに対してPropertyDrawerクラスを継承したエディタ拡張を実装する
実装例
string型フィールドをタグ選択用UIに置き換える専用属性の実装例です。
上記をTagAttribute.csなどというファイル名でUnityプロジェクトに保存すると使えるようになります。
使用例
実際に使用する側は、次のようにシリアライズされるstring型フィールドに対して[Tag]属性を付加すれば良いです。
実行結果
string型で定義されたフィールドがタグ選択用UIに置き換わっています。
単なるドロップダウンではなく、新しいタグを追加するAdd Tag…項目も表示されています。
スクリプトの説明
例では、1つのソースファイルに属性とそのエディタ拡張を実装しています。
そのため、エディタ拡張で使われる名前空間UnityEditorは#if UNITY_EDITOR ~ #endifディレクティブで囲んでusingする必要があります。
UnityEditor名前空間はUnityエディタでのみ有効なため、UNITY_EDITORが未定義の時(ビルド時)ではコンパイルエラーとなってしまいます。
正しくビルドできない原因になるため注意が必要です。
[Tag]属性を使えるようにするために、次のようにTagAttributeクラスを定義しています。
今回はフィールドに対して機能させる属性のため、[AttributeUsage(AttributeTargets.Field)]属性をTagAttributeクラスに付加しています。
また、Unityでフィールド用の属性として使えるようにするために、PropertyAttributeクラスを継承しています。
参考:PropertyAttribute – Unity スクリプトリファレンス
そして、[Tag]属性として指定されたフィールドに対して専用UIを表示させるために、カスタムのProperty Drawerを実装します。
これは、PropertyDrawerクラスを継承すれば良いです。また、[Tag]属性に作用させるために[CustomPropertyDrawer(typeof(TagAttribute))]属性をクラスに付加しています。
参考:PropertyDrawer – Unity スクリプトリファレンス
参考:CustomPropertyDrawer – Unity スクリプトリファレンス
実際の表示部分の実装は、PropertyDrawer.OnGUIメソッドをオーバーライドすることで行います。
[Tag]属性が指定されるフィールドはstring型であってほしいため、それ以外の型ではデフォルトUIを表示させるために、次の部分で型チェックを行っています。
そして、string型であることが分かったら、EditorGUI.TagFieldメソッドでタグの選択式UIを表示させます。
戻り値として選択されたタグ名が返るため、これをプロパティに反映すれば良いです。
参考:EditorGUI-TagField – Unity スクリプトリファレンス
専用の構造体とProperty Drawerを自作する
タグ名をstring型ではなく、専用の型として扱うことで、インスペクター表示を変更させることも可能です。
この方法では、次のメリットが得られます。
- 専用の型を設けることによって、コードの可読性向上が期待できる
- 目的の異なるstring型変数への間違った代入を防げる
- 独自のプロパティやメソッドを実装できる
実装の流れは以下のようになります。
- シリアライズ可能な専用構造体を実装する
- 上記構造体に対してPropertyDrawerクラスを継承したエディタ拡張を実装する
最初の属性を実装する方法との主な相違点は、属性ではなく構造体を実装するといった点です。
実装例
Tagという名前の構造体を実装した例です。エディタ拡張でタグ専用の選択式UIに置き換えるコードも含まれています。
上記をTag.csなどのファイル名でUnityプロジェクトに保存すると使えるようになります。
使用例
上記Tag構造体の使用例は以下の通りです。
上記をUseStructExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチすると、インスペクターから設定できるようになります。
実行結果
ゲームを実行すると、次のようにタグに関する情報がログ出力されます。
上記は何もタグを指定しない例です。タグ名(_tag.Name)が空だったり、タグが存在しない判定(_tag.IsTagged: False)だったりします。
試しにTag項目に「Player」を指定すると、以下のようにログ出力が変化します。
途中でスクリプトから生成された「Player」というタグ名が格納されたTag構造体インスタンスと比較していますが、同じタグ名のため最後の出力「_tag == tagFromScript」がTrueになっています。
別の「Finish」などのタグ名にすると、「_tag == tagFromScript」はFalseとなります。
スクリプトの説明
Tag構造体は、シリアライズ可能な型である必要があるため、[Serializable]属性を指定して定義します。
参考:Serializable – Unity スクリプトリファレンス
例では、タグ名(string型の文字列)とタグ付けされているかどうかを返すプロパティを実装しています。
これ以外にも、Tag構造体の等価演算や文字列への変換などもサポートしています。
エディタ拡張により表示を置き換えるコードは以下部分です。
[Tag]属性を実装する例とは異なり、TagAttribute型ではなくTag型に対して[CustomPropertyDrawer(typeof(Tag))]として属性を指定するところや、フィールドの型がstring型かどうかのチェックが無くなっていたり、_tagNameフィールドにアクセスしていたりといった違いがあります。
使用側では、次のようにTag型のフィールドとして定義するだけでタグ選択用の専用UIに置き換わります。
実際にタグ情報にアクセスする処理は以下部分です。
さいごに
タグ名をインスペクターから専用UIで指定可能にするには、属性を自作すれば良いですが、独自構造体を実装することでも実現可能です。
特に後者は専用の型を使うことによる可読性やメンテナンス性の向上が期待できるでしょう。