SpriteSkinEditor.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. #if ENABLE_ANIMATION_COLLECTION && ENABLE_ANIMATION_BURST
  2. #define ENABLE_ANIMATION_PERFORMANCE
  3. #endif
  4. using UnityEngine;
  5. using UnityEditorInternal;
  6. using UnityEngine.U2D.Animation;
  7. using UnityEditor.IMGUI.Controls;
  8. using UnityEngine.U2D;
  9. using UnityEngine.U2D.Common;
  10. namespace UnityEditor.U2D.Animation
  11. {
  12. [CustomEditor(typeof(SpriteSkin))]
  13. [CanEditMultipleObjects]
  14. class SpriteSkinEditor : Editor
  15. {
  16. private static class Contents
  17. {
  18. public static readonly GUIContent listHeaderLabel = new GUIContent("Bones", "GameObject Transform to represent the Bones defined by the Sprite that is currently used for deformation.");
  19. public static readonly GUIContent rootBoneLabel = new GUIContent("Root Bone", "GameObject Transform to represent the Root Bone.");
  20. public static readonly string spriteNotFound = L10n.Tr("Sprite not found in SpriteRenderer");
  21. public static readonly string spriteHasNoSkinningInformation = L10n.Tr("Sprite has no Bind Poses");
  22. public static readonly string spriteHasNoWeights = L10n.Tr("Sprite has no weights");
  23. public static readonly string rootTransformNotFound = L10n.Tr("Root Bone not set");
  24. public static readonly string rootTransformNotFoundInArray = L10n.Tr("Bone list doesn't contain a reference to the Root Bone");
  25. public static readonly string invalidTransformArray = L10n.Tr("Bone list is invalid");
  26. public static readonly string transformArrayContainsNull = L10n.Tr("Bone list contains unassigned references");
  27. public static readonly string invalidTransformArrayLength = L10n.Tr("The number of Sprite's Bind Poses and the number of Transforms should match");
  28. public static readonly GUIContent useManager = new GUIContent("Enable batching", "When enabled, SpriteSkin deformation will be done in batch to improve performance.");
  29. public static readonly GUIContent alwaysUpdate = new GUIContent("Always Update", "Executes deformation of SpriteSkin even when the associated SpriteRenderer has been culled and is not visible.");
  30. public static readonly string experimental = L10n.Tr("Experimental");
  31. }
  32. private static Color s_BoundingBoxHandleColor = new Color(255, 255, 255, 150) / 255;
  33. private SerializedProperty m_RootBoneProperty;
  34. private SerializedProperty m_BoneTransformsProperty;
  35. private SerializedProperty m_AlwaysUpdateProperty;
  36. private SpriteSkin m_SpriteSkin;
  37. private ReorderableList m_ReorderableList;
  38. private Sprite m_CurrentSprite;
  39. private BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle();
  40. private bool m_NeedsRebind = false;
  41. #if ENABLE_ANIMATION_PERFORMANCE
  42. private SerializedProperty m_UseBatching;
  43. private bool m_ExperimentalFold;
  44. #endif
  45. private bool m_BoneFold = true;
  46. private void OnEnable()
  47. {
  48. m_SpriteSkin = (SpriteSkin)target;
  49. m_SpriteSkin.OnEditorEnable();
  50. m_RootBoneProperty = serializedObject.FindProperty("m_RootBone");
  51. #if ENABLE_ANIMATION_PERFORMANCE
  52. m_UseBatching = serializedObject.FindProperty("m_UseBatching");
  53. #endif
  54. m_BoneTransformsProperty = serializedObject.FindProperty("m_BoneTransforms");
  55. m_AlwaysUpdateProperty = serializedObject.FindProperty("m_AlwaysUpdate");
  56. m_CurrentSprite = m_SpriteSkin.spriteRenderer.sprite;
  57. m_BoundsHandle.axes = BoxBoundsHandle.Axes.X | BoxBoundsHandle.Axes.Y;
  58. m_BoundsHandle.SetColor(s_BoundingBoxHandleColor);
  59. SetupReorderableList();
  60. Undo.undoRedoPerformed += UndoRedoPerformed;
  61. }
  62. private void OnDestroy()
  63. {
  64. Undo.undoRedoPerformed -= UndoRedoPerformed;
  65. }
  66. private void UndoRedoPerformed()
  67. {
  68. m_CurrentSprite = m_SpriteSkin.spriteRenderer.sprite;
  69. }
  70. private void SetupReorderableList()
  71. {
  72. m_ReorderableList = new ReorderableList(serializedObject, m_BoneTransformsProperty, false, true, false, false);
  73. m_ReorderableList.headerHeight = 1.0f;
  74. m_ReorderableList.elementHeightCallback = (int index) =>
  75. {
  76. return EditorGUIUtility.singleLineHeight + 6;
  77. };
  78. m_ReorderableList.drawElementCallback = (Rect rect, int index, bool isactive, bool isfocused) =>
  79. {
  80. var content = GUIContent.none;
  81. if (m_CurrentSprite != null)
  82. {
  83. var bones = m_CurrentSprite.GetBones();
  84. if (index < bones.Length)
  85. content = new GUIContent(bones[index].name);
  86. }
  87. rect.y += 2f;
  88. rect.height = EditorGUIUtility.singleLineHeight;
  89. SerializedProperty element = m_BoneTransformsProperty.GetArrayElementAtIndex(index);
  90. EditorGUI.PropertyField(rect, element, content);
  91. };
  92. }
  93. private void InitializeBoneTransformArray()
  94. {
  95. if (m_CurrentSprite)
  96. {
  97. var elementCount = m_BoneTransformsProperty.arraySize;
  98. var bindPoses = m_CurrentSprite.GetBindPoses();
  99. if (elementCount != bindPoses.Length)
  100. {
  101. m_BoneTransformsProperty.arraySize = bindPoses.Length;
  102. for (int i = elementCount; i < m_BoneTransformsProperty.arraySize; ++i)
  103. m_BoneTransformsProperty.GetArrayElementAtIndex(i).objectReferenceValue = null;
  104. m_NeedsRebind = true;
  105. }
  106. }
  107. }
  108. public override void OnInspectorGUI()
  109. {
  110. serializedObject.Update();
  111. EditorGUILayout.PropertyField(m_AlwaysUpdateProperty, Contents.alwaysUpdate);
  112. var sprite = m_SpriteSkin.spriteRenderer.sprite;
  113. var spriteChanged = m_CurrentSprite != sprite;
  114. if (m_ReorderableList == null || spriteChanged)
  115. {
  116. m_CurrentSprite = sprite;
  117. InitializeBoneTransformArray();
  118. SetupReorderableList();
  119. }
  120. EditorGUI.BeginChangeCheck();
  121. EditorGUILayout.PropertyField(m_RootBoneProperty, Contents.rootBoneLabel);
  122. if (EditorGUI.EndChangeCheck())
  123. {
  124. m_NeedsRebind = true;
  125. }
  126. m_BoneFold = EditorGUILayout.Foldout(m_BoneFold, Contents.listHeaderLabel, true);
  127. if (m_BoneFold)
  128. {
  129. EditorGUILayout.Space();
  130. if (!serializedObject.isEditingMultipleObjects)
  131. {
  132. EditorGUI.BeginDisabledGroup(m_SpriteSkin.rootBone == null);
  133. m_ReorderableList.DoLayoutList();
  134. EditorGUI.EndDisabledGroup();
  135. }
  136. }
  137. EditorGUILayout.BeginHorizontal();
  138. GUILayout.FlexibleSpace();
  139. EditorGUI.BeginDisabledGroup(!EnableCreateBones());
  140. DoGenerateBonesButton();
  141. EditorGUI.EndDisabledGroup();
  142. EditorGUI.BeginDisabledGroup(!EnableSetBindPose());
  143. DoResetBindPoseButton();
  144. EditorGUI.EndDisabledGroup();
  145. GUILayout.FlexibleSpace();
  146. EditorGUILayout.EndHorizontal();
  147. #if ENABLE_ANIMATION_PERFORMANCE
  148. m_ExperimentalFold = EditorGUILayout.Foldout(m_ExperimentalFold, Contents.experimental, true);
  149. if (m_ExperimentalFold)
  150. {
  151. EditorGUI.indentLevel++;
  152. EditorGUI.BeginChangeCheck();
  153. EditorGUILayout.PropertyField(m_UseBatching, Contents.useManager);
  154. if (EditorGUI.EndChangeCheck())
  155. {
  156. foreach (var obj in targets)
  157. {
  158. ((SpriteSkin)obj).UseBatching(m_UseBatching.boolValue);
  159. }
  160. }
  161. EditorGUI.indentLevel--;
  162. }
  163. #endif
  164. serializedObject.ApplyModifiedProperties();
  165. if (m_NeedsRebind)
  166. Rebind();
  167. if (spriteChanged && !m_SpriteSkin.ignoreNextSpriteChange)
  168. {
  169. ResetBounds(Undo.GetCurrentGroupName());
  170. m_SpriteSkin.ignoreNextSpriteChange = false;
  171. }
  172. DoValidationWarnings();
  173. }
  174. private void Rebind()
  175. {
  176. foreach (var t in targets)
  177. {
  178. var spriteSkin = t as SpriteSkin;
  179. if(spriteSkin.spriteRenderer.sprite == null || spriteSkin.rootBone == null)
  180. continue;
  181. spriteSkin.Rebind();
  182. ResetBoundsIfNeeded(spriteSkin);
  183. }
  184. m_NeedsRebind = false;
  185. }
  186. private void ResetBounds(string undoName = "Reset Bounds")
  187. {
  188. foreach (var t in targets)
  189. {
  190. var spriteSkin = t as SpriteSkin;
  191. if (!spriteSkin.isValid)
  192. continue;
  193. Undo.RegisterCompleteObjectUndo(spriteSkin, undoName);
  194. spriteSkin.CalculateBounds();
  195. EditorUtility.SetDirty(spriteSkin);
  196. }
  197. }
  198. private void ResetBoundsIfNeeded(SpriteSkin spriteSkin)
  199. {
  200. if (spriteSkin.isValid && spriteSkin.bounds == new Bounds())
  201. spriteSkin.CalculateBounds();
  202. }
  203. private bool EnableCreateBones()
  204. {
  205. foreach (var t in targets)
  206. {
  207. var spriteSkin = t as SpriteSkin;
  208. var sprite = spriteSkin.spriteRenderer.sprite;
  209. if (sprite != null && spriteSkin.rootBone == null)
  210. return true;
  211. }
  212. return false;
  213. }
  214. private bool EnableSetBindPose()
  215. {
  216. return IsAnyTargetValid();
  217. }
  218. private bool IsAnyTargetValid()
  219. {
  220. foreach (var t in targets)
  221. {
  222. var spriteSkin = t as SpriteSkin;
  223. if (spriteSkin.isValid)
  224. return true;
  225. }
  226. return false;
  227. }
  228. private void DoGenerateBonesButton()
  229. {
  230. if (GUILayout.Button("Create Bones", GUILayout.MaxWidth(125f)))
  231. {
  232. foreach (var t in targets)
  233. {
  234. var spriteSkin = t as SpriteSkin;
  235. var sprite = spriteSkin.spriteRenderer.sprite;
  236. if (sprite == null || spriteSkin.rootBone != null)
  237. continue;
  238. Undo.RegisterCompleteObjectUndo(spriteSkin, "Create Bones");
  239. spriteSkin.CreateBoneHierarchy();
  240. foreach (var transform in spriteSkin.boneTransforms)
  241. Undo.RegisterCreatedObjectUndo(transform.gameObject, "Create Bones");
  242. ResetBoundsIfNeeded(spriteSkin);
  243. EditorUtility.SetDirty(spriteSkin);
  244. }
  245. BoneGizmo.instance.boneGizmoController.OnSelectionChanged();
  246. }
  247. }
  248. private void DoResetBindPoseButton()
  249. {
  250. if (GUILayout.Button("Reset Bind Pose", GUILayout.MaxWidth(125f)))
  251. {
  252. foreach (var t in targets)
  253. {
  254. var spriteSkin = t as SpriteSkin;
  255. if (!spriteSkin.isValid)
  256. continue;
  257. Undo.RecordObjects(spriteSkin.boneTransforms, "Reset Bind Pose");
  258. spriteSkin.ResetBindPose();
  259. }
  260. }
  261. }
  262. private void DoValidationWarnings()
  263. {
  264. EditorGUILayout.Space();
  265. bool preAppendObjectName = targets.Length > 1;
  266. foreach (var t in targets)
  267. {
  268. var spriteSkin = t as SpriteSkin;
  269. var validationResult = spriteSkin.Validate();
  270. if (validationResult == SpriteSkinValidationResult.Ready)
  271. continue;
  272. var text = "";
  273. switch (validationResult)
  274. {
  275. case SpriteSkinValidationResult.SpriteNotFound:
  276. text = Contents.spriteNotFound;
  277. break;
  278. case SpriteSkinValidationResult.SpriteHasNoSkinningInformation:
  279. text = Contents.spriteHasNoSkinningInformation;
  280. break;
  281. case SpriteSkinValidationResult.SpriteHasNoWeights:
  282. text = Contents.spriteHasNoWeights;
  283. break;
  284. case SpriteSkinValidationResult.RootTransformNotFound:
  285. text = Contents.rootTransformNotFound;
  286. break;
  287. case SpriteSkinValidationResult.RootNotFoundInTransformArray:
  288. text = Contents.rootTransformNotFoundInArray;
  289. break;
  290. case SpriteSkinValidationResult.InvalidTransformArray:
  291. text = Contents.invalidTransformArray;
  292. break;
  293. case SpriteSkinValidationResult.InvalidTransformArrayLength:
  294. text = Contents.invalidTransformArrayLength;
  295. break;
  296. case SpriteSkinValidationResult.TransformArrayContainsNull:
  297. text = Contents.transformArrayContainsNull;
  298. break;
  299. }
  300. if (preAppendObjectName)
  301. text = spriteSkin.name + ": " + text;
  302. EditorGUILayout.HelpBox(text, MessageType.Warning);
  303. }
  304. }
  305. }
  306. }