Ingen beskrivning
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.

GridPaintingState.cs 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using UnityEditor.EditorTools;
  6. using UnityEngine;
  7. using Object = UnityEngine.Object;
  8. namespace UnityEditor.Tilemaps
  9. {
  10. /// <summary>
  11. /// GridPaintingState controls the state of objects for painting with a Tile Palette.
  12. /// </summary>
  13. /// <remarks>
  14. /// Utilize this class to get and set the current painting target and brush for painting
  15. /// with the Tile Palette.
  16. /// </remarks>
  17. public class GridPaintingState : ScriptableSingleton<GridPaintingState>
  18. {
  19. [SerializeField] private GameObject m_EditModeScenePaintTarget; // Which GameObject in scene was the last painting target in EditMode
  20. [SerializeField] private GameObject m_ScenePaintTarget; // Which GameObject in scene is considered as painting target
  21. [SerializeField] private EditorTool[] m_BrushTools;
  22. [SerializeField] private GridBrushBase m_Brush; // Which brush will handle painting callbacks
  23. [SerializeField] private PaintableGrid m_ActiveGrid; // Grid that has painting focus (can be palette, too)
  24. [SerializeField] private PaintableGrid m_LastActiveGrid; // Grid that last had painting focus (can be palette, too)
  25. [SerializeField] private HashSet<Object> m_InterestedPainters = new HashSet<Object>(); // A list of objects that can paint using the GridPaintingState
  26. private GameObject[] m_CachedPaintTargets;
  27. private bool m_FlushPaintTargetCache;
  28. private Editor m_CachedEditor;
  29. private bool m_SavingPalette;
  30. private float m_BrushToolbarSize;
  31. /// <summary>
  32. /// Callback when the Tile Palette's active target has changed
  33. /// </summary>
  34. public static event Action<GameObject> scenePaintTargetChanged;
  35. /// <summary>
  36. /// Callback when the Tile Palette's active brush has changed.
  37. /// </summary>
  38. public static event Action<GridBrushBase> brushChanged;
  39. /// <summary>
  40. /// Callback when the Tile Palette's active palette GameObject has changed.
  41. /// </summary>
  42. public static event Action<GameObject> paletteChanged;
  43. private void OnEnable()
  44. {
  45. EditorApplication.hierarchyChanged += HierarchyChanged;
  46. EditorApplication.playModeStateChanged += PlayModeStateChanged;
  47. Selection.selectionChanged += OnSelectionChange;
  48. m_FlushPaintTargetCache = true;
  49. }
  50. private void OnDisable()
  51. {
  52. m_InterestedPainters.Clear();
  53. EditorApplication.hierarchyChanged -= HierarchyChanged;
  54. EditorApplication.playModeStateChanged -= PlayModeStateChanged;
  55. Selection.selectionChanged -= OnSelectionChange;
  56. FlushCache();
  57. }
  58. private void OnSelectionChange()
  59. {
  60. if (hasInterestedPainters && ValidatePaintTarget(Selection.activeGameObject))
  61. {
  62. scenePaintTarget = Selection.activeGameObject;
  63. }
  64. }
  65. private void PlayModeStateChanged(PlayModeStateChange state)
  66. {
  67. if (state == PlayModeStateChange.ExitingEditMode)
  68. {
  69. m_EditModeScenePaintTarget = scenePaintTarget;
  70. }
  71. else if (state == PlayModeStateChange.EnteredEditMode)
  72. {
  73. if (GridPaintActiveTargetsPreferences.restoreEditModeSelection && m_EditModeScenePaintTarget != null)
  74. {
  75. scenePaintTarget = m_EditModeScenePaintTarget;
  76. }
  77. }
  78. }
  79. private void HierarchyChanged()
  80. {
  81. if (hasInterestedPainters)
  82. {
  83. m_FlushPaintTargetCache = true;
  84. if (validTargets == null || validTargets.Length == 0 || !validTargets.Contains(scenePaintTarget))
  85. {
  86. // case 1102618: Try to use current Selection as scene paint target if possible
  87. if (Selection.activeGameObject != null && hasInterestedPainters && ValidatePaintTarget(Selection.activeGameObject))
  88. {
  89. scenePaintTarget = Selection.activeGameObject;
  90. }
  91. else
  92. {
  93. AutoSelectPaintTarget();
  94. }
  95. }
  96. }
  97. }
  98. private GameObject[] GetValidTargets()
  99. {
  100. if (m_FlushPaintTargetCache)
  101. {
  102. m_CachedPaintTargets = null;
  103. if (activeBrushEditor != null)
  104. m_CachedPaintTargets = activeBrushEditor.validTargets;
  105. if (m_CachedPaintTargets == null || m_CachedPaintTargets.Length == 0)
  106. scenePaintTarget = null;
  107. else
  108. {
  109. var comparer = GridPaintActiveTargetsPreferences.GetTargetComparer();
  110. if (comparer != null)
  111. Array.Sort(m_CachedPaintTargets, comparer);
  112. }
  113. m_FlushPaintTargetCache = false;
  114. }
  115. return m_CachedPaintTargets;
  116. }
  117. internal static void AutoSelectPaintTarget()
  118. {
  119. if (activeBrushEditor != null)
  120. {
  121. if (validTargets != null && validTargets.Length > 0)
  122. {
  123. scenePaintTarget = validTargets[0];
  124. }
  125. }
  126. }
  127. /// <summary>
  128. /// The currently active target for the Tile Palette
  129. /// </summary>
  130. public static GameObject scenePaintTarget
  131. {
  132. get { return instance.m_ScenePaintTarget; }
  133. set
  134. {
  135. if (value != instance.m_ScenePaintTarget)
  136. {
  137. instance.m_ScenePaintTarget = value;
  138. if (scenePaintTargetChanged != null)
  139. scenePaintTargetChanged(instance.m_ScenePaintTarget);
  140. RepaintGridPaintPaletteWindow();
  141. }
  142. }
  143. }
  144. /// <summary>
  145. /// The currently active brush for the Tile Palette
  146. /// </summary>
  147. public static GridBrushBase gridBrush
  148. {
  149. get
  150. {
  151. if (instance.m_Brush == null)
  152. {
  153. instance.m_Brush = GridPaletteBrushes.instance.GetLastUsedBrush();
  154. UpdateBrushToolbar();
  155. }
  156. return instance.m_Brush;
  157. }
  158. set
  159. {
  160. if (instance.m_Brush != value)
  161. {
  162. instance.m_Brush = value;
  163. instance.m_FlushPaintTargetCache = true;
  164. if (value != null)
  165. {
  166. GridPaletteBrushes.instance.StoreLastUsedBrush(value);
  167. UpdateBrushToolbar();
  168. }
  169. // Ensure that current scenePaintTarget is still a valid target after a brush change
  170. if (scenePaintTarget != null && !ValidatePaintTarget(scenePaintTarget))
  171. scenePaintTarget = null;
  172. // Use Selection if previous scenePaintTarget was not valid
  173. if (scenePaintTarget == null)
  174. scenePaintTarget = ValidatePaintTarget(Selection.activeGameObject) ? Selection.activeGameObject : null;
  175. // Auto select a valid target if there is still no scenePaintTarget
  176. if (scenePaintTarget == null)
  177. AutoSelectPaintTarget();
  178. if (null != brushChanged)
  179. brushChanged(value);
  180. RepaintGridPaintPaletteWindow();
  181. }
  182. }
  183. }
  184. /// <summary>
  185. /// Returns all available brushes for the Tile Palette
  186. /// </summary>
  187. public static IList<GridBrushBase> brushes
  188. {
  189. get { return GridPaletteBrushes.brushes; }
  190. }
  191. internal static GridBrush defaultBrush
  192. {
  193. get { return gridBrush as GridBrush; }
  194. set { gridBrush = value; }
  195. }
  196. /// <summary>
  197. /// The currently active palette GameObject for the Tile Palette
  198. /// </summary>
  199. public static GameObject palette
  200. {
  201. get
  202. {
  203. if (GridPaintPaletteWindow.instances.Count > 0)
  204. return GridPaintPaletteWindow.instances[0].palette;
  205. return null;
  206. }
  207. set
  208. {
  209. if (value == null || !GridPalettes.palettes.Contains(value))
  210. throw new ArgumentException(L10n.Tr("Unable to set invalid palette"));
  211. if (GridPaintPaletteWindow.instances.Count > 0 && GridPaintPaletteWindow.instances[0].palette != value)
  212. {
  213. GridPaintPaletteWindow.instances[0].palette = value;
  214. }
  215. }
  216. }
  217. /// <summary>
  218. /// Checks if target GameObject is part of the active Palette.
  219. /// </summary>
  220. /// <param name="target">GameObject to check.</param>
  221. /// <returns>True if the target GameObject is part of the active palette. False if not.</returns>
  222. public static bool IsPartOfActivePalette(GameObject target)
  223. {
  224. if (GridPaintPaletteWindow.instances.Count > 0 && target == GridPaintPaletteWindow.instances[0].paletteInstance)
  225. return true;
  226. if (target == palette)
  227. return true;
  228. var parent = target.transform.parent;
  229. return parent != null && IsPartOfActivePalette(parent.gameObject);
  230. }
  231. /// <summary>
  232. /// Returns all available Palette GameObjects for the Tile Palette
  233. /// </summary>
  234. public static IList<GameObject> palettes
  235. {
  236. get { return GridPalettes.palettes; }
  237. }
  238. /// <summary>
  239. /// The currently active editor for the active brush for the Tile Palette
  240. /// </summary>
  241. public static GridBrushEditorBase activeBrushEditor
  242. {
  243. get
  244. {
  245. Editor.CreateCachedEditor(gridBrush, null, ref instance.m_CachedEditor);
  246. GridBrushEditorBase baseEditor = instance.m_CachedEditor as GridBrushEditorBase;
  247. return baseEditor;
  248. }
  249. }
  250. internal static Editor fallbackEditor
  251. {
  252. get
  253. {
  254. Editor.CreateCachedEditor(gridBrush, null, ref instance.m_CachedEditor);
  255. return instance.m_CachedEditor;
  256. }
  257. }
  258. internal static PaintableGrid activeGrid
  259. {
  260. get { return instance.m_ActiveGrid; }
  261. set
  262. {
  263. instance.m_ActiveGrid = value;
  264. if (instance.m_ActiveGrid != null)
  265. instance.m_LastActiveGrid = value;
  266. }
  267. }
  268. internal static PaintableGrid lastActiveGrid
  269. {
  270. get { return instance.m_LastActiveGrid; }
  271. }
  272. internal static EditorTool[] activeBrushTools
  273. {
  274. get { return instance.m_BrushTools; }
  275. set { instance.m_BrushTools = value; }
  276. }
  277. internal static float activeBrushToolbarSize
  278. {
  279. get
  280. {
  281. if (instance.m_BrushToolbarSize == 0.0f)
  282. CalculateToolbarSize();
  283. return instance.m_BrushToolbarSize;
  284. }
  285. set { instance.m_BrushToolbarSize = value; }
  286. }
  287. private static void CalculateToolbarSize()
  288. {
  289. GUIStyle toolbarStyle = "Command";
  290. activeBrushToolbarSize = activeBrushTools.Sum(x => toolbarStyle.CalcSize(x.toolbarIcon).x);
  291. }
  292. internal static void SetBrushTools(EditorTool[] editorTools)
  293. {
  294. activeBrushTools = editorTools;
  295. activeBrushToolbarSize = 0.0f;
  296. }
  297. private static bool ValidatePaintTarget(GameObject candidate)
  298. {
  299. if (candidate == null)
  300. return false;
  301. // Case 1327021: Do not allow disabled GameObjects as a paint target
  302. if (!candidate.activeInHierarchy)
  303. return false;
  304. if (candidate.GetComponentInParent<Grid>() == null && candidate.GetComponent<Grid>() == null)
  305. return false;
  306. if (validTargets != null && validTargets.Length > 0 && !validTargets.Contains(candidate))
  307. return false;
  308. if (PrefabUtility.IsPartOfPrefabAsset(candidate))
  309. return false;
  310. return true;
  311. }
  312. internal static void FlushCache()
  313. {
  314. if (instance.m_CachedEditor != null)
  315. {
  316. DestroyImmediate(instance.m_CachedEditor);
  317. instance.m_CachedEditor = null;
  318. }
  319. instance.m_FlushPaintTargetCache = true;
  320. }
  321. /// <summary>
  322. /// A list of all valid targets that can be set as an active target for the Tile Palette
  323. /// </summary>
  324. public static GameObject[] validTargets
  325. {
  326. get { return instance.GetValidTargets(); }
  327. }
  328. internal static bool savingPalette
  329. {
  330. get { return instance.m_SavingPalette; }
  331. set { instance.m_SavingPalette = value; }
  332. }
  333. internal static void OnPaletteChanged(GameObject palette)
  334. {
  335. if (null != paletteChanged)
  336. paletteChanged(palette);
  337. }
  338. internal static void UpdateBrushToolbar()
  339. {
  340. BrushToolsAttribute toolAttribute = null;
  341. if (instance.m_Brush != null)
  342. toolAttribute = (BrushToolsAttribute)instance.m_Brush.GetType().GetCustomAttribute(typeof(BrushToolsAttribute), false);
  343. TilemapEditorTool.UpdateEditorTools(toolAttribute);
  344. }
  345. internal static void UpdateActiveGridPalette()
  346. {
  347. if (GridPaintPaletteWindow.instances.Count > 0)
  348. GridPaintPaletteWindow.instances[0].DelayedResetPreviewInstance();
  349. }
  350. internal static void RepaintGridPaintPaletteWindow()
  351. {
  352. if (GridPaintPaletteWindow.instances.Count > 0)
  353. GridPaintPaletteWindow.instances[0].Repaint();
  354. }
  355. internal static void UnlockGridPaintPaletteClipboardForEditing()
  356. {
  357. if (GridPaintPaletteWindow.instances.Count > 0)
  358. GridPaintPaletteWindow.instances[0].clipboardView.UnlockAndEdit();
  359. }
  360. internal static void RegisterPainterInterest(Object painter)
  361. {
  362. instance.m_InterestedPainters.Add(painter);
  363. }
  364. internal static void UnregisterPainterInterest(Object painter)
  365. {
  366. instance.m_InterestedPainters.Remove(painter);
  367. }
  368. private bool hasInterestedPainters
  369. {
  370. get { return m_InterestedPainters.Count > 0; }
  371. }
  372. }
  373. }