経緯
Unityのエディタ拡張でいろいろ便利な機能を作ろうとしたりした時、コンポーネントのフィールド中にカスタムクラスがあったとする、そのカスタムクラス内のprivateなメンバに対してInspectorから操作をしたいときのメモ
以下のスクリプト例 Sampleクラスのhogeフィールドへのアクセスをそのまま行おうとしてもprivateなメンバな為直接は操作できない
using UnityEngine;
# if UNITY_EDITOR
using UnityEditor;
# endif
namespace Sample
{
/// <summary>
/// サンプルクラス.
/// </summary>
public class Sample : MonoBehaviour
{
/// <summary>
/// 何かしらのデータクラス.
/// </summary>
[System.Serializable]
public class HogeParam
{
[SerializeField] int id;
public int Id => id;
public HogeParam(
int argId)
{
id = argId;
}
}
[SerializeField] string characterName;
[SerializeField] HogeParam hoge;
# if UNITY_EDITOR
[CustomEditor(typeof(Sample))]
class SampleEditor : Editor
{
public override void OnInspectorGUI()
{
var component = target as Sample;
EditorGUILayout.LabelField("エディタ拡張だべ");
component.characterName = EditorGUILayout.TextField("キャラ名", component.characterName);
// privateなメンバなためアクセスできない,プロパティもゲッターのみなので値のセットはできない.
//component.hoge.Id = EditorGUILayout.IntField("ID", component.hoge.Id);
}
}
# endif
}
}
RTAプレイヤー的最速解決方法
最も手っ取り早く解決するのはそもそものHogeParamクラスのメンバフィールドをpublicにしてしまうかpublicプロパティを使って外部からアクセスできるようにする方法
using UnityEngine;
# if UNITY_EDITOR
using UnityEditor;
# endif
namespace Sample
{
/// <summary>
/// サンプルクラス.
/// </summary>
public class Sample : MonoBehaviour
{
/// <summary>
/// 何かしらのデータクラス.
/// </summary>
[System.Serializable]
public class HogeParam
{
[SerializeField] int id;
public int Id
{
get { return id; }
set { id = value; }
}
public HogeParam(
int argId)
{
id = argId;
}
}
[SerializeField] string characterName;
[SerializeField] HogeParam hoge;
# if UNITY_EDITOR
[CustomEditor(typeof(Sample))]
class SampleEditor : Editor
{
public override void OnInspectorGUI()
{
var component = target as Sample;
EditorGUILayout.LabelField("エディタ拡張だべ");
component.characterName = EditorGUILayout.TextField("キャラ名", component.characterName);
component.hoge.Id = EditorGUILayout.IntField("ID", component.hoge.Id);
}
}
# endif
}
}
publicなフィールドを持ちたくないマン的解決方法
人によってはフィールドをpublicにするなんてとんでもない! プロパティで自由に外部から操作できないパラメータも作りたい!けどInspector拡張からは操作できるようにしたいって人もいると思われるのでその場合はC#のReflrectionを利用すると解決できる
using UnityEngine;
#if UNITY_EDITOR
using System.Reflection;
using UnityEditor;
#endif
namespace Sample
{
/// <summary>
/// サンプルクラス.
/// </summary>
public class Sample : MonoBehaviour
{
/// <summary>
/// 何かしらのデータクラス.
/// </summary>
[System.Serializable]
public class HogeParam
{
[SerializeField] int id;
public HogeParam(
int argId)
{
id = argId;
}
}
[SerializeField] string characterName;
[SerializeField] HogeParam hoge;
#if UNITY_EDITOR
[CustomEditor(typeof(Sample))]
class Class1Editor : Editor
{
public override void OnInspectorGUI()
{
var component = target as Sample;
EditorGUILayout.LabelField("エディタ拡張だべ");
component.characterName = EditorGUILayout.TextField("キャラ名", component.characterName);
EditorGUILayout.LabelField("Hoge");
EditorGUI.indentLevel++;
// Reflectionを利用してパラメータを操作.
{
System.Type fieldType = component.hoge.GetType();
FieldInfo fieldInfo = fieldType.GetField("id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
int tempId = (int)fieldInfo.GetValue(component.hoge);
fieldInfo.SetValue(component.hoge, (int)EditorGUILayout.IntField("ID", tempId));
}
EditorGUI.indentLevel--;
}
}
#endif
}
}
無事Inspectorからカスタムクラス内部のprivateなメンバへのアクセスができた
あとがき
Reflectionは便利だが乱用するとソースの可読性とかそもそものアクセス制限が意味をなさなくなるのでゲームロジック本編での利用や濫用はやめたほうがいいと個人的に思ってる
その辺はプロジェクトの方針等との相談
僕としてはエディタ拡張のみOKかなぁと思ってる