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