LabelSearchAttribute.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. #if UNITY_EDITOR
  5. using UnityEditor;
  6. using System.Linq;
  7. #endif
  8. /// <summary>
  9. /// ラベル名からアセットを検索し、アタッチします
  10. /// </summary>
  11. public class LabelSearchAttribute : PropertyAttribute
  12. {
  13. /// <summary>
  14. /// <para> falseで検索が完了していなくても、とりあえずインスペクターを表示するようになる</para>
  15. /// <para>trueだと検索が終わるまでインスペクターが表示されない</para>
  16. /// </summary>
  17. public bool init = false;
  18. /// <summary>
  19. /// <para>インスペクターが表示された初回のみ検索が行われる</para>
  20. /// <para>falseだと検索が行われず描画されない</para>
  21. /// </summary>
  22. public bool search = true;
  23. /// <summary>
  24. /// <para>検索するラベル名</para>
  25. /// </summary>
  26. public string labelName;
  27. /// <summary>
  28. /// <para>インスペクターにラベル名を表示するかどうか</para>
  29. /// <para>trueで表示する</para>
  30. /// </summary>
  31. public bool canPrintLabelName = false;
  32. /// <summary>
  33. /// <para>配列の時に使用する</para>
  34. /// <para>インスペクターを表示した時、最初から配列のフィールドを描画したい場合trueにする</para>
  35. /// </summary>
  36. public bool foldout = false;
  37. /// <summary>
  38. /// <para>検索順が降順か昇順か</para>
  39. /// </summary>
  40. public Direction direction = Direction.ASC;
  41. /// <summary>
  42. /// <para>取得する最大数</para>
  43. /// <para>負や0を指定しても2147483647になる
  44. /// </summary>
  45. public int limit = 2147483647;
  46. /// <summary>
  47. /// <para>検索の高速化のためTypeをキャッシュしておく</para>
  48. /// </summary>
  49. public static Dictionary<string, System.Type> assetTypes = new Dictionary<string, System.Type>();
  50. public LabelSearchAttribute(string labelName)
  51. {
  52. this.labelName = labelName;
  53. }
  54. public LabelSearchAttribute(string labelName, int limit)
  55. {
  56. if (Mathf.Sign(limit) == 1)
  57. {
  58. this.limit = limit;
  59. }
  60. this.labelName = labelName;
  61. }
  62. public LabelSearchAttribute(string labelName, Direction direction)
  63. {
  64. this.labelName = labelName;
  65. this.direction = direction;
  66. }
  67. public LabelSearchAttribute(string labelName, int limit, Direction direction)
  68. {
  69. this.labelName = labelName;
  70. if (Mathf.Sign(limit) == 1)
  71. {
  72. this.limit = limit;
  73. }
  74. this.direction = direction;
  75. }
  76. public enum Direction
  77. {
  78. ASC,
  79. DESC
  80. }
  81. }
  82. #if UNITY_EDITOR
  83. /// <summary>
  84. /// Label drawer.
  85. /// </summary>
  86. [CustomPropertyDrawer(typeof(LabelSearchAttribute))]
  87. public class LabelSearchDrawer : PropertyDrawer
  88. {
  89. /// <summary>
  90. /// GUIの高さ
  91. /// </summary>
  92. private const int CONTENT_HEIGHT = 16;
  93. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  94. {
  95. if (labelSearchAttribute.init == false)
  96. {
  97. labelSearchAttribute.init = true;
  98. return;
  99. }
  100. //デバッグ時使用 ( 検索時間 )
  101. //double start = EditorApplication.timeSinceStartup;
  102. if (labelSearchAttribute.canPrintLabelName)
  103. {
  104. label.text += string.Format(" ( Label = {0} )", labelSearchAttribute.labelName);
  105. }
  106. if (property.isArray)
  107. {
  108. EditorGUI.indentLevel = 0;
  109. labelSearchAttribute.foldout = EditorGUI.Foldout(position, labelSearchAttribute.foldout, label);
  110. if (labelSearchAttribute.search)
  111. {
  112. DrawArrayProperty(position, property, label);
  113. //デバッグ時使用 ( 検索時間 )
  114. //Debug.Log (((float)EditorApplication.timeSinceStartup - start) + "ms");
  115. }
  116. else
  117. {
  118. DrawCachedArrayProperty(position, property, label);
  119. }
  120. }
  121. else
  122. {
  123. if (labelSearchAttribute.search)
  124. {
  125. DrawSingleProperty(position, property, label);
  126. ////デバッグ時使用 ( 検索時間 )
  127. //Debug.Log (((float)EditorApplication.timeSinceStartup - start) + "ms");
  128. }
  129. else
  130. {
  131. DrawCachedSingleProperty(position, property, label);
  132. }
  133. }
  134. labelSearchAttribute.search = false;
  135. }
  136. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  137. {
  138. float height = 0;
  139. if (property.isArray && labelSearchAttribute.foldout)
  140. {
  141. height = (property.arraySize + 1) * CONTENT_HEIGHT;
  142. }
  143. return base.GetPropertyHeight(property, label) + height;
  144. }
  145. LabelSearchAttribute labelSearchAttribute
  146. {
  147. get { return (LabelSearchAttribute) attribute; }
  148. }
  149. /// <summary>
  150. /// <para>キャッシュされたプロパティを使用して描画する</para>
  151. /// </summary>
  152. /// <param name='position'>
  153. /// Position.
  154. /// </param>
  155. /// <param name='property'>
  156. /// Property.
  157. /// </param>
  158. /// <param name='label'>
  159. /// Label.
  160. /// </param>
  161. void DrawCachedSingleProperty(Rect position, SerializedProperty property, GUIContent label)
  162. {
  163. property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue,
  164. GetType(property), false);
  165. }
  166. /// <summary>
  167. /// <para>キャッシュされた配列を使用して描画する</para>
  168. /// <para>負荷削減のため一定間隔で検索を行うようにした</para>
  169. /// </summary>
  170. /// <param name='position'>
  171. /// Position.
  172. /// </param>
  173. /// <param name='property'>
  174. /// Property.
  175. /// </param>
  176. /// <param name='label'>
  177. /// Label.
  178. /// </param>
  179. void DrawCachedArrayProperty(Rect position, SerializedProperty property, GUIContent label)
  180. {
  181. if (labelSearchAttribute.foldout)
  182. {
  183. position.y += CONTENT_HEIGHT;
  184. EditorGUI.indentLevel = 2;
  185. System.Type type = GetType(property.GetArrayElementAtIndex(0));
  186. EditorGUI.LabelField(position, "Size", property.arraySize.ToString());
  187. for (int i = 0; i < property.arraySize; i++)
  188. {
  189. position.y += CONTENT_HEIGHT;
  190. position.height = CONTENT_HEIGHT;
  191. GUIContent content =
  192. EditorGUIUtility.ObjectContent(property.GetArrayElementAtIndex(i).objectReferenceValue, type);
  193. content.image = AssetPreview.GetMiniTypeThumbnail(type);
  194. // 要素1つ1つにフォーカスが当たらないためObjectFieldである必要はないのでLabelFieldで描画
  195. // PingObjectの機能使いたいけど...
  196. // EditorGUI.ObjectField (position, new GUIContent (ObjectNames.NicifyVariableName ("Element" + i)), property.GetArrayElementAtIndex (i).objectReferenceValue, type, false);
  197. EditorGUI.LabelField(position, new GUIContent(ObjectNames.NicifyVariableName("Element" + i)), content);
  198. }
  199. }
  200. }
  201. /// <summary>
  202. /// <para>アセットを検索して描画する</para>
  203. /// </summary>
  204. /// <param name='position'>
  205. /// Position.
  206. /// </param>
  207. /// <param name='property'>
  208. /// Property.
  209. /// </param>
  210. /// <param name='label'>
  211. /// Label.
  212. /// </param>
  213. void DrawSingleProperty(Rect position, SerializedProperty property, GUIContent label)
  214. {
  215. System.Type type = GetType(property);
  216. property.objectReferenceValue = null;
  217. foreach (string path in GetAllAssetPath())
  218. {
  219. System.Type assetType = null;
  220. Object asset = null;
  221. if (LabelSearchAttribute.assetTypes.TryGetValue(path, out assetType) == false)
  222. {
  223. asset = AssetDatabase.LoadMainAssetAtPath(path);
  224. if (asset == null)
  225. {
  226. continue;
  227. }
  228. assetType = asset.GetType();
  229. LabelSearchAttribute.assetTypes.Add(path, assetType);
  230. }
  231. if (type != assetType)
  232. {
  233. continue;
  234. }
  235. if (asset == null)
  236. {
  237. asset = AssetDatabase.LoadMainAssetAtPath(path);
  238. if (asset == null)
  239. {
  240. continue;
  241. }
  242. }
  243. if (
  244. string.IsNullOrEmpty(
  245. AssetDatabase.GetLabels(asset).FirstOrDefault(l => l.Equals(labelSearchAttribute.labelName))) ==
  246. false)
  247. {
  248. property.objectReferenceValue = asset;
  249. break;
  250. }
  251. }
  252. property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue, type,
  253. false);
  254. }
  255. /// <summary>
  256. /// <para>該当するアセットを複数検索して描画する</para>
  257. /// </summary>
  258. /// <param name='position'>
  259. /// Position.
  260. /// </param>
  261. /// <param name='property'>
  262. /// Property.
  263. /// </param>
  264. /// <param name='label'>
  265. /// Label.
  266. /// </param>
  267. void DrawArrayProperty(Rect position, SerializedProperty property, GUIContent label)
  268. {
  269. int size = 0;
  270. EditorGUI.indentLevel = 2;
  271. if (labelSearchAttribute.foldout)
  272. {
  273. position.y += CONTENT_HEIGHT;
  274. EditorGUI.LabelField(position, "Size", property.arraySize.ToString());
  275. }
  276. property.arraySize = 0;
  277. property.InsertArrayElementAtIndex(0);
  278. System.Type type = GetType(property.GetArrayElementAtIndex(0));
  279. foreach (string path in GetAllAssetPath())
  280. {
  281. System.Type assetType = null;
  282. Object asset = null;
  283. if (LabelSearchAttribute.assetTypes.TryGetValue(path, out assetType) == false)
  284. {
  285. asset = AssetDatabase.LoadMainAssetAtPath(path);
  286. assetType = asset.GetType();
  287. LabelSearchAttribute.assetTypes.Add(path, assetType);
  288. }
  289. if (type != assetType)
  290. {
  291. continue;
  292. }
  293. if (asset == null)
  294. {
  295. asset = AssetDatabase.LoadMainAssetAtPath(path);
  296. }
  297. if (
  298. string.IsNullOrEmpty(
  299. AssetDatabase.GetLabels(asset).FirstOrDefault(l => l.Equals(labelSearchAttribute.labelName))) ==
  300. false)
  301. {
  302. property.arraySize = ++size;
  303. property.GetArrayElementAtIndex(size - 1).objectReferenceValue = asset;
  304. if (labelSearchAttribute.foldout)
  305. {
  306. position.y += CONTENT_HEIGHT;
  307. position.height = CONTENT_HEIGHT;
  308. GUIContent content =
  309. EditorGUIUtility.ObjectContent(property.GetArrayElementAtIndex(size - 1).objectReferenceValue,
  310. type);
  311. content.image = AssetPreview.GetMiniTypeThumbnail(type);
  312. // 要素1つ1つにフォーカスが当たらないためObjectFieldである必要はないのでLabelFieldで描画
  313. // PingObjectの機能使いたいけど...
  314. // EditorGUI.ObjectField (position, new GUIContent (ObjectNames.NicifyVariableName ("Element" + i)), property.GetArrayElementAtIndex (i).objectReferenceValue, type, false);
  315. EditorGUI.LabelField(position,
  316. new GUIContent(ObjectNames.NicifyVariableName("Element" + (size - 1))), content);
  317. }
  318. }
  319. if (labelSearchAttribute.limit <= property.arraySize)
  320. {
  321. break;
  322. }
  323. }
  324. }
  325. /// <summary>
  326. /// 全てのアセットのパスを取得
  327. /// </summary>
  328. /// <returns>
  329. /// The all asset path.
  330. /// </returns>
  331. string[] GetAllAssetPath()
  332. {
  333. string[] allAssetPath = AssetDatabase.GetAllAssetPaths();
  334. System.Array.Sort(allAssetPath);
  335. if (labelSearchAttribute.direction.Equals(LabelSearchAttribute.Direction.DESC))
  336. {
  337. System.Array.Reverse(allAssetPath);
  338. }
  339. return allAssetPath;
  340. }
  341. /// <summary>
  342. /// プロパティからTypeを取得
  343. /// </summary>
  344. /// <returns>
  345. /// The type.
  346. /// </returns>
  347. /// <param name='property'>
  348. /// Property.
  349. /// </param>
  350. System.Type GetType(SerializedProperty property)
  351. {
  352. return
  353. Assembly.Load("UnityEngine.dll")
  354. .GetType("UnityEngine." + property.type.Replace("PPtr<$", "").Replace(">", ""));
  355. }
  356. }
  357. #endif