前提
UnityはコンポーネントをInspector上でアタッチすることによって参照を取得することができる
Interfaceを使うと疎結合になるため良い設計をするためには必須
Interfaceは通常シリアライズできないためInspectorで登録できない
対応策
- Interfaceを実装しているComponentの参照を保持
- InterfaceはGetComponentで取得する
サンプル
ISomeInterface.cs
public interface ISomeInterface
{
void LogAny();
}
SomeController.cs
public class SomeController : MonoBehaviour
{
// 1. コンポーネントの参照を取得
[SerializeField] Component someComponent;
// 2. Interfaceを取得してキャッシュ
ISomeInterface logger = null;
ISomeInterface Logger{
get{
if(logger == null)
logger = someComponent.GetComponent<ISomeInterface>();
return logger;
}
}
void Start()
{
Logger.LogAny();
}
}
SomeController
のように書くことで、Interfaceを取得することは可能になる。
しかし、このコードではsomeComponentにISomeInterfaceを実装していないコンポーネントでも登録できてしまう。
そこで登録できるコンポーネントに制限をかける。
登録できるコンポーネントに制限をかける
CustomoAttributeを作成する。
ComponentRestrictionAttribute.cs
using System;
using UnityEngine;
[System.AttributeUsage (System.AttributeTargets.Field,Inherited = true, AllowMultiple = false)]
public class ComponentRestrictionAttribute : PropertyAttribute
{
public Type type;
public ComponentRestrictionAttribute(Type type){
this.type = type;
}
}
ComponentRestrictionDrawer.cs
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer( typeof ( ComponentRestrictionAttribute ) )]
public class ComponentRestrictionDrawer : PropertyDrawer
{
public override void OnGUI (Rect position, SerializedProperty property, GUIContent label)
{
var restriction = (ComponentRestrictionAttribute)attribute;
if (property.propertyType == SerializedPropertyType.ObjectReference) {
EditorGUI.ObjectField(position, property, restriction.type);
}else{
EditorGUI.PropertyField(position, property);
}
}
}
ComponentRestrictionDrawerはEditorフォルダ以下に作成
SomeController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SomeController : MonoBehaviour
{
// ComponentRestriction追加
[SerializeField, ComponentRestriction(typeof(ISomeInterface))] Component someComponent;
ISomeInterface logger = null;
ISomeInterface Logger{
get{
if(logger == null)
logger = someComponent.GetComponent<ISomeInterface>();
return logger;
}
}
void Start()
{
Logger.LogAny();
}
}
ComponentRrestrictionを追加
これでISomeInterfaceを実装していないコンポーネントは登録できなくなる
GifのAttributeTestは以下
AttributeTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttributeTest : MonoBehaviour, ISomeInterface
{
public void LogAny()
{
Debug.Log("Hello");
}
}
まとめ
コード量は多くなってしまうもののInterfaceを取得することができた。
コードテンプレート、または自動生成がないとつらい。