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.

AnimatedTile.cs 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. using System;
  2. #if UNITY_EDITOR
  3. using UnityEditor;
  4. using UnityEditorInternal;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. #endif
  8. namespace UnityEngine.Tilemaps
  9. {
  10. /// <summary>
  11. /// Animated Tiles are tiles which run through and display a list of sprites in sequence.
  12. /// </summary>
  13. [Serializable]
  14. [HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/AnimatedTile.html")]
  15. public class AnimatedTile : TileBase
  16. {
  17. /// <summary>
  18. /// The List of Sprites set for the Animated Tile.
  19. /// This will be played in sequence.
  20. /// </summary>
  21. public Sprite[] m_AnimatedSprites;
  22. /// <summary>
  23. /// The minimum possible speed at which the Animation of the Tile will be played.
  24. /// A speed value will be randomly chosen between the minimum and maximum speed.
  25. /// </summary>
  26. public float m_MinSpeed = 1f;
  27. /// <summary>
  28. /// The maximum possible speed at which the Animation of the Tile will be played.
  29. /// A speed value will be randomly chosen between the minimum and maximum speed.
  30. /// </summary>
  31. public float m_MaxSpeed = 1f;
  32. /// <summary>
  33. /// The starting time of this Animated Tile.
  34. /// This allows you to start the Animation from time in the list of Animated Sprites depending on the
  35. /// Tilemap's Animation Frame Rate.
  36. /// </summary>
  37. public float m_AnimationStartTime;
  38. /// <summary>
  39. /// The starting frame of this Animated Tile.
  40. /// This allows you to start the Animation from a particular Sprite in the list of Animated Sprites.
  41. /// If this is set, this overrides m_AnimationStartTime.
  42. /// </summary>
  43. public int m_AnimationStartFrame = 0;
  44. /// <summary>
  45. /// The Collider Shape generated by the Tile.
  46. /// </summary>
  47. public Tile.ColliderType m_TileColliderType;
  48. /// <summary>
  49. /// Retrieves any tile rendering data from the scripted tile.
  50. /// </summary>
  51. /// <param name="position">Position of the Tile on the Tilemap.</param>
  52. /// <param name="tilemap">The Tilemap the tile is present on.</param>
  53. /// <param name="tileData">Data to render the tile.</param>
  54. public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
  55. {
  56. tileData.transform = Matrix4x4.identity;
  57. tileData.color = Color.white;
  58. if (m_AnimatedSprites != null && m_AnimatedSprites.Length > 0)
  59. {
  60. tileData.sprite = m_AnimatedSprites[m_AnimatedSprites.Length - 1];
  61. tileData.colliderType = m_TileColliderType;
  62. }
  63. }
  64. /// <summary>
  65. /// Retrieves any tile animation data from the scripted tile.
  66. /// </summary>
  67. /// <param name="position">Position of the Tile on the Tilemap.</param>
  68. /// <param name="tilemap">The Tilemap the tile is present on.</param>
  69. /// <param name="tileAnimationData">Data to run an animation on the tile.</param>
  70. /// <returns>Whether the call was successful.</returns>
  71. public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
  72. {
  73. if (m_AnimatedSprites.Length > 0)
  74. {
  75. tileAnimationData.animatedSprites = m_AnimatedSprites;
  76. tileAnimationData.animationSpeed = Random.Range(m_MinSpeed, m_MaxSpeed);
  77. tileAnimationData.animationStartTime = m_AnimationStartTime;
  78. if (0 < m_AnimationStartFrame && m_AnimationStartFrame <= m_AnimatedSprites.Length)
  79. {
  80. var tilemapComponent = tilemap.GetComponent<Tilemap>();
  81. if (tilemapComponent != null && tilemapComponent.animationFrameRate > 0)
  82. tileAnimationData.animationStartTime = (m_AnimationStartFrame - 1) / tilemapComponent.animationFrameRate;
  83. }
  84. return true;
  85. }
  86. return false;
  87. }
  88. }
  89. #if UNITY_EDITOR
  90. [CustomEditor(typeof(AnimatedTile))]
  91. public class AnimatedTileEditor : Editor
  92. {
  93. private static class Styles
  94. {
  95. public static readonly GUIContent orderAnimatedTileSpritesInfo =
  96. EditorGUIUtility.TrTextContent("Place sprites shown based on the order of animation.");
  97. public static readonly GUIContent emptyAnimatedTileInfo =
  98. EditorGUIUtility.TrTextContent(
  99. "Drag Sprite or Sprite Texture assets \n" +
  100. " to start creating an Animated Tile.");
  101. public static readonly GUIContent minimumSpeedLabel = EditorGUIUtility.TrTextContent("Minimum Speed",
  102. "The minimum possible speed at which the Animation of the Tile will be played. A speed value will be randomly chosen between the minimum and maximum speed.");
  103. public static readonly GUIContent maximumSpeedLabel = EditorGUIUtility.TrTextContent("Maximum Speed",
  104. "The maximum possible speed at which the Animation of the Tile will be played. A speed value will be randomly chosen between the minimum and maximum speed.");
  105. public static readonly GUIContent startTimeLabel = EditorGUIUtility.TrTextContent("Start Time", "The starting time of this Animated Tile. This allows you to start the Animation from a particular time.");
  106. public static readonly GUIContent startFrameLabel = EditorGUIUtility.TrTextContent("Start Frame", "The starting frame of this Animated Tile. This allows you to start the Animation from a particular Sprite in the list of Animated Sprites.");
  107. public static readonly GUIContent colliderTypeLabel = EditorGUIUtility.TrTextContent("Collider Type", "The Collider Shape generated by the Tile.");
  108. }
  109. private static readonly string k_UndoName = L10n.Tr("Change AnimatedTile");
  110. private SerializedProperty m_AnimatedSprites;
  111. private AnimatedTile tile { get { return (target as AnimatedTile); } }
  112. private List<Sprite> dragAndDropSprites;
  113. private ReorderableList reorderableList;
  114. private void OnEnable()
  115. {
  116. reorderableList = new ReorderableList(tile.m_AnimatedSprites, typeof(Sprite), true, true, true, true);
  117. reorderableList.drawHeaderCallback = OnDrawHeader;
  118. reorderableList.drawElementCallback = OnDrawElement;
  119. reorderableList.elementHeightCallback = GetElementHeight;
  120. reorderableList.onAddCallback = OnAddElement;
  121. reorderableList.onRemoveCallback = OnRemoveElement;
  122. reorderableList.onReorderCallback = OnReorderElement;
  123. m_AnimatedSprites = serializedObject.FindProperty("m_AnimatedSprites");
  124. }
  125. private void OnDrawHeader(Rect rect)
  126. {
  127. GUI.Label(rect, Styles.orderAnimatedTileSpritesInfo);
  128. }
  129. private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
  130. {
  131. if (tile.m_AnimatedSprites != null && index < tile.m_AnimatedSprites.Length)
  132. {
  133. var spriteName = tile.m_AnimatedSprites[index] != null ? tile.m_AnimatedSprites[index].name : "Null";
  134. tile.m_AnimatedSprites[index] = (Sprite) EditorGUI.ObjectField(rect
  135. , $"Sprite {index + 1}: {spriteName}"
  136. , tile.m_AnimatedSprites[index]
  137. , typeof(Sprite)
  138. , false);
  139. }
  140. }
  141. private float GetElementHeight(int index)
  142. {
  143. return 3 * EditorGUI.GetPropertyHeight(SerializedPropertyType.ObjectReference,
  144. null);
  145. }
  146. private void OnAddElement(ReorderableList list)
  147. {
  148. var count = tile.m_AnimatedSprites != null ? tile.m_AnimatedSprites.Length + 1 : 1;
  149. ResizeAnimatedSpriteList(count);
  150. if (list.index == 0 || list.index < list.count)
  151. {
  152. Array.Copy(tile.m_AnimatedSprites, list.index + 1, tile.m_AnimatedSprites, list.index + 2, list.count - list.index - 1);
  153. tile.m_AnimatedSprites[list.index + 1] = null;
  154. if (list.IsSelected(list.index))
  155. list.index += 1;
  156. }
  157. else
  158. {
  159. tile.m_AnimatedSprites[count - 1] = null;
  160. }
  161. }
  162. private void OnRemoveElement(ReorderableList list)
  163. {
  164. if (tile.m_AnimatedSprites != null && tile.m_AnimatedSprites.Length > 0 && list.index < tile.m_AnimatedSprites.Length)
  165. {
  166. var sprites = tile.m_AnimatedSprites.ToList();
  167. sprites.RemoveAt(list.index);
  168. tile.m_AnimatedSprites = sprites.ToArray();
  169. }
  170. }
  171. private void OnReorderElement(ReorderableList list)
  172. {
  173. // Fix for 2020.1, which does not track changes when reordering in the list
  174. EditorUtility.SetDirty(tile);
  175. }
  176. private void DisplayClipboardText(GUIContent clipboardText, Rect position)
  177. {
  178. Color old = GUI.color;
  179. GUI.color = Color.gray;
  180. var infoSize = GUI.skin.label.CalcSize(clipboardText);
  181. Rect rect = new Rect(position.center.x - infoSize.x * .5f
  182. , position.center.y - infoSize.y * .5f
  183. , infoSize.x
  184. , infoSize.y);
  185. GUI.Label(rect, clipboardText);
  186. GUI.color = old;
  187. }
  188. private bool dragAndDropActive
  189. {
  190. get
  191. {
  192. return dragAndDropSprites != null
  193. && dragAndDropSprites.Count > 0;
  194. }
  195. }
  196. private void DragAndDropClear()
  197. {
  198. dragAndDropSprites = null;
  199. DragAndDrop.visualMode = DragAndDropVisualMode.None;
  200. Event.current.Use();
  201. }
  202. private static List<Sprite> GetSpritesFromTexture(Texture2D texture)
  203. {
  204. string path = AssetDatabase.GetAssetPath(texture);
  205. Object[] assets = AssetDatabase.LoadAllAssetsAtPath(path);
  206. List<Sprite> sprites = new List<Sprite>();
  207. foreach (Object asset in assets)
  208. {
  209. if (asset is Sprite)
  210. {
  211. sprites.Add(asset as Sprite);
  212. }
  213. }
  214. return sprites;
  215. }
  216. private static List<Sprite> GetValidSingleSprites(Object[] objects)
  217. {
  218. List<Sprite> result = new List<Sprite>();
  219. foreach (Object obj in objects)
  220. {
  221. if (obj is Sprite)
  222. {
  223. result.Add(obj as Sprite);
  224. }
  225. else if (obj is Texture2D)
  226. {
  227. Texture2D texture = obj as Texture2D;
  228. List<Sprite> sprites = GetSpritesFromTexture(texture);
  229. if (sprites.Count > 0)
  230. {
  231. result.AddRange(sprites);
  232. }
  233. }
  234. }
  235. return result;
  236. }
  237. private void HandleDragAndDrop(Rect guiRect)
  238. {
  239. if (DragAndDrop.objectReferences.Length == 0 || !guiRect.Contains(Event.current.mousePosition))
  240. return;
  241. switch (Event.current.type)
  242. {
  243. case EventType.DragUpdated:
  244. {
  245. dragAndDropSprites = GetValidSingleSprites(DragAndDrop.objectReferences);
  246. if (dragAndDropActive)
  247. {
  248. DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
  249. Event.current.Use();
  250. GUI.changed = true;
  251. }
  252. }
  253. break;
  254. case EventType.DragPerform:
  255. {
  256. if (!dragAndDropActive)
  257. return;
  258. Undo.RegisterCompleteObjectUndo(tile, "Drag and Drop to Animated Tile");
  259. ResizeAnimatedSpriteList(dragAndDropSprites.Count);
  260. Array.Copy(dragAndDropSprites.ToArray(), tile.m_AnimatedSprites, dragAndDropSprites.Count);
  261. DragAndDropClear();
  262. GUI.changed = true;
  263. EditorUtility.SetDirty(tile);
  264. GUIUtility.ExitGUI();
  265. }
  266. break;
  267. case EventType.Repaint:
  268. // Handled in Render()
  269. break;
  270. }
  271. if (Event.current.type == EventType.DragExited ||
  272. Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
  273. {
  274. DragAndDropClear();
  275. }
  276. }
  277. /// <summary>
  278. /// Draws an Inspector for the AnimatedTile.
  279. /// </summary>
  280. public override void OnInspectorGUI()
  281. {
  282. serializedObject.Update();
  283. Undo.RecordObject(tile, k_UndoName);
  284. EditorGUI.BeginChangeCheck();
  285. int count = EditorGUILayout.DelayedIntField("Number of Animated Sprites", tile.m_AnimatedSprites != null ? tile.m_AnimatedSprites.Length : 0);
  286. if (count < 0)
  287. count = 0;
  288. if (tile.m_AnimatedSprites == null || tile.m_AnimatedSprites.Length != count)
  289. ResizeAnimatedSpriteList(count);
  290. if (count == 0)
  291. {
  292. Rect rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * 5);
  293. HandleDragAndDrop(rect);
  294. EditorGUI.DrawRect(rect, dragAndDropActive && rect.Contains(Event.current.mousePosition) ? Color.white : Color.black);
  295. var innerRect = new Rect(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
  296. EditorGUI.DrawRect(innerRect, EditorGUIUtility.isProSkin
  297. ? (Color) new Color32 (56, 56, 56, 255)
  298. : (Color) new Color32 (194, 194, 194, 255));
  299. DisplayClipboardText(Styles.emptyAnimatedTileInfo, rect);
  300. GUILayout.Space(rect.height);
  301. EditorGUILayout.Space();
  302. }
  303. if (reorderableList != null)
  304. {
  305. var tileCount = tile.m_AnimatedSprites != null ? tile.m_AnimatedSprites.Length : 0;
  306. if (reorderableList.list == null || reorderableList.count != tileCount)
  307. reorderableList.list = tile.m_AnimatedSprites;
  308. reorderableList.DoLayoutList();
  309. }
  310. using (new EditorGUI.DisabledScope(tile.m_AnimatedSprites == null || tile.m_AnimatedSprites.Length == 0))
  311. {
  312. float minSpeed = EditorGUILayout.FloatField(Styles.minimumSpeedLabel, tile.m_MinSpeed);
  313. float maxSpeed = EditorGUILayout.FloatField(Styles.maximumSpeedLabel, tile.m_MaxSpeed);
  314. if (minSpeed < 0.0f)
  315. minSpeed = 0.0f;
  316. if (maxSpeed < 0.0f)
  317. maxSpeed = 0.0f;
  318. if (maxSpeed < minSpeed)
  319. maxSpeed = minSpeed;
  320. tile.m_MinSpeed = minSpeed;
  321. tile.m_MaxSpeed = maxSpeed;
  322. using (new EditorGUI.DisabledScope(tile.m_AnimatedSprites == null
  323. || (0 < tile.m_AnimationStartFrame
  324. && tile.m_AnimationStartFrame <= tile.m_AnimatedSprites.Length)))
  325. {
  326. tile.m_AnimationStartTime = EditorGUILayout.FloatField(Styles.startTimeLabel, tile.m_AnimationStartTime);
  327. }
  328. tile.m_AnimationStartFrame = EditorGUILayout.IntField(Styles.startFrameLabel, tile.m_AnimationStartFrame);
  329. tile.m_TileColliderType = (Tile.ColliderType) EditorGUILayout.EnumPopup(Styles.colliderTypeLabel, tile.m_TileColliderType);
  330. }
  331. if (EditorGUI.EndChangeCheck())
  332. {
  333. serializedObject.ApplyModifiedProperties();
  334. EditorUtility.SetDirty(tile);
  335. }
  336. }
  337. private void ResizeAnimatedSpriteList(int count)
  338. {
  339. m_AnimatedSprites.arraySize = count;
  340. serializedObject.ApplyModifiedProperties();
  341. }
  342. }
  343. #endif
  344. }