No Description
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.

RuleOverrideTileEditor.cs 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using UnityEngine;
  2. using UnityEngine.Tilemaps;
  3. using UnityEditorInternal;
  4. using System.Collections.Generic;
  5. namespace UnityEditor
  6. {
  7. /// <summary>
  8. /// The Editor for a RuleOverrideTileEditor.
  9. /// </summary>
  10. [CustomEditor(typeof(RuleOverrideTile))]
  11. public class RuleOverrideTileEditor : Editor
  12. {
  13. private static class Styles
  14. {
  15. public static readonly GUIContent overrideTile = EditorGUIUtility.TrTextContent("Tile"
  16. , "The Rule Tile to override.");
  17. }
  18. /// <summary>
  19. /// The RuleOverrideTile being edited
  20. /// </summary>
  21. public RuleOverrideTile overrideTile => target as RuleOverrideTile;
  22. /// <summary>
  23. /// The RuleTileEditor for the overridden instance of the RuleTile
  24. /// </summary>
  25. public RuleTileEditor ruleTileEditor
  26. {
  27. get
  28. {
  29. if (m_RuleTileEditorTarget != overrideTile.m_Tile)
  30. {
  31. DestroyImmediate(m_RuleTileEditor);
  32. m_RuleTileEditor = Editor.CreateEditor(overrideTile.m_InstanceTile) as RuleTileEditor;
  33. m_RuleTileEditorTarget = overrideTile.m_Tile;
  34. }
  35. return m_RuleTileEditor;
  36. }
  37. }
  38. RuleTileEditor m_RuleTileEditor;
  39. RuleTile m_RuleTileEditorTarget;
  40. /// <summary>
  41. /// List of Sprites and overriding Sprites
  42. /// </summary>
  43. public List<KeyValuePair<Sprite, Sprite>> m_Sprites = new List<KeyValuePair<Sprite, Sprite>>();
  44. /// <summary>
  45. /// List of GameObjects and overriding GameObjects
  46. /// </summary>
  47. public List<KeyValuePair<GameObject, GameObject>> m_GameObjects = new List<KeyValuePair<GameObject, GameObject>>();
  48. private ReorderableList m_SpriteList;
  49. private ReorderableList m_GameObjectList;
  50. private int m_MissingOriginalSpriteIndex;
  51. private int m_MissingOriginalGameObjectIndex;
  52. /// <summary>
  53. /// Height for a Sprite Element
  54. /// </summary>
  55. public static float k_SpriteElementHeight = 48;
  56. /// <summary>
  57. /// Height for a GameObject Element
  58. /// </summary>
  59. public static float k_GameObjectElementHeight = 16;
  60. /// <summary>
  61. /// Padding between Rule Elements
  62. /// </summary>
  63. public static float k_PaddingBetweenRules = 4;
  64. /// <summary>
  65. /// OnEnable for the RuleOverrideTileEditor
  66. /// </summary>
  67. public virtual void OnEnable()
  68. {
  69. if (m_SpriteList == null)
  70. {
  71. m_SpriteList = new ReorderableList(m_Sprites, typeof(KeyValuePair<Sprite, Sprite>), false, true, false, false);
  72. m_SpriteList.drawHeaderCallback = DrawSpriteListHeader;
  73. m_SpriteList.drawElementCallback = DrawSpriteElement;
  74. m_SpriteList.elementHeightCallback = GetSpriteElementHeight;
  75. }
  76. if (m_GameObjectList == null)
  77. {
  78. m_GameObjectList = new ReorderableList(m_GameObjects, typeof(KeyValuePair<Sprite, Sprite>), false, true, false, false);
  79. m_GameObjectList.drawHeaderCallback = DrawGameObjectListHeader;
  80. m_GameObjectList.drawElementCallback = DrawGameObjectElement;
  81. m_GameObjectList.elementHeightCallback = GetGameObjectElementHeight;
  82. }
  83. }
  84. /// <summary>
  85. /// OnDisable for the RuleOverrideTileEditor
  86. /// </summary>
  87. public virtual void OnDisable()
  88. {
  89. DestroyImmediate(ruleTileEditor);
  90. m_RuleTileEditorTarget = null;
  91. }
  92. /// <summary>
  93. /// Draws the Inspector GUI for the RuleOverrideTileEditor
  94. /// </summary>
  95. public override void OnInspectorGUI()
  96. {
  97. serializedObject.UpdateIfRequiredOrScript();
  98. DrawTileField();
  99. DrawCustomFields();
  100. overrideTile.GetOverrides(m_Sprites, ref m_MissingOriginalSpriteIndex);
  101. overrideTile.GetOverrides(m_GameObjects, ref m_MissingOriginalGameObjectIndex);
  102. EditorGUI.BeginChangeCheck();
  103. m_SpriteList.DoLayoutList();
  104. if (EditorGUI.EndChangeCheck())
  105. {
  106. overrideTile.ApplyOverrides(m_Sprites);
  107. SaveTile();
  108. }
  109. EditorGUI.BeginChangeCheck();
  110. m_GameObjectList.DoLayoutList();
  111. if (EditorGUI.EndChangeCheck())
  112. {
  113. overrideTile.ApplyOverrides(m_GameObjects);
  114. SaveTile();
  115. }
  116. }
  117. /// <summary>
  118. /// Draws the header for the Sprite list
  119. /// </summary>
  120. /// <param name="rect">GUI Rect to draw the header at</param>
  121. public void DrawSpriteListHeader(Rect rect)
  122. {
  123. float xMax = rect.xMax;
  124. rect.xMax = rect.xMax / 2.0f;
  125. GUI.Label(rect, "Original Sprite", EditorStyles.label);
  126. rect.xMin = rect.xMax;
  127. rect.xMax = xMax;
  128. GUI.Label(rect, "Override Sprite", EditorStyles.label);
  129. }
  130. /// <summary>
  131. /// Draws the header for the GameObject list
  132. /// </summary>
  133. /// <param name="rect">GUI Rect to draw the header at</param>
  134. public void DrawGameObjectListHeader(Rect rect)
  135. {
  136. float xMax = rect.xMax;
  137. rect.xMax = rect.xMax / 2.0f;
  138. GUI.Label(rect, "Original GameObject", EditorStyles.label);
  139. rect.xMin = rect.xMax;
  140. rect.xMax = xMax;
  141. GUI.Label(rect, "Override GameObject", EditorStyles.label);
  142. }
  143. /// <summary>
  144. /// Gets the GUI element height for a Sprite element with the given index
  145. /// </summary>
  146. /// <param name="index">Index of the Sprite element</param>
  147. /// <returns>GUI element height for the Sprite element</returns>
  148. public float GetSpriteElementHeight(int index)
  149. {
  150. float height = k_SpriteElementHeight + k_PaddingBetweenRules;
  151. bool isMissing = index >= m_MissingOriginalSpriteIndex;
  152. if (isMissing)
  153. height += 16;
  154. return height;
  155. }
  156. /// <summary>
  157. /// Gets the GUI element height for a GameObject element with the given index
  158. /// </summary>
  159. /// <param name="index">Index of the GameObject element</param>
  160. /// <returns>GUI element height for the GameObject element</returns>
  161. public float GetGameObjectElementHeight(int index)
  162. {
  163. float height = k_GameObjectElementHeight + k_PaddingBetweenRules;
  164. bool isMissing = index >= m_MissingOriginalGameObjectIndex;
  165. if (isMissing)
  166. height += 16;
  167. return height;
  168. }
  169. /// <summary>
  170. /// Draws the Sprite element for the RuleOverride list
  171. /// </summary>
  172. /// <param name="rect">Rect to draw the Sprite Element in</param>
  173. /// <param name="index">Index of the Sprite Element to draw</param>
  174. /// <param name="active">Whether the Sprite Element is active</param>
  175. /// <param name="focused">Whether the Sprite Element is focused</param>
  176. public void DrawSpriteElement(Rect rect, int index, bool active, bool focused)
  177. {
  178. bool isMissing = index >= m_MissingOriginalSpriteIndex;
  179. if (isMissing)
  180. {
  181. EditorGUI.HelpBox(new Rect(rect.xMin, rect.yMin, rect.width, 16), "Original Sprite missing", MessageType.Warning);
  182. rect.yMin += 16;
  183. }
  184. Sprite originalSprite = m_Sprites[index].Key;
  185. Sprite overrideSprite = m_Sprites[index].Value;
  186. rect.y += 2;
  187. rect.height -= k_PaddingBetweenRules;
  188. rect.xMax = rect.xMax / 2.0f;
  189. using (new EditorGUI.DisabledScope(true))
  190. EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.height, rect.height), originalSprite, typeof(Sprite), false);
  191. rect.xMin = rect.xMax;
  192. rect.xMax *= 2.0f;
  193. EditorGUI.BeginChangeCheck();
  194. overrideSprite = EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.height, rect.height), overrideSprite, typeof(Sprite), false) as Sprite;
  195. if (EditorGUI.EndChangeCheck())
  196. m_Sprites[index] = new KeyValuePair<Sprite, Sprite>(originalSprite, overrideSprite);
  197. }
  198. /// <summary>
  199. /// Draws the GameObject element for the RuleOverride list
  200. /// </summary>
  201. /// <param name="rect">Rect to draw the GameObject Element in</param>
  202. /// <param name="index">Index of the GameObject Element to draw</param>
  203. /// <param name="active">Whether the GameObject Element is active</param>
  204. /// <param name="focused">Whether the GameObject Element is focused</param>
  205. public void DrawGameObjectElement(Rect rect, int index, bool active, bool focused)
  206. {
  207. bool isMissing = index >= m_MissingOriginalGameObjectIndex;
  208. if (isMissing)
  209. {
  210. EditorGUI.HelpBox(new Rect(rect.xMin, rect.yMin, rect.width, 16), "Original GameObject missing", MessageType.Warning);
  211. rect.yMin += 16;
  212. }
  213. GameObject originalGameObject = m_GameObjects[index].Key;
  214. GameObject overrideGameObject = m_GameObjects[index].Value;
  215. rect.y += 2;
  216. rect.height -= k_PaddingBetweenRules;
  217. rect.xMax = rect.xMax / 2.0f;
  218. using (new EditorGUI.DisabledScope(true))
  219. EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.width, rect.height), originalGameObject, typeof(GameObject), false);
  220. rect.xMin = rect.xMax;
  221. rect.xMax *= 2.0f;
  222. EditorGUI.BeginChangeCheck();
  223. overrideGameObject = EditorGUI.ObjectField(new Rect(rect.xMin, rect.yMin, rect.width, rect.height), overrideGameObject, typeof(GameObject), false) as GameObject;
  224. if (EditorGUI.EndChangeCheck())
  225. m_GameObjects[index] = new KeyValuePair<GameObject, GameObject>(originalGameObject, overrideGameObject);
  226. }
  227. /// <summary>
  228. /// Draws a field for the RuleTile be overridden
  229. /// </summary>
  230. public void DrawTileField()
  231. {
  232. EditorGUI.BeginChangeCheck();
  233. RuleTile tile = EditorGUILayout.ObjectField(Styles.overrideTile, overrideTile.m_Tile, typeof(RuleTile), false) as RuleTile;
  234. if (EditorGUI.EndChangeCheck())
  235. {
  236. if (!LoopCheck(tile))
  237. {
  238. overrideTile.m_Tile = tile;
  239. SaveTile();
  240. }
  241. else
  242. {
  243. Debug.LogWarning("Circular tile reference");
  244. }
  245. }
  246. bool LoopCheck(RuleTile checkTile)
  247. {
  248. if (!overrideTile.m_InstanceTile)
  249. return false;
  250. HashSet<RuleTile> renferenceTils = new HashSet<RuleTile>();
  251. Add(overrideTile.m_InstanceTile);
  252. return renferenceTils.Contains(checkTile);
  253. void Add(RuleTile ruleTile)
  254. {
  255. if (renferenceTils.Contains(ruleTile))
  256. return;
  257. renferenceTils.Add(ruleTile);
  258. var overrideTiles = RuleTileEditor.FindAffectedOverrideTiles(ruleTile);
  259. foreach (var overrideTile in overrideTiles)
  260. Add(overrideTile.m_InstanceTile);
  261. }
  262. }
  263. }
  264. /// <summary>
  265. /// Draw editor fields for custom properties for the RuleOverrideTile
  266. /// </summary>
  267. public void DrawCustomFields()
  268. {
  269. if (ruleTileEditor)
  270. {
  271. ruleTileEditor.target.hideFlags = HideFlags.None;
  272. ruleTileEditor.DrawCustomFields(true);
  273. ruleTileEditor.target.hideFlags = HideFlags.NotEditable;
  274. }
  275. }
  276. private void SaveInstanceTileAsset()
  277. {
  278. bool assetChanged = false;
  279. if (overrideTile.m_InstanceTile)
  280. {
  281. if (!overrideTile.m_Tile || overrideTile.m_InstanceTile.GetType() != overrideTile.m_Tile.GetType())
  282. {
  283. DestroyImmediate(overrideTile.m_InstanceTile, true);
  284. overrideTile.m_InstanceTile = null;
  285. assetChanged = true;
  286. }
  287. }
  288. if (!overrideTile.m_InstanceTile)
  289. {
  290. if (overrideTile.m_Tile)
  291. {
  292. var t = overrideTile.m_Tile.GetType();
  293. RuleTile instanceTile = ScriptableObject.CreateInstance(t) as RuleTile;
  294. instanceTile.hideFlags = HideFlags.NotEditable;
  295. AssetDatabase.AddObjectToAsset(instanceTile, overrideTile);
  296. overrideTile.m_InstanceTile = instanceTile;
  297. assetChanged = true;
  298. }
  299. }
  300. if (overrideTile.m_InstanceTile)
  301. {
  302. string instanceTileName = overrideTile.m_Tile.name + " (Override)";
  303. if (overrideTile.m_InstanceTile.name != instanceTileName)
  304. {
  305. overrideTile.m_InstanceTile.name = instanceTileName;
  306. assetChanged = true;
  307. }
  308. }
  309. if (assetChanged)
  310. {
  311. EditorUtility.SetDirty(overrideTile.m_InstanceTile);
  312. AssetDatabase.SaveAssetIfDirty(overrideTile.m_InstanceTile);
  313. }
  314. }
  315. /// <summary>
  316. /// Saves any changes to the RuleOverrideTile
  317. /// </summary>
  318. public void SaveTile()
  319. {
  320. EditorUtility.SetDirty(target);
  321. SceneView.RepaintAll();
  322. SaveInstanceTileAsset();
  323. if (overrideTile.m_InstanceTile)
  324. {
  325. overrideTile.Override();
  326. RuleTileEditor.UpdateAffectedOverrideTiles(overrideTile.m_InstanceTile);
  327. }
  328. if (ruleTileEditor && ruleTileEditor.m_PreviewTilemaps != null)
  329. {
  330. foreach (var tilemap in ruleTileEditor.m_PreviewTilemaps)
  331. tilemap.RefreshAllTiles();
  332. }
  333. }
  334. /// <summary>
  335. /// Renders a static preview Texture2D for a RuleOverrideTile asset
  336. /// </summary>
  337. /// <param name="assetPath">Asset path of the RuleOverrideTile</param>
  338. /// <param name="subAssets">Arrays of assets from the given Asset path</param>
  339. /// <param name="width">Width of the static preview</param>
  340. /// <param name="height">Height of the static preview </param>
  341. /// <returns>Texture2D containing static preview for the RuleOverrideTile asset</returns>
  342. public override Texture2D RenderStaticPreview(string assetPath, Object[] subAssets, int width, int height)
  343. {
  344. if (ruleTileEditor)
  345. return ruleTileEditor.RenderStaticPreview(assetPath, subAssets, width, height);
  346. return base.RenderStaticPreview(assetPath, subAssets, width, height);
  347. }
  348. /// <summary>
  349. /// Whether the RuleOverrideTile has a preview GUI
  350. /// </summary>
  351. /// <returns>True if RuleOverrideTile has a preview GUI. False if not.</returns>
  352. public override bool HasPreviewGUI()
  353. {
  354. if (ruleTileEditor)
  355. return ruleTileEditor.HasPreviewGUI();
  356. return false;
  357. }
  358. /// <summary>
  359. /// Updates preview settings for the RuleOverrideTile.
  360. /// </summary>
  361. public override void OnPreviewSettings()
  362. {
  363. if (ruleTileEditor)
  364. ruleTileEditor.OnPreviewSettings();
  365. }
  366. /// <summary>
  367. /// Draws the preview GUI for the RuleTile
  368. /// </summary>
  369. /// <param name="rect">Rect to draw the preview GUI</param>
  370. /// <param name="background">The GUIStyle of the background for the preview</param>
  371. public override void OnPreviewGUI(Rect rect, GUIStyle background)
  372. {
  373. if (ruleTileEditor)
  374. ruleTileEditor.OnPreviewGUI(rect, background);
  375. }
  376. }
  377. }