SpriteSequenceCapture.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using Assets.HeroEditor4D.Common.Scripts.CharacterScripts;
  7. using Assets.HeroEditor4D.Common.Scripts.Common;
  8. using UnityEngine;
  9. using UnityEngine.UI;
  10. namespace Assets.HeroEditor4D.Common.Scripts.EditorScripts
  11. {
  12. /// <summary>
  13. /// Used for creating sprite sheets for frame-by-frame animation.
  14. /// </summary>
  15. public class SpriteSequenceCapture : MonoBehaviour
  16. {
  17. public Character4D Character4D;
  18. public GameObject Canvas;
  19. public GameObject Camera;
  20. public string UpperBodyAnimationFolder;
  21. public string LowerBodyAnimationFolder;
  22. public string ComplexAnimationFolder;
  23. public List<string> UpperBodyClipNames;
  24. public List<string> LowerBodyClipNames;
  25. public List<string> ComplexClipNames;
  26. public Dropdown DirectionDropdown;
  27. public Dropdown PrimaryAnimationDropdown;
  28. public Dropdown SecondaryAnimationDropdown;
  29. public Slider CameraSizeSlider;
  30. public Dropdown FrameSizeDropdown;
  31. public Dropdown FrameRatioDropdown;
  32. public Dropdown ScreenshotIntervalDropdown;
  33. public Dropdown ShadowDropdown;
  34. public GameObject Shadow;
  35. public ScreenshotTransparent ScreenshotTransparent;
  36. #if UNITY_EDITOR
  37. /// <summary>
  38. /// Called only in Editor.
  39. /// </summary>
  40. public void OnValidate()
  41. {
  42. UpperBodyClipNames = Directory.GetFiles(UpperBodyAnimationFolder, "*.anim", SearchOption.AllDirectories).Select(Path.GetFileNameWithoutExtension).ToList();
  43. LowerBodyClipNames = Directory.GetFiles(LowerBodyAnimationFolder, "*.anim", SearchOption.AllDirectories).Select(Path.GetFileNameWithoutExtension).ToList();
  44. ComplexClipNames = Directory.GetFiles(ComplexAnimationFolder, "*.anim", SearchOption.AllDirectories).Select(Path.GetFileNameWithoutExtension).ToList();
  45. UpperBodyClipNames.AddRange(ComplexClipNames);
  46. for (var i = 0; i < UpperBodyClipNames.Count; i++)
  47. {
  48. if (UpperBodyClipNames[i].EndsWith("U")) UpperBodyClipNames[i] = UpperBodyClipNames[i].Substring(0, UpperBodyClipNames[i].Length - 1);
  49. }
  50. for (var i = 0; i < LowerBodyClipNames.Count; i++)
  51. {
  52. if (LowerBodyClipNames[i].EndsWith("L")) LowerBodyClipNames[i] = LowerBodyClipNames[i].Substring(0, LowerBodyClipNames[i].Length - 1);
  53. }
  54. PrimaryAnimationDropdown.options = new List<Dropdown.OptionData>();
  55. SecondaryAnimationDropdown.options = new List<Dropdown.OptionData>();
  56. foreach (var clipName in UpperBodyClipNames)
  57. {
  58. PrimaryAnimationDropdown.options.Add(new Dropdown.OptionData(clipName));
  59. }
  60. foreach (var clipName in LowerBodyClipNames)
  61. {
  62. SecondaryAnimationDropdown.options.Add(new Dropdown.OptionData(clipName));
  63. }
  64. PrimaryAnimationDropdown.value = UpperBodyClipNames.IndexOf("Slash1H");
  65. SecondaryAnimationDropdown.value = LowerBodyClipNames.IndexOf("Idle");
  66. }
  67. /// <summary>
  68. /// Called on start.
  69. /// </summary>
  70. public void Start()
  71. {
  72. if (Character4D) Character4D.SetDirection(Vector2.left);
  73. foreach (var dropdown in new[] { PrimaryAnimationDropdown, SecondaryAnimationDropdown, FrameSizeDropdown, FrameRatioDropdown, ScreenshotIntervalDropdown, ShadowDropdown })
  74. {
  75. dropdown.RefreshShownValue();
  76. }
  77. if (UpperBodyClipNames.Count == 0) OnValidate();
  78. }
  79. /// <summary>
  80. /// Called when direction dropdown changed.
  81. /// </summary>
  82. /// <param name="value"></param>
  83. public void OnDirectionChanged(int value)
  84. {
  85. switch (value)
  86. {
  87. case 0: Character4D.SetDirection(Vector2.left); break;
  88. case 1: Character4D.SetDirection(Vector2.right); break;
  89. case 2: Character4D.SetDirection(Vector2.down); break;
  90. case 3: Character4D.SetDirection(Vector2.up); break;
  91. }
  92. }
  93. /// <summary>
  94. /// Load character from prefab.
  95. /// </summary>
  96. public void Load()
  97. {
  98. var path = UnityEditor.EditorUtility.OpenFilePanel("Open character prefab", "", "prefab");
  99. if (path.Length > 0)
  100. {
  101. path = "Assets" + path.Replace(Application.dataPath, null);
  102. Load(path);
  103. }
  104. }
  105. /// <summary>
  106. /// Load character from prefab by given path.
  107. /// </summary>
  108. public void Load(string path)
  109. {
  110. var character = UnityEditor.AssetDatabase.LoadAssetAtPath<Character4D>(path);
  111. if (character == null) throw new Exception("Error loading character, please make sure you are loading correct prefab!");
  112. if (Character4D != null) Destroy(Character4D.gameObject);
  113. Character4D = Instantiate(character, transform);
  114. Character4D.transform.localPosition = Vector3.zero;
  115. Shadow = Character4D.transform.Find("Shadow").gameObject;
  116. OnDirectionChanged(DirectionDropdown.value);
  117. var mat = new Material(Shader.Find("Sprites/Default"));
  118. foreach (var spriteRenderer in Character4D.GetComponentsInChildren<SpriteRenderer>())
  119. {
  120. if (spriteRenderer.name != "Eyes")
  121. {
  122. spriteRenderer.material = mat;
  123. }
  124. }
  125. Debug.LogWarning("All materials were replaced by [Sprites/Default] to avoid outline artefacts.");
  126. }
  127. /// <summary>
  128. /// Create sprite sheet.
  129. /// </summary>
  130. public void Capture()
  131. {
  132. var frameSize = new[] { 256, 512, 1024 }[FrameSizeDropdown.value];
  133. var frameRatio = FrameRatioDropdown.value + 4;
  134. var interval = new[] { 0.1f, 0.25f, 0.5f, 1f }[ScreenshotIntervalDropdown.value];
  135. Camera.GetComponent<Camera>().orthographicSize = CameraSizeSlider.value;
  136. Shadow.SetActive(ShadowDropdown.value == 0);
  137. var upperClips = new List<string> { UpperBodyClipNames[PrimaryAnimationDropdown.value] };
  138. var lowerClips = new List<string> { LowerBodyClipNames[SecondaryAnimationDropdown.value] };
  139. StartCoroutine(CaptureFrames(upperClips, lowerClips, frameSize, frameRatio, interval));
  140. }
  141. private void ShowFrame(string upperClip, string lowerClip, float normalizedTime)
  142. {
  143. if (ComplexClipNames.Contains(upperClip))
  144. {
  145. Character4D.Animator.Play(upperClip, 2, normalizedTime);
  146. }
  147. else
  148. {
  149. Character4D.Animator.Play(upperClip, 1, normalizedTime);
  150. Character4D.Animator.Play(lowerClip, 0, normalizedTime);
  151. }
  152. Character4D.Animator.speed = 0;
  153. if (Character4D.Animator.IsInTransition(1))
  154. {
  155. Debug.Log("IsInTransition");
  156. }
  157. }
  158. private IEnumerator CaptureFrames(List<string> upperClips, List<string> lowerClips, int frameSize, int frameRatio, float interval)
  159. {
  160. Canvas.SetActive(false);
  161. var death = upperClips.Any(i => i.Contains("Death"));
  162. foreach (var upperClip in upperClips)
  163. {
  164. foreach (var lowerClip in lowerClips)
  165. {
  166. for (var i = 0; i < frameRatio; i++)
  167. {
  168. ShowFrame(upperClip, lowerClip, (float) i / (frameRatio - 1));
  169. yield return new WaitForSeconds(interval);
  170. string direction;
  171. switch (DirectionDropdown.value)
  172. {
  173. case 0: direction = "Left"; break;
  174. case 1: direction = "Right"; break;
  175. case 2: direction = "Front"; break;
  176. case 3: direction = "Back"; break;
  177. default: throw new NotImplementedException();
  178. }
  179. var path = death ? $"{Application.dataPath.Replace("/Assets", null)}/SpriteSheets/{direction}/{upperClip}/{i}.png" : $"{Application.dataPath.Replace("/Assets", null)}/SpriteSheets/{direction}/{upperClip}-{lowerClip}/{i}.png";
  180. ScreenshotTransparent.Width = ScreenshotTransparent.Height = frameSize;
  181. ScreenshotTransparent.Capture(path);
  182. }
  183. }
  184. if (death) break;
  185. }
  186. Canvas.SetActive(true);
  187. }
  188. #endif
  189. }
  190. }