Qiitaには初投稿になります
よろしくお願いします
#背景
インスペクター上でよく見かけるこれ↓
(右の◎でTextureやらGameObjectやらを選択してセットできる)
これの名前がObjectFieldという名前らしいです。
優れモノで選択するときはこんな感じに↓
インタラクティブに選べます。
この選択画面ObjectPickerやらObjectSelectorと呼ばれているみたいです。
この画面の上部の文字入力欄に
文字を入力するとファイル名でフィルタリングをしてくれるのですが、Pickerを開いた段階でフィルタリングしたい!
#前提
UnityのEditor拡張です
環境
- Unity 2017.3.f1
- JetBrains Rider
#ObjectFieldについて
Editor拡張でObjectFieldを表示するときは
ドキュメント
これを使って、
public static Object ObjectField(Object obj, Type objType, bool allowSceneObjects, params GUILayoutOption[] options);
public class TestEditor:EditorWindow
{
private Sprite back;
private void OnGUI()
{
Sprite newSprite =
(Sprite) EditorGUILayout.ObjectField(back, typeof(Sprite),
false);
if(newSprite!=null)back=newSprite;
}
こんな感じでObjectField表示します。
上のドキュメントのobjTypeで設定したTypeで表示するオブジェクトを制約できます。
objTypeを設定すれば設定したいオブジェクトを大体は絞り込めるようになるんですが、画像とか多くなってくると設定するのもひと手間になってくるので予め文字列をフィルタに入れるようにして探しやすくします。
#reflectionを用いたフィルタ文字列の設定
元々、この投稿にあるソースを改良したのでこちらを読んでくれればいいのですが、
ObjectField Asset Browser, How to open it with code, not the objectfield
上の記事での質問は「ObjectFieldを使わないであのPickerを使いてえ!」
に対して
ObjectSelectorをリフレクションを使って(ObjectSelectorは公開されていないので参照できません)取ってくる。
というものになります。
でObjectSelectorのソースにinternal string searchFilter
という変数があったのでそれをObjectSelectorを表示されるときに一緒に設定しました。(サンキューBunny83)
以下にBunny83さんのソースに自分が少し追加したソースコードを示します.
using UnityEditor;
using System.Reflection;
public static class ObjectSelectorWrapper
{
private static System.Type T;
private static bool oldState = false;
static ObjectSelectorWrapper()
{
T = System.Type.GetType("UnityEditor.ObjectSelector,UnityEditor");
}
//追加した部分
public static void SetFilterString(string filter)
{
if (filter == null) filter = String.Empty;
FieldInfo field = T.GetField("m_SearchFilter", BindingFlags.SetProperty| BindingFlags.Instance | BindingFlags.NonPublic|BindingFlags.Public);
field.SetValue(Get(),filter);
}
//追加した部分終わり
private static EditorWindow Get()
{
PropertyInfo P = T.GetProperty("get", BindingFlags.Public | BindingFlags.Static);
return P.GetValue(null,null) as EditorWindow;
}
public static void ShowSelector(System.Type aRequiredType)
{
MethodInfo ShowMethod = T.GetMethod("Show",BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
ShowMethod.Invoke(Get (), new object[]{null,aRequiredType,null, true});
}
public static T GetSelectedObject<T>() where T : UnityEngine.Object
{
MethodInfo GetCurrentObjectMethod = ObjectSelectorWrapper.T.GetMethod("GetCurrentObject",BindingFlags.Static | BindingFlags.Public);
return GetCurrentObjectMethod.Invoke(null,null) as T;
}
public static bool isVisible
{
get
{
PropertyInfo P = T.GetProperty("isVisible", BindingFlags.Public | BindingFlags.Static);
return (bool)P.GetValue(null,null);
}
}
public static bool HasJustBeenClosed()
{
bool visible = isVisible;
if (visible != oldState && visible == false)
{
oldState = false;
return true;
}
oldState = visible;
return false;
}
}
#使い方
先ほどのTestEditorに適用してみます
using UnityEditor;
using UnityEngine;
public class TestEditor:EditorWindow
{
[MenuItem("Test/Test")]
static void Open()
{
GetWindow<TestEditor>();
}
private Sprite back;
private void OnGUI()
{
Sprite newSprite =
(Sprite) EditorGUILayout.ObjectField(back, typeof(Sprite),
false);
if (ObjectSelectorWrapper.isVisible)
{
ObjectSelectorWrapper.SetFilterString("arrow");
}
}
}
#注意
Reflectionは特定のメソッド、フィールドに依存するためUnity側の変更によっては動かないこともありますので注意してください
TestEditorでの実装は若干問題があります。それはPickerの文字列フィルタへの挿入が他のObjectFieldでも作用してしまうことがあります。
なので、このObjectFieldがクリックされたらって実装しようと思ったんですけどGUIの特定ができなくて投げました(涙目)
すまぬ…
もしこんな方法があるよ~ってのがある方はコメントお願いします。
…まあObjectFieldに固執せずObjectFiedlっぽいの実装してGUI.Buttonでやればいいと思うんですが…