Ei kuvausta
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

GridBrushPickStore.cs 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Text.RegularExpressions;
  5. using UnityEditorInternal;
  6. using UnityEngine;
  7. using Object = UnityEngine.Object;
  8. namespace UnityEditor.Tilemaps
  9. {
  10. /// <summary>
  11. /// A ScriptableObject that stores picks for a particular GridBrushBase type.
  12. /// The picks include a list of picks defined by the user and a limited list
  13. /// of picks which were last made by the user. The picks can be loaded onto
  14. /// the active Brush in the TilePalette.
  15. /// </summary>
  16. public class GridBrushPickStore : ScriptableObject
  17. {
  18. private static readonly string s_LibraryPickPath = "Library/GridBrush/Pick";
  19. private static readonly string s_LastPickPath = "Library/GridBrush/Last";
  20. private static readonly string s_UserPickPath = "Library/GridBrush/User";
  21. private static readonly string s_LibraryAssetName = "Default";
  22. private static readonly string s_AssetExtension = ".asset";
  23. private static readonly string k_GridBrushPickLastSavedMaxCountPref = "GridBrushPickLastSavedMaxCount";
  24. private static readonly string k_GridBrushPickUserSavedMaxCountPref = "GridBrushPickUserSavedMaxCount";
  25. private static readonly string k_GridBrushPickLastIndexPref = "GridBrushPickLastIndex";
  26. private static readonly string k_GridBrushPickLastSavedCountPref = "GridBrushPickLastSavedCount";
  27. internal static int gridBrushPickLastSavedMaxCount
  28. {
  29. get => EditorPrefs.GetInt(k_GridBrushPickLastSavedMaxCountPref, 5);
  30. set => EditorPrefs.SetInt(k_GridBrushPickLastSavedMaxCountPref, value);
  31. }
  32. internal static int gridBrushPickUserSavedMaxCount
  33. {
  34. get => EditorPrefs.GetInt(k_GridBrushPickUserSavedMaxCountPref, 50);
  35. set => EditorPrefs.SetInt(k_GridBrushPickUserSavedMaxCountPref, value);
  36. }
  37. internal static int gridBrushPickLastIndex
  38. {
  39. get => EditorPrefs.GetInt(k_GridBrushPickLastIndexPref, -1);
  40. set => EditorPrefs.SetInt(k_GridBrushPickLastIndexPref, value);
  41. }
  42. internal static int gridBrushPickLastSavedCount
  43. {
  44. get => EditorPrefs.GetInt(k_GridBrushPickLastSavedCountPref, 0);
  45. set => EditorPrefs.SetInt(k_GridBrushPickLastSavedCountPref, value);
  46. }
  47. [HideInInspector]
  48. [SerializeField]
  49. private int m_UserSavedCount;
  50. private Type m_FilteredBrushType;
  51. [HideInInspector]
  52. [SerializeField]
  53. private string m_FilteredBrushText;
  54. private List<GridBrushBase> m_LastSavedBrushes = new List<GridBrushBase>();
  55. [SerializeField]
  56. private List<GridBrushBase> m_UserSavedBrushes = new List<GridBrushBase>();
  57. private List<GridBrushBase> m_FilteredUserSavedBrushes = new List<GridBrushBase>();
  58. /// <summary>
  59. /// The index of the latest last pick that was made.
  60. /// </summary>
  61. public int lastIndex => gridBrushPickLastIndex;
  62. /// <summary>
  63. /// A list of GridBrushBases which represent the last picks made.
  64. /// </summary>
  65. public List<GridBrushBase> lastSavedBrushes
  66. {
  67. get => m_LastSavedBrushes;
  68. }
  69. /// <summary>
  70. /// A list of GridBrushBases which represent the user picks made.
  71. /// </summary>
  72. public List<GridBrushBase> userSavedBrushes
  73. {
  74. get => m_UserSavedBrushes;
  75. }
  76. /// <summary>
  77. /// A list of GridBrushBases which represent the user picks made
  78. /// filtered by the current filter type.
  79. /// </summary>
  80. public List<GridBrushBase> filteredUserSavedBrushes
  81. {
  82. get => m_FilteredUserSavedBrushes;
  83. }
  84. private void OnDestroy()
  85. {
  86. foreach (var brush in m_LastSavedBrushes)
  87. {
  88. if (!EditorUtility.IsPersistent(brush))
  89. DestroyImmediate(brush);
  90. }
  91. m_LastSavedBrushes.Clear();
  92. foreach (var brush in m_UserSavedBrushes)
  93. {
  94. if (!EditorUtility.IsPersistent(brush))
  95. DestroyImmediate(brush);
  96. }
  97. m_UserSavedBrushes.Clear();
  98. }
  99. internal int GetIndexOfLastSavedBrush(GridBrushBase brush)
  100. {
  101. return m_LastSavedBrushes.IndexOf(brush);
  102. }
  103. internal int GetIndexOfUserBrush(GridBrushBase brush)
  104. {
  105. return m_UserSavedBrushes.IndexOf(brush);
  106. }
  107. internal int GetIndexOfUserBrushFromFilteredIdx(int filteredIdx)
  108. {
  109. if (filteredIdx < 0 || filteredIdx >= filteredUserSavedBrushes.Count)
  110. return -1;
  111. return GetIndexOfUserBrush(filteredUserSavedBrushes[filteredIdx]);
  112. }
  113. internal bool IsValid()
  114. {
  115. return (lastSavedBrushes.Count == 0 || lastSavedBrushes[0] != null)
  116. && (userSavedBrushes.Count == 0 || userSavedBrushes[0] != null)
  117. && (filteredUserSavedBrushes.Count == 0 || filteredUserSavedBrushes[0] != null);
  118. }
  119. /// <summary>
  120. /// Adds the specified Brush as a new last pick.
  121. /// </summary>
  122. /// <description>
  123. /// This will add a copy of the Brush as a new last pick. If this new copy is over
  124. /// the limit of the number of last picks, the oldest last pick will be erased.
  125. /// The last index of the GridBrushPickStore will be changed to this new addition.
  126. /// </description>
  127. /// <param name="brush">Brush to save as a new last pick.</param>
  128. public void AddNewLastSavedBrush(GridBrushBase brush)
  129. {
  130. if (brush == null)
  131. return;
  132. var clone = Instantiate(brush);
  133. var name = brush.name;
  134. if (String.IsNullOrWhiteSpace(name))
  135. name = brush.GetType().Name;
  136. clone.name = name;
  137. var nextIndex = (lastIndex + 1) % gridBrushPickLastSavedMaxCount;
  138. if (nextIndex < m_LastSavedBrushes.Count)
  139. m_LastSavedBrushes[nextIndex] = clone;
  140. else
  141. m_LastSavedBrushes.Add(clone);
  142. SaveLibraryGridBrushAsset(clone, nextIndex, false);
  143. gridBrushPickLastIndex = nextIndex;
  144. gridBrushPickLastSavedCount = m_LastSavedBrushes.Count;
  145. GridPaintingState.InvokeBrushPickStoreChanged();
  146. }
  147. /// <summary>
  148. /// Clears the Brush at the index of the last pick list.
  149. /// </summary>
  150. /// <description>
  151. /// This will clear the pick at the index, but will not change the size of the
  152. /// pick list.
  153. /// </description>
  154. /// <param name="index">The index of the Brush of the last pick list to clear.</param>
  155. public void ClearLastSavedBrush(int index)
  156. {
  157. if (index < 0 || index >= m_LastSavedBrushes.Count)
  158. return;
  159. m_LastSavedBrushes[index] = null;
  160. FilterBrushes();
  161. SaveGridBrushPickStoreAsset();
  162. var gridBrushPath = GenerateGridBrushInstanceLibraryPath(index, false);
  163. FileUtil.DeleteFileOrDirectory(gridBrushPath);
  164. }
  165. /// <summary>
  166. /// Adds the specified Brush as a new user pick.
  167. /// </summary>
  168. /// <param name="brush">Brush to save as a new user pick.</param>
  169. public void AddNewUserSavedBrush(GridBrushBase brush)
  170. {
  171. if (brush == null)
  172. return;
  173. var clone = Instantiate(brush);
  174. var name = brush.name;
  175. if (String.IsNullOrWhiteSpace(name))
  176. name = brush.GetType().Name;
  177. clone.name = name;
  178. m_UserSavedBrushes.Add(clone);
  179. m_UserSavedCount = m_UserSavedBrushes.Count;
  180. EditorUtility.SetDirty(this);
  181. FilterBrushes();
  182. SaveLibraryGridBrushAsset(clone, m_UserSavedCount - 1, true);
  183. SaveGridBrushPickStoreAsset();
  184. }
  185. /// <summary>
  186. /// Swaps the position of the specified Brush
  187. /// </summary>
  188. /// <param name="oldIdx">Index of the Brush to swap.</param>
  189. /// <param name="newIdx">Index to swap the Brush to.</param>
  190. public void SwapUserSavedBrushes(int oldIdx, int newIdx)
  191. {
  192. if (oldIdx < 0 || oldIdx >= userSavedBrushes.Count)
  193. return;
  194. if (newIdx < 0 || newIdx >= userSavedBrushes.Count)
  195. return;
  196. var brush = userSavedBrushes[oldIdx];
  197. userSavedBrushes.RemoveAt(oldIdx);
  198. userSavedBrushes.Insert(newIdx, brush);
  199. if (AssetDatabase.IsNativeAsset(this))
  200. {
  201. SaveGridBrushPickStoreAsset();
  202. }
  203. else
  204. {
  205. SaveUserSavedBrushFromIndex(oldIdx < newIdx ? oldIdx : newIdx);
  206. }
  207. }
  208. private void SaveUserSavedBrushFromIndex(int index)
  209. {
  210. if (index < 0 || index >= m_UserSavedBrushes.Count)
  211. return;
  212. for (int i = index; i < m_UserSavedCount; ++i)
  213. SaveLibraryGridBrushAsset(m_UserSavedBrushes[i], i, true);
  214. FilterBrushes();
  215. }
  216. /// <summary>
  217. /// Saves over a brush in the user pick with the given index with
  218. /// the specified Brush.
  219. /// </summary>
  220. /// <param name="index">The index of the Brush of the user pick list to save over.</param>
  221. /// <param name="brush">Brush to save over as a user pick.</param>
  222. public void SaveUserSavedBrush(int index, GridBrushBase brush)
  223. {
  224. if (brush == null)
  225. return;
  226. if (index < 0 || index >= m_UserSavedBrushes.Count)
  227. return;
  228. if (m_UserSavedBrushes[index] != brush)
  229. {
  230. var clone = Instantiate(brush);
  231. clone.name = brush.name;
  232. m_UserSavedBrushes[index] = clone;
  233. brush = clone;
  234. }
  235. m_UserSavedCount = m_UserSavedBrushes.Count;
  236. EditorUtility.SetDirty(this);
  237. FilterBrushes();
  238. SaveLibraryGridBrushAsset(brush, index, true);
  239. SaveGridBrushPickStoreAsset();
  240. }
  241. /// <summary>
  242. /// Removes the Brush at the index of the last pick list.
  243. /// </summary>
  244. /// <param name="index">The index of the Brush of the user pick list to remove.</param>
  245. /// <returns>Whether the Brush was removed.</returns>
  246. public bool RemoveUserSavedBrush(int index)
  247. {
  248. if (index < 0 || index >= m_UserSavedBrushes.Count)
  249. return false;
  250. var brush = m_UserSavedBrushes[index];
  251. m_UserSavedBrushes.RemoveAt(index);
  252. m_UserSavedCount = m_UserSavedBrushes.Count;
  253. EditorUtility.SetDirty(this);
  254. FilterBrushes();
  255. SaveGridBrushPickStoreAsset();
  256. if (brush != null && AssetDatabase.IsNativeAsset(brush))
  257. {
  258. AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(brush));
  259. }
  260. else
  261. {
  262. for (int i = index; i < m_UserSavedCount; ++i)
  263. SaveLibraryGridBrushAsset(m_UserSavedBrushes[i], i, true);
  264. }
  265. return true;
  266. }
  267. /// <summary>
  268. /// Sets the type to filter all user brushes by.
  269. /// </summary>
  270. /// <param name="filterType">Type to filter user brushes.</param>
  271. /// <param name="filterText">Text to filter user brush names.</param>
  272. public void SetUserBrushFilterType(Type filterType, string filterText)
  273. {
  274. m_FilteredBrushType = filterType;
  275. m_FilteredBrushText = filterText;
  276. FilterBrushes();
  277. }
  278. private void FilterBrushes()
  279. {
  280. m_FilteredUserSavedBrushes.Clear();
  281. foreach (var brush in m_UserSavedBrushes)
  282. {
  283. var validBrush = brush != null;
  284. var hasFilteredBrushType = m_FilteredBrushType != null;
  285. var hasFilteredBrushText = !String.IsNullOrWhiteSpace(m_FilteredBrushText);
  286. if (hasFilteredBrushType
  287. && validBrush && brush.GetType() != m_FilteredBrushType)
  288. {
  289. continue;
  290. }
  291. if (hasFilteredBrushText
  292. && validBrush
  293. && !Regex.IsMatch(brush.name, m_FilteredBrushText
  294. , RegexOptions.Singleline | RegexOptions.IgnoreCase))
  295. {
  296. continue;
  297. }
  298. // Show brush only if valid or there is no filter for an invalid brush
  299. if (validBrush || (!hasFilteredBrushType && !hasFilteredBrushText))
  300. m_FilteredUserSavedBrushes.Add(brush);
  301. }
  302. }
  303. private void SaveGridBrushPickStoreAsset()
  304. {
  305. if (AssetDatabase.IsNativeAsset(this))
  306. {
  307. EditorUtility.SetDirty(this);
  308. AssetDatabase.SaveAssetIfDirty(this);
  309. }
  310. else
  311. {
  312. var pickPath = GenerateGridBrushPickLibraryPath(s_LibraryAssetName);
  313. var folderPath = Path.GetDirectoryName(pickPath);
  314. if (!Directory.Exists(folderPath))
  315. {
  316. Directory.CreateDirectory(folderPath);
  317. }
  318. InternalEditorUtility.SaveToSerializedFileAndForget(new Object[] { this }, pickPath, EditorSettings.serializationMode != SerializationMode.ForceBinary);
  319. }
  320. GridPaintingState.InvokeBrushPickStoreChanged();
  321. }
  322. internal static GridBrushPickStore LoadOrCreateLibraryGridBrushPickAsset()
  323. {
  324. var pickStore = GridBrushPickStoreSettingsProvider.GetUserBrushPickStore();
  325. if (pickStore == null)
  326. {
  327. // Load library Grid Brush Pick Store
  328. var serializedObjects = InternalEditorUtility.LoadSerializedFileAndForget(GenerateGridBrushPickLibraryPath(s_LibraryAssetName));
  329. if (serializedObjects != null && serializedObjects.Length > 0)
  330. {
  331. pickStore = serializedObjects[0] as GridBrushPickStore;
  332. if (pickStore != null)
  333. {
  334. var count = pickStore.m_UserSavedCount;
  335. var userBrushes = new List<GridBrushBase>();
  336. for (int i = 0; i < count; ++i)
  337. {
  338. var brush = LoadLibraryGridBrushAsset(i, true);
  339. if (brush != null)
  340. {
  341. userBrushes.Add(brush);
  342. }
  343. else
  344. {
  345. userBrushes.Add(null);
  346. }
  347. }
  348. pickStore.m_UserSavedBrushes = userBrushes;
  349. }
  350. }
  351. }
  352. if (pickStore != null)
  353. {
  354. var index = gridBrushPickLastIndex;
  355. var count = gridBrushPickLastSavedCount;
  356. var brushes = new List<GridBrushBase>();
  357. for (int i = 0; i < count; ++i)
  358. {
  359. var brush = LoadLibraryGridBrushAsset(i, false);
  360. if (brush != null)
  361. {
  362. brushes.Add(brush);
  363. }
  364. else if (index >= brushes.Count)
  365. {
  366. index--;
  367. }
  368. }
  369. gridBrushPickLastIndex = index;
  370. gridBrushPickLastSavedCount = brushes.Count;
  371. pickStore.m_LastSavedBrushes = brushes;
  372. pickStore.FilterBrushes();
  373. return pickStore;
  374. }
  375. return CreateLibraryGridBrushPickAsset();
  376. }
  377. private void SaveLibraryGridBrushAsset(GridBrushBase brush, int index, bool user)
  378. {
  379. if (brush == null)
  380. return;
  381. if (user && AssetDatabase.IsNativeAsset(brush))
  382. {
  383. var assetPath = AssetDatabase.GetAssetPath(brush);
  384. var fileName = FileUtil.UnityGetFileNameWithoutExtension(assetPath);
  385. if (fileName != brush.name)
  386. {
  387. var newName = brush.name;
  388. brush.name = fileName;
  389. AssetDatabase.SaveAssetIfDirty(brush);
  390. AssetDatabase.RenameAsset(assetPath, newName);
  391. }
  392. else
  393. {
  394. AssetDatabase.SaveAssetIfDirty(brush);
  395. }
  396. }
  397. else if (user && AssetDatabase.IsNativeAsset(this))
  398. {
  399. var assetPath = AssetDatabase.GetAssetPath(this);
  400. var folderPath = Path.GetDirectoryName(assetPath);
  401. var gridBrushPath = FileUtil.CombinePaths(folderPath, $"{brush.name}{s_AssetExtension}");
  402. gridBrushPath = AssetDatabase.GenerateUniqueAssetPath(gridBrushPath);
  403. AssetDatabase.CreateAsset(brush, gridBrushPath);
  404. }
  405. else
  406. {
  407. var gridBrushPath = GenerateGridBrushInstanceLibraryPath(index, user);
  408. var folderPath = Path.GetDirectoryName(gridBrushPath);
  409. if (!Directory.Exists(folderPath))
  410. {
  411. Directory.CreateDirectory(folderPath);
  412. }
  413. InternalEditorUtility.SaveToSerializedFileAndForget(new Object[] { brush }, gridBrushPath, EditorSettings.serializationMode != SerializationMode.ForceBinary);
  414. }
  415. }
  416. private static GridBrushBase LoadLibraryGridBrushAsset(int index, bool user)
  417. {
  418. var gridBrushPath = GenerateGridBrushInstanceLibraryPath(index, user);
  419. var serializedObjects = InternalEditorUtility.LoadSerializedFileAndForget(gridBrushPath);
  420. if (serializedObjects != null && serializedObjects.Length > 0)
  421. {
  422. var brush = serializedObjects[0] as GridBrushBase;
  423. if (brush != null)
  424. return brush;
  425. }
  426. return null;
  427. }
  428. private static GridBrushPickStore CreateLibraryGridBrushPickAsset()
  429. {
  430. var pickStore = CreateInstance<GridBrushPickStore>();
  431. pickStore.hideFlags = HideFlags.DontSave;
  432. pickStore.name = s_LibraryAssetName;
  433. pickStore.SaveGridBrushPickStoreAsset();
  434. return pickStore;
  435. }
  436. private static string GenerateGridBrushPickLibraryPath(string name)
  437. {
  438. var path = FileUtil.CombinePaths(s_LibraryPickPath, name + s_AssetExtension);
  439. path = FileUtil.NiceWinPath(path);
  440. return path;
  441. }
  442. private static string GenerateGridBrushInstanceLibraryPath(int index, bool user)
  443. {
  444. var path = FileUtil.CombinePaths(user ? s_UserPickPath : s_LastPickPath, index.ToString() + s_AssetExtension);
  445. path = FileUtil.NiceWinPath(path);
  446. return path;
  447. }
  448. }
  449. }