MRが楽しい

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

UnityでInspectorで設定したインスタンス群に共通インタフェースでアクセスする

本日は Unity の小ネタ枠です。
UnityでInspectorで設定したインスタンス群に共通インタフェースでアクセスする方法を試しました。

課題

現状、Unity では Interface 型で登録できるオブジェクトフィールドを Inpector に表示することはできません。
任意の Interface を継承した MonoBehaviour であってもオブジェクトフィールドに表示する場合は実体の型で指定する必要があります。

例えば、以下のインタフェースと2つの継承した型を作成しました。
・HoloMonBehaveIF.cs

namespace HoloMonApp.Character.Behave
{
    public interface HoloMonBehaveIF
    {
        /// <summary>
        /// 初期化
        /// </summary>
        void AwakeInit(HoloMonBehaveReference reference);
    }
}

・HoloMonPurposeBehaveAPI.cs

using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
namespace HoloMonApp.Character.Behave.Purpose
{
    public class HoloMonPurposeBehaveAPI : MonoBehaviour, HoloMonBehaveIF
    {
        /// <summary>
        /// 共通参照
        /// </summary>
        private HoloMonBehaveReference p_Reference;

        /// <summary>
        /// 初期化
        /// </summary>
        /// <param name="reference"></param>
        public void AwakeInit(HoloMonBehaveReference reference)
        {
            p_Reference = reference;
        }
    }
}

・HoloMonPurposeBehaveAPI.cs

using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
namespace HoloMonApp.Character.Behave.Purpose
{
    public class HoloMonActionModeLogicAPI : MonoBehaviour, HoloMonBehaveIF
    {
        /// <summary>
        /// 共通参照
        /// </summary>
        private HoloMonBehaveReference p_Reference;

        /// <summary>
        /// 初期化
        /// </summary>
        /// <param name="reference"></param>
        public void AwakeInit(HoloMonBehaveReference reference)
        {
            p_Reference = reference;
        }
    }
}

これら2つの型を以下のスクリプトのように Interface 型で SerializeField してもオブジェクトフィールドには表示されません。
・HoloMonBehaveTestAPI.cs

using UnityEngine;
namespace HoloMonApp.Character.Behave
{
    public class HoloMonBehaveTestAPI : MonoBehaviour
    {
        [SerializeField]
        private HoloMonBehaveIF p_PurposeBehaveAPI;

        [SerializeField]
        private HoloMonBehaveIF p_ActionModeLogicAPI;
    }
}

f:id:bluebirdofoz:20220331234606j:plain

オブジェクトフィールドに表示するには以下のように継承した実体の型で指定する必要があります。
・HoloMonBehaveTestAPI.cs

using UnityEngine;
using HoloMonApp.Character.Behave.Purpose;
using HoloMonApp.Character.Behave.ModeLogic;
namespace HoloMonApp.Character.Behave
{
    public class HoloMonBehaveTestAPI : MonoBehaviour
    {
        [SerializeField]
        private HoloMonPurposeBehaveAPI p_PurposeBehaveAPI;

        [SerializeField]
        private HoloMonActionModeLogicAPI p_ActionModeLogicAPI;
    }
}

f:id:bluebirdofoz:20220331234706j:plain

対処

Unity の仕組みとして有効な解決策はなく、オブジェクトフィールドでは実体の型で指定するしかないようです。
今回は以下のようにスクリプト側でフィールドを全て Interface 型に再変換してアクセスすることで対処を行いました。
・HoloMonBehaveAPI.cs

using UnityEngine;
using HoloMonApp.Character.Behave.Purpose;
using HoloMonApp.Character.Behave.ModeLogic;
namespace HoloMonApp.Character.Behave
{
    public class HoloMonBehaveTestAPI : MonoBehaviour
    {
        [SerializeField]
        private HoloMonPurposeBehaveAPI p_PurposeBehaveAPI;

        [SerializeField]
        private HoloMonActionModeLogicAPI p_ActionModeLogicAPI;
        
        /// <summary>
        /// インタフェースの参照リスト
        /// </summary>
        private List<HoloMonBehaveIF> p_ListIF
            => p_ListIFInstance
            ?? (p_ListIFInstance = new List<HoloMonBehaveIF>()
            {
                p_PurposeBehaveAPI,
                p_ActionModeLogicAPI,
            });

        /// <summary>
        /// インタフェースの参照リスト(実体)
        /// </summary>
        private List<HoloMonBehaveIF> p_ListIFInstance;

        /// <summary>
        /// 初期化
        /// </summary>
        private void Awake()
        {
            HoloMonBehaveReference Reference = this.GetComponent<HoloMonBehaveReference>();
            foreach (HoloMonBehaveIF instance in p_ListIF)
            {
                instance.AwakeInit(Reference);
            }
        }
    }
}

f:id:bluebirdofoz:20220331234720j:plain