LoginSignup
2
0

More than 5 years have passed since last update.

[Unity] エディタ拡張と型リフレクション

Posted at

エディタ拡張と型リフレクション

注意

この文章にはリフレクションを実行時に使用する事を前提とした内容が含まれています。
実行時にリフレクションを行おうとすると拒否反応が出る訓練されたエンジニアの方はご注意ください。

概要

ふとC#はリフレクションを行うことが可能な言語だったのを思い出し
エディタ拡張と組み合わせて何かできないか試してみる。

[SerializeField]
[ReflectionSubClassPopup(typeof(CharacterAction),true)]
private List<string> _actionTypeNameList = new List<string>();
public IReadOnlyCollection<string> ActionTypeNameList  
{ 
    get { return _actionTypeNameList; } 
}

これを

foreach(var item in ActionTypeNameList){
    this.AddComponent(Type.GetType(item));
}

とすることでコンポーネントの追加を行いたい場面があったとする。
パッと浮かんだのは、EditorGUI.Popupに特定クラスから派生したクラス名を列挙できないか というもの。

リフレクション

取り敢えず派生クラスを取得するコードを書いてみる。

public static IEnumerable<Type> GetSubClasses(Type type)
{
    var t = Assembly.GetAssembly(type)
        .GetTypes()
        .Where(m => m.IsSubclassOf(type) && !m.IsAbstract);
    return t;
}

うわぁ思ったより簡単に取得できる。
引数でIsAbstract判定を行うかどうか等を追加すればそれなりに便利な関数になりそう。

この関数をエディタ拡張と組み合わせていく。

エディタ拡張

まず属性を定義

ReflectionSubClassPopupAttribute.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Egliss
{
    public class ReflectionSubClassPopupAttribute: PropertyAttribute 
    {
        public ReflectionSubClassPopupAttribute(Type t, bool isNothingable = false)
        {
            this.type = t;
            this.isNothingable = isNothingable;
        }
        public Type type { get; private set; }
        public bool isNothingable { get; private set; }
    }
}

次にその属性に適応するPropertyDrawerを定義

ReflectionSubClassPopupDrawer.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using System.Reflection;

namespace Egliss.Editors
{
    [CustomPropertyDrawer(typeof(ReflectionSubClassPopupAttribute))]
    public class ReflectionSubClassPopupDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var atr = this.attribute as ReflectionSubClassPopupAttribute;
            var types = EditorHelper.GetSubClasses(atr.type).Select(m => m.FullName).ToList();
            var index = types.FindIndex(m => m == property.stringValue);

            //add nothing
            if (atr.isNothingable)
            {
                types.Insert(0, "Nothing");
                index++;
            }
            if (types.Count == -1)
            {
                var typeName = Assembly.GetAssembly(atr.type).FullName;
                EditorGUI.LabelField(position, typeName + "'s subclass not found...");
                return;
            }

            //type string missing
            if(index == -1)
                index = 0;

            property.stringValue = types[EditorGUI.Popup(position, label.text, index, types.ToArray())];
        }
    }
}

これで準備OK!

結果

属性を変数に付与して実際にインスペクタをのぞいてみる


[SerializeField]
[ReflectionSubClassPopup(typeof(CharacterAction), true)]
private string _actionTypeName;

2018y02m27d_034621041.jpg

無事列挙されるのを確認。
後はこれをListにも対応していく つもりだったのだが...

[SerializeField]
[ReflectionSubClassPopup(typeof(CharacterAction),true)]
private List<string> _actionList = new List<string>();

これが

2018y02m27d_034835518.jpg

こう表示されてしまった。
属性がListそのものではなくListの要素に付与されている...

今回はこの挙動が目的だったので深くは追及しないが、
気が向いたらListへのPropertyDrawerの書き方も調べておこう。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0