388 lines
16 KiB
C#
388 lines
16 KiB
C#
using System;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
using UnityEditorInternal;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
#endif
|
|
|
|
namespace UnityEngine.Tilemaps
|
|
{
|
|
/// <summary>
|
|
/// Animated Tiles are tiles which run through and display a list of sprites in sequence.
|
|
/// </summary>
|
|
[Serializable]
|
|
[HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.tilemap.extras@latest/index.html?subfolder=/manual/AnimatedTile.html")]
|
|
public class AnimatedTile : TileBase
|
|
{
|
|
/// <summary>
|
|
/// The List of Sprites set for the Animated Tile.
|
|
/// This will be played in sequence.
|
|
/// </summary>
|
|
public Sprite[] m_AnimatedSprites;
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public float m_MinSpeed = 1f;
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public float m_MaxSpeed = 1f;
|
|
/// <summary>
|
|
/// The starting time of this Animated Tile.
|
|
/// This allows you to start the Animation from time in the list of Animated Sprites depending on the
|
|
/// Tilemap's Animation Frame Rate.
|
|
/// </summary>
|
|
public float m_AnimationStartTime;
|
|
/// <summary>
|
|
/// The starting frame of this Animated Tile.
|
|
/// This allows you to start the Animation from a particular Sprite in the list of Animated Sprites.
|
|
/// If this is set, this overrides m_AnimationStartTime.
|
|
/// </summary>
|
|
public int m_AnimationStartFrame = 0;
|
|
/// <summary>
|
|
/// The Collider Shape generated by the Tile.
|
|
/// </summary>
|
|
public Tile.ColliderType m_TileColliderType;
|
|
|
|
/// <summary>
|
|
/// Retrieves any tile rendering data from the scripted tile.
|
|
/// </summary>
|
|
/// <param name="position">Position of the Tile on the Tilemap.</param>
|
|
/// <param name="tilemap">The Tilemap the tile is present on.</param>
|
|
/// <param name="tileData">Data to render the tile.</param>
|
|
public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
|
|
{
|
|
tileData.transform = Matrix4x4.identity;
|
|
tileData.color = Color.white;
|
|
if (m_AnimatedSprites != null && m_AnimatedSprites.Length > 0)
|
|
{
|
|
tileData.sprite = m_AnimatedSprites[m_AnimatedSprites.Length - 1];
|
|
tileData.colliderType = m_TileColliderType;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves any tile animation data from the scripted tile.
|
|
/// </summary>
|
|
/// <param name="position">Position of the Tile on the Tilemap.</param>
|
|
/// <param name="tilemap">The Tilemap the tile is present on.</param>
|
|
/// <param name="tileAnimationData">Data to run an animation on the tile.</param>
|
|
/// <returns>Whether the call was successful.</returns>
|
|
public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
|
|
{
|
|
if (m_AnimatedSprites.Length > 0)
|
|
{
|
|
tileAnimationData.animatedSprites = m_AnimatedSprites;
|
|
tileAnimationData.animationSpeed = Random.Range(m_MinSpeed, m_MaxSpeed);
|
|
tileAnimationData.animationStartTime = m_AnimationStartTime;
|
|
if (0 < m_AnimationStartFrame && m_AnimationStartFrame <= m_AnimatedSprites.Length)
|
|
{
|
|
var tilemapComponent = tilemap.GetComponent<Tilemap>();
|
|
if (tilemapComponent != null && tilemapComponent.animationFrameRate > 0)
|
|
tileAnimationData.animationStartTime = (m_AnimationStartFrame - 1) / tilemapComponent.animationFrameRate;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(AnimatedTile))]
|
|
public class AnimatedTileEditor : Editor
|
|
{
|
|
private static class Styles
|
|
{
|
|
public static readonly GUIContent orderAnimatedTileSpritesInfo =
|
|
EditorGUIUtility.TrTextContent("Place sprites shown based on the order of animation.");
|
|
public static readonly GUIContent emptyAnimatedTileInfo =
|
|
EditorGUIUtility.TrTextContent(
|
|
"Drag Sprite or Sprite Texture assets \n" +
|
|
" to start creating an Animated Tile.");
|
|
public static readonly GUIContent minimumSpeedLabel = EditorGUIUtility.TrTextContent("Minimum Speed",
|
|
"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.");
|
|
public static readonly GUIContent maximumSpeedLabel = EditorGUIUtility.TrTextContent("Maximum Speed",
|
|
"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.");
|
|
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.");
|
|
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.");
|
|
public static readonly GUIContent colliderTypeLabel = EditorGUIUtility.TrTextContent("Collider Type", "The Collider Shape generated by the Tile.");
|
|
}
|
|
|
|
private static readonly string k_UndoName = L10n.Tr("Change AnimatedTile");
|
|
|
|
private SerializedProperty m_AnimatedSprites;
|
|
|
|
private AnimatedTile tile { get { return (target as AnimatedTile); } }
|
|
|
|
private List<Sprite> dragAndDropSprites;
|
|
|
|
private ReorderableList reorderableList;
|
|
|
|
private void OnEnable()
|
|
{
|
|
reorderableList = new ReorderableList(tile.m_AnimatedSprites, typeof(Sprite), true, true, true, true);
|
|
reorderableList.drawHeaderCallback = OnDrawHeader;
|
|
reorderableList.drawElementCallback = OnDrawElement;
|
|
reorderableList.elementHeightCallback = GetElementHeight;
|
|
reorderableList.onAddCallback = OnAddElement;
|
|
reorderableList.onRemoveCallback = OnRemoveElement;
|
|
reorderableList.onReorderCallback = OnReorderElement;
|
|
|
|
m_AnimatedSprites = serializedObject.FindProperty("m_AnimatedSprites");
|
|
}
|
|
|
|
private void OnDrawHeader(Rect rect)
|
|
{
|
|
GUI.Label(rect, Styles.orderAnimatedTileSpritesInfo);
|
|
}
|
|
|
|
private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
|
|
{
|
|
if (tile.m_AnimatedSprites != null && index < tile.m_AnimatedSprites.Length)
|
|
{
|
|
var spriteName = tile.m_AnimatedSprites[index] != null ? tile.m_AnimatedSprites[index].name : "Null";
|
|
tile.m_AnimatedSprites[index] = (Sprite) EditorGUI.ObjectField(rect
|
|
, $"Sprite {index + 1}: {spriteName}"
|
|
, tile.m_AnimatedSprites[index]
|
|
, typeof(Sprite)
|
|
, false);
|
|
}
|
|
}
|
|
|
|
private float GetElementHeight(int index)
|
|
{
|
|
return 3 * EditorGUI.GetPropertyHeight(SerializedPropertyType.ObjectReference,
|
|
null);
|
|
}
|
|
|
|
private void OnAddElement(ReorderableList list)
|
|
{
|
|
var count = tile.m_AnimatedSprites != null ? tile.m_AnimatedSprites.Length + 1 : 1;
|
|
ResizeAnimatedSpriteList(count);
|
|
|
|
if (list.index == 0 || list.index < list.count)
|
|
{
|
|
Array.Copy(tile.m_AnimatedSprites, list.index + 1, tile.m_AnimatedSprites, list.index + 2, list.count - list.index - 1);
|
|
tile.m_AnimatedSprites[list.index + 1] = null;
|
|
if (list.IsSelected(list.index))
|
|
list.index += 1;
|
|
}
|
|
else
|
|
{
|
|
tile.m_AnimatedSprites[count - 1] = null;
|
|
}
|
|
}
|
|
|
|
private void OnRemoveElement(ReorderableList list)
|
|
{
|
|
if (tile.m_AnimatedSprites != null && tile.m_AnimatedSprites.Length > 0 && list.index < tile.m_AnimatedSprites.Length)
|
|
{
|
|
var sprites = tile.m_AnimatedSprites.ToList();
|
|
sprites.RemoveAt(list.index);
|
|
tile.m_AnimatedSprites = sprites.ToArray();
|
|
}
|
|
}
|
|
|
|
private void OnReorderElement(ReorderableList list)
|
|
{
|
|
// Fix for 2020.1, which does not track changes when reordering in the list
|
|
EditorUtility.SetDirty(tile);
|
|
}
|
|
|
|
private void DisplayClipboardText(GUIContent clipboardText, Rect position)
|
|
{
|
|
Color old = GUI.color;
|
|
GUI.color = Color.gray;
|
|
var infoSize = GUI.skin.label.CalcSize(clipboardText);
|
|
Rect rect = new Rect(position.center.x - infoSize.x * .5f
|
|
, position.center.y - infoSize.y * .5f
|
|
, infoSize.x
|
|
, infoSize.y);
|
|
GUI.Label(rect, clipboardText);
|
|
GUI.color = old;
|
|
}
|
|
|
|
private bool dragAndDropActive
|
|
{
|
|
get
|
|
{
|
|
return dragAndDropSprites != null
|
|
&& dragAndDropSprites.Count > 0;
|
|
}
|
|
}
|
|
|
|
private void DragAndDropClear()
|
|
{
|
|
dragAndDropSprites = null;
|
|
DragAndDrop.visualMode = DragAndDropVisualMode.None;
|
|
Event.current.Use();
|
|
}
|
|
|
|
private static List<Sprite> GetSpritesFromTexture(Texture2D texture)
|
|
{
|
|
string path = AssetDatabase.GetAssetPath(texture);
|
|
Object[] assets = AssetDatabase.LoadAllAssetsAtPath(path);
|
|
List<Sprite> sprites = new List<Sprite>();
|
|
|
|
foreach (Object asset in assets)
|
|
{
|
|
if (asset is Sprite)
|
|
{
|
|
sprites.Add(asset as Sprite);
|
|
}
|
|
}
|
|
|
|
return sprites;
|
|
}
|
|
|
|
private static List<Sprite> GetValidSingleSprites(Object[] objects)
|
|
{
|
|
List<Sprite> result = new List<Sprite>();
|
|
foreach (Object obj in objects)
|
|
{
|
|
if (obj is Sprite)
|
|
{
|
|
result.Add(obj as Sprite);
|
|
}
|
|
else if (obj is Texture2D)
|
|
{
|
|
Texture2D texture = obj as Texture2D;
|
|
List<Sprite> sprites = GetSpritesFromTexture(texture);
|
|
if (sprites.Count > 0)
|
|
{
|
|
result.AddRange(sprites);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private void HandleDragAndDrop(Rect guiRect)
|
|
{
|
|
if (DragAndDrop.objectReferences.Length == 0 || !guiRect.Contains(Event.current.mousePosition))
|
|
return;
|
|
|
|
switch (Event.current.type)
|
|
{
|
|
case EventType.DragUpdated:
|
|
{
|
|
dragAndDropSprites = GetValidSingleSprites(DragAndDrop.objectReferences);
|
|
if (dragAndDropActive)
|
|
{
|
|
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
|
Event.current.Use();
|
|
GUI.changed = true;
|
|
}
|
|
}
|
|
break;
|
|
case EventType.DragPerform:
|
|
{
|
|
if (!dragAndDropActive)
|
|
return;
|
|
|
|
Undo.RegisterCompleteObjectUndo(tile, "Drag and Drop to Animated Tile");
|
|
ResizeAnimatedSpriteList(dragAndDropSprites.Count);
|
|
Array.Copy(dragAndDropSprites.ToArray(), tile.m_AnimatedSprites, dragAndDropSprites.Count);
|
|
DragAndDropClear();
|
|
GUI.changed = true;
|
|
EditorUtility.SetDirty(tile);
|
|
GUIUtility.ExitGUI();
|
|
}
|
|
break;
|
|
case EventType.Repaint:
|
|
// Handled in Render()
|
|
break;
|
|
}
|
|
|
|
if (Event.current.type == EventType.DragExited ||
|
|
Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
|
|
{
|
|
DragAndDropClear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draws an Inspector for the AnimatedTile.
|
|
/// </summary>
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
Undo.RecordObject(tile, k_UndoName);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
int count = EditorGUILayout.DelayedIntField("Number of Animated Sprites", tile.m_AnimatedSprites != null ? tile.m_AnimatedSprites.Length : 0);
|
|
if (count < 0)
|
|
count = 0;
|
|
|
|
if (tile.m_AnimatedSprites == null || tile.m_AnimatedSprites.Length != count)
|
|
ResizeAnimatedSpriteList(count);
|
|
|
|
if (count == 0)
|
|
{
|
|
Rect rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * 5);
|
|
HandleDragAndDrop(rect);
|
|
EditorGUI.DrawRect(rect, dragAndDropActive && rect.Contains(Event.current.mousePosition) ? Color.white : Color.black);
|
|
var innerRect = new Rect(rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
|
|
EditorGUI.DrawRect(innerRect, EditorGUIUtility.isProSkin
|
|
? (Color) new Color32 (56, 56, 56, 255)
|
|
: (Color) new Color32 (194, 194, 194, 255));
|
|
DisplayClipboardText(Styles.emptyAnimatedTileInfo, rect);
|
|
GUILayout.Space(rect.height);
|
|
EditorGUILayout.Space();
|
|
}
|
|
|
|
if (reorderableList != null)
|
|
{
|
|
var tileCount = tile.m_AnimatedSprites != null ? tile.m_AnimatedSprites.Length : 0;
|
|
if (reorderableList.list == null || reorderableList.count != tileCount)
|
|
reorderableList.list = tile.m_AnimatedSprites;
|
|
reorderableList.DoLayoutList();
|
|
}
|
|
|
|
using (new EditorGUI.DisabledScope(tile.m_AnimatedSprites == null || tile.m_AnimatedSprites.Length == 0))
|
|
{
|
|
float minSpeed = EditorGUILayout.FloatField(Styles.minimumSpeedLabel, tile.m_MinSpeed);
|
|
float maxSpeed = EditorGUILayout.FloatField(Styles.maximumSpeedLabel, tile.m_MaxSpeed);
|
|
if (minSpeed < 0.0f)
|
|
minSpeed = 0.0f;
|
|
|
|
if (maxSpeed < 0.0f)
|
|
maxSpeed = 0.0f;
|
|
|
|
if (maxSpeed < minSpeed)
|
|
maxSpeed = minSpeed;
|
|
|
|
tile.m_MinSpeed = minSpeed;
|
|
tile.m_MaxSpeed = maxSpeed;
|
|
|
|
using (new EditorGUI.DisabledScope(tile.m_AnimatedSprites == null
|
|
|| (0 < tile.m_AnimationStartFrame
|
|
&& tile.m_AnimationStartFrame <= tile.m_AnimatedSprites.Length)))
|
|
{
|
|
tile.m_AnimationStartTime = EditorGUILayout.FloatField(Styles.startTimeLabel, tile.m_AnimationStartTime);
|
|
}
|
|
tile.m_AnimationStartFrame = EditorGUILayout.IntField(Styles.startFrameLabel, tile.m_AnimationStartFrame);
|
|
tile.m_TileColliderType = (Tile.ColliderType) EditorGUILayout.EnumPopup(Styles.colliderTypeLabel, tile.m_TileColliderType);
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(tile);
|
|
}
|
|
}
|
|
|
|
private void ResizeAnimatedSpriteList(int count)
|
|
{
|
|
m_AnimatedSprites.arraySize = count;
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
}
|
|
#endif
|
|
}
|