using UnityEngine; using System.Collections.Generic; using System.Reflection; #if UNITY_EDITOR using UnityEditor; using System.Linq; #endif /// /// ラベル名からアセットを検索し、アタッチします /// public class LabelSearchAttribute : PropertyAttribute { /// /// falseで検索が完了していなくても、とりあえずインスペクターを表示するようになる /// trueだと検索が終わるまでインスペクターが表示されない /// public bool init = false; /// /// インスペクターが表示された初回のみ検索が行われる /// falseだと検索が行われず描画されない /// public bool search = true; /// /// 検索するラベル名 /// public string labelName; /// /// インスペクターにラベル名を表示するかどうか /// trueで表示する /// public bool canPrintLabelName = false; /// /// 配列の時に使用する /// インスペクターを表示した時、最初から配列のフィールドを描画したい場合trueにする /// public bool foldout = false; /// /// 検索順が降順か昇順か /// public Direction direction = Direction.ASC; /// /// 取得する最大数 /// 負や0を指定しても2147483647になる /// public int limit = 2147483647; /// /// 検索の高速化のためTypeをキャッシュしておく /// public static Dictionary assetTypes = new Dictionary(); public LabelSearchAttribute(string labelName) { this.labelName = labelName; } public LabelSearchAttribute(string labelName, int limit) { if (Mathf.Sign(limit) == 1) { this.limit = limit; } this.labelName = labelName; } public LabelSearchAttribute(string labelName, Direction direction) { this.labelName = labelName; this.direction = direction; } public LabelSearchAttribute(string labelName, int limit, Direction direction) { this.labelName = labelName; if (Mathf.Sign(limit) == 1) { this.limit = limit; } this.direction = direction; } public enum Direction { ASC, DESC } } #if UNITY_EDITOR /// /// Label drawer. /// [CustomPropertyDrawer(typeof(LabelSearchAttribute))] public class LabelSearchDrawer : PropertyDrawer { /// /// GUIの高さ /// private const int CONTENT_HEIGHT = 16; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (labelSearchAttribute.init == false) { labelSearchAttribute.init = true; return; } //デバッグ時使用 ( 検索時間 ) //double start = EditorApplication.timeSinceStartup; if (labelSearchAttribute.canPrintLabelName) { label.text += string.Format(" ( Label = {0} )", labelSearchAttribute.labelName); } if (property.isArray) { EditorGUI.indentLevel = 0; labelSearchAttribute.foldout = EditorGUI.Foldout(position, labelSearchAttribute.foldout, label); if (labelSearchAttribute.search) { DrawArrayProperty(position, property, label); //デバッグ時使用 ( 検索時間 ) //Debug.Log (((float)EditorApplication.timeSinceStartup - start) + "ms"); } else { DrawCachedArrayProperty(position, property, label); } } else { if (labelSearchAttribute.search) { DrawSingleProperty(position, property, label); ////デバッグ時使用 ( 検索時間 ) //Debug.Log (((float)EditorApplication.timeSinceStartup - start) + "ms"); } else { DrawCachedSingleProperty(position, property, label); } } labelSearchAttribute.search = false; } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { float height = 0; if (property.isArray && labelSearchAttribute.foldout) { height = (property.arraySize + 1) * CONTENT_HEIGHT; } return base.GetPropertyHeight(property, label) + height; } LabelSearchAttribute labelSearchAttribute { get { return (LabelSearchAttribute) attribute; } } /// /// キャッシュされたプロパティを使用して描画する /// /// /// Position. /// /// /// Property. /// /// /// Label. /// void DrawCachedSingleProperty(Rect position, SerializedProperty property, GUIContent label) { property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue, GetType(property), false); } /// /// キャッシュされた配列を使用して描画する /// 負荷削減のため一定間隔で検索を行うようにした /// /// /// Position. /// /// /// Property. /// /// /// Label. /// void DrawCachedArrayProperty(Rect position, SerializedProperty property, GUIContent label) { if (labelSearchAttribute.foldout) { position.y += CONTENT_HEIGHT; EditorGUI.indentLevel = 2; System.Type type = GetType(property.GetArrayElementAtIndex(0)); EditorGUI.LabelField(position, "Size", property.arraySize.ToString()); for (int i = 0; i < property.arraySize; i++) { position.y += CONTENT_HEIGHT; position.height = CONTENT_HEIGHT; GUIContent content = EditorGUIUtility.ObjectContent(property.GetArrayElementAtIndex(i).objectReferenceValue, type); content.image = AssetPreview.GetMiniTypeThumbnail(type); // 要素1つ1つにフォーカスが当たらないためObjectFieldである必要はないのでLabelFieldで描画 // PingObjectの機能使いたいけど... // EditorGUI.ObjectField (position, new GUIContent (ObjectNames.NicifyVariableName ("Element" + i)), property.GetArrayElementAtIndex (i).objectReferenceValue, type, false); EditorGUI.LabelField(position, new GUIContent(ObjectNames.NicifyVariableName("Element" + i)), content); } } } /// /// アセットを検索して描画する /// /// /// Position. /// /// /// Property. /// /// /// Label. /// void DrawSingleProperty(Rect position, SerializedProperty property, GUIContent label) { System.Type type = GetType(property); property.objectReferenceValue = null; foreach (string path in GetAllAssetPath()) { System.Type assetType = null; Object asset = null; if (LabelSearchAttribute.assetTypes.TryGetValue(path, out assetType) == false) { asset = AssetDatabase.LoadMainAssetAtPath(path); if (asset == null) { continue; } assetType = asset.GetType(); LabelSearchAttribute.assetTypes.Add(path, assetType); } if (type != assetType) { continue; } if (asset == null) { asset = AssetDatabase.LoadMainAssetAtPath(path); if (asset == null) { continue; } } if ( string.IsNullOrEmpty( AssetDatabase.GetLabels(asset).FirstOrDefault(l => l.Equals(labelSearchAttribute.labelName))) == false) { property.objectReferenceValue = asset; break; } } property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue, type, false); } /// /// 該当するアセットを複数検索して描画する /// /// /// Position. /// /// /// Property. /// /// /// Label. /// void DrawArrayProperty(Rect position, SerializedProperty property, GUIContent label) { int size = 0; EditorGUI.indentLevel = 2; if (labelSearchAttribute.foldout) { position.y += CONTENT_HEIGHT; EditorGUI.LabelField(position, "Size", property.arraySize.ToString()); } property.arraySize = 0; property.InsertArrayElementAtIndex(0); System.Type type = GetType(property.GetArrayElementAtIndex(0)); foreach (string path in GetAllAssetPath()) { System.Type assetType = null; Object asset = null; if (LabelSearchAttribute.assetTypes.TryGetValue(path, out assetType) == false) { asset = AssetDatabase.LoadMainAssetAtPath(path); assetType = asset.GetType(); LabelSearchAttribute.assetTypes.Add(path, assetType); } if (type != assetType) { continue; } if (asset == null) { asset = AssetDatabase.LoadMainAssetAtPath(path); } if ( string.IsNullOrEmpty( AssetDatabase.GetLabels(asset).FirstOrDefault(l => l.Equals(labelSearchAttribute.labelName))) == false) { property.arraySize = ++size; property.GetArrayElementAtIndex(size - 1).objectReferenceValue = asset; if (labelSearchAttribute.foldout) { position.y += CONTENT_HEIGHT; position.height = CONTENT_HEIGHT; GUIContent content = EditorGUIUtility.ObjectContent(property.GetArrayElementAtIndex(size - 1).objectReferenceValue, type); content.image = AssetPreview.GetMiniTypeThumbnail(type); // 要素1つ1つにフォーカスが当たらないためObjectFieldである必要はないのでLabelFieldで描画 // PingObjectの機能使いたいけど... // EditorGUI.ObjectField (position, new GUIContent (ObjectNames.NicifyVariableName ("Element" + i)), property.GetArrayElementAtIndex (i).objectReferenceValue, type, false); EditorGUI.LabelField(position, new GUIContent(ObjectNames.NicifyVariableName("Element" + (size - 1))), content); } } if (labelSearchAttribute.limit <= property.arraySize) { break; } } } /// /// 全てのアセットのパスを取得 /// /// /// The all asset path. /// string[] GetAllAssetPath() { string[] allAssetPath = AssetDatabase.GetAllAssetPaths(); System.Array.Sort(allAssetPath); if (labelSearchAttribute.direction.Equals(LabelSearchAttribute.Direction.DESC)) { System.Array.Reverse(allAssetPath); } return allAssetPath; } /// /// プロパティからTypeを取得 /// /// /// The type. /// /// /// Property. /// System.Type GetType(SerializedProperty property) { return Assembly.Load("UnityEngine.dll") .GetType("UnityEngine." + property.type.Replace("PPtr<$", "").Replace(">", "")); } } #endif