MRが楽しい

MRやVRについて学習したことを書き残す

インタフェースを指定してSerializeFieldにコンポーネントの参照を設定する その3(CustomPropertyDrawerで型指定のカスタムプロパティを作成する)

本日は Unity の小ネタ枠です。
ホロモンアプリ実装時にインタフェースを指定してSerializeFieldにコンポーネントの参照を設定したいことがあったので実装を試してみました。
今回は CustomPropertyDrawer を利用して実装を試してみました。

前回記事

以下の記事の続きです。
bluebirdofoz.hatenablog.com

CustomPropertyDrawerで型指定のカスタムプロパティを作成する

PropertyAttribute を派生してカスタムアトリビュートを作成し、更にそのカスタムアトリビュートのインスペクター上での動作を CustomPropertyDrawer で制御します。
これにより指定の型を実装しているコンポーネントのみ登録可能なカスタムプロパティを作成可能です。

以下の記事を参考にしました。
qiita.com

カスタムアトリビュートの実装は特に変更が必要な箇所がないため、解説コメントを追記した以外はそのまま引用しています。

指定の型を位置指定パラメータで受け取るカスタムアトリビュート

・ComponentRestrictionAttribute.cs

using System;
using UnityEngine;

// AttributeUsage:適用可能なプログラム要素を指定する
// Inherited:派生したクラスによって継承可能か
// AllowMultiple:属性の複数のインスタンスが存在できるか
// https://learn.microsoft.com/ja-jp/dotnet/standard/attributes/writing-custom-attributes
[System.AttributeUsage(System.AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
// PropertyAttributeでカスタムプロパティを定義する
// https://docs.unity3d.com/ja/current/ScriptReference/PropertyAttribute.html
public class ComponentRestrictionAttribute : PropertyAttribute
{
    /// <summary>
    /// 位置指定パラメータ
    /// https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/concepts/attributes/creating-custom-attributes
    /// </summary>
    public Type type;
    public ComponentRestrictionAttribute(Type type)
    {
        this.type = type;
    }
}
アトリビュートのインスペクター上での動作を制御するカスタムプロパティ

・ComponentRestrictionDrawer.cs

using UnityEngine;
using UnityEditor;

// CustomPropertyDrawerでインスぺクター上の描画をカスタマイズする
// https://docs.unity3d.com/ja/current/ScriptReference/CustomPropertyDrawer.html
[CustomPropertyDrawer(typeof(ComponentRestrictionAttribute))]
public class ComponentRestrictionDrawer : PropertyDrawer
{
    // OnGUIで描画時の処理をカスタマイズする
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var restriction = (ComponentRestrictionAttribute)attribute;

        // UnityEngine.Objectから派生した型か否か(SerializedPropertyType.ObjectReference)
        // https://docs.unity3d.com/ja/current/ScriptReference/SerializedPropertyType.ObjectReference.html
        if (property.propertyType == SerializedPropertyType.ObjectReference)
        {
            // ComponentRestrictionAttributeで指定した型のみ割り当てを許可する
            // https://docs.unity3d.com/ja/current/ScriptReference/EditorGUI.ObjectField.html
            EditorGUI.ObjectField(position, property, restriction.type);
        }
        else
        {
            EditorGUI.PropertyField(position, property);
        }
    }
}

スクリプトは Unity のエディター拡張なので Editor フォルダ配下に作成する必要があります。

利用方法

型を指定したいプロパティに ComponentRestriction アトリビュートを設定します。
・WorldItemAccesserExample04.cs

using UnityEngine;
namespace HoloMonApp.Content.Character.WorldItem.Common
{
    public class WorldItemAccesserExample04 : MonoBehaviour
    {
        // ComponentRestrictionでWorldItemSharingModuleIF型を指定する
        [SerializeField, ComponentRestriction(typeof(WorldItemSharingModuleIF))]
        private Component p_WorldItemSharingModuleIFComponent;
        private WorldItemSharingModuleIF p_WorldItemSharingModuleIF => p_WorldItemSharingModuleIFComponent as WorldItemSharingModuleIF;
    }
}

このプロパティには指定の型を実装したコンポーネントのみが登録できるようになります。

一度カスタムプロパティを作成してしまえばその後の型指定がとても楽になります。