SpriteSheetCapture.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Assets.HeroEditor4D.Common.Scripts.CharacterScripts;
  5. using Assets.HeroEditor4D.Common.Scripts.Common;
  6. using Assets.HeroEditor4D.Common.Scripts.Enums;
  7. using UnityEngine;
  8. namespace Assets.HeroEditor4D.Common.Scripts.EditorScripts
  9. {
  10. [RequireComponent(typeof(Camera))]
  11. public class SpriteSheetCapture : MonoBehaviour
  12. {
  13. private Character4D _character;
  14. public void Capture(Vector2 direction, List<CaptureOption> options, int frameSize, int frameCount, bool shadow)
  15. {
  16. StartCoroutine(CaptureFrames(direction, options, frameSize, frameCount, shadow));
  17. }
  18. private IEnumerator CaptureFrames(Vector2 direction, List<CaptureOption> options, int frameSize, int frameCount, bool shadow)
  19. {
  20. _character = FindObjectOfType<Character4D>();
  21. _character.SetDirection(direction);
  22. _character.Shadows.ForEach(i => i.SetActive(false));
  23. _character.Shadows[0].SetActive(shadow);
  24. var stateHandler = _character.Animator.GetBehaviours<StateHandler>().SingleOrDefault(i => i.Name == "Death");
  25. if (stateHandler)
  26. {
  27. stateHandler.StateExit.RemoveAllListeners();
  28. }
  29. var clips = new List<List<Texture2D>>();
  30. foreach (var option in options)
  31. {
  32. _character.SetExpression("Default");
  33. _character.AnimationManager.SetState(CharacterState.Idle);
  34. _character.Animator.speed = 99;
  35. yield return null;
  36. _character.Animator.speed = 0;
  37. var frames = new List<Texture2D>();
  38. for (var j = 0; j < frameCount; j++)
  39. {
  40. var normalizedTime = (float) j / (frameCount - 1);
  41. yield return ShowFrame(option.StateL, option.StateU, option.StateC, normalizedTime);
  42. var clip = _character.Animator.GetCurrentAnimatorClipInfo(option.StateU == null ? 2 : 1)[0].clip;
  43. var expressionEvent = clip.events.Where(i => i.functionName == "SetExpression" && Mathf.Abs(i.time / clip.length - normalizedTime) <= 1f / (frameCount - 1))
  44. .OrderBy(i => Mathf.Abs(i.time / clip.length - normalizedTime)).FirstOrDefault();
  45. if (expressionEvent != null)
  46. {
  47. _character.SetExpression(expressionEvent.stringParameter);
  48. }
  49. var frame = CaptureFrame(frameSize, frameSize);
  50. frames.Add(frame);
  51. yield return null;
  52. }
  53. clips.Add(frames);
  54. }
  55. _character.AnimationManager.SetState(CharacterState.Idle);
  56. _character.Animator.speed = 1;
  57. if (stateHandler)
  58. {
  59. stateHandler.StateExit.AddListener(() => _character.SetExpression("Default"));
  60. }
  61. var texture = CreateSheet(clips, frameSize, frameSize);
  62. yield return StandaloneFilePicker.SaveFile("Save as sprite sheet", "", "Character", ".png", texture.EncodeToPNG(), (success, path) => { Debug.Log(success ? $"Saved as {path}" : "Error saving."); });
  63. }
  64. private IEnumerator ShowFrame(string stateL, string stateU, string stateC, float normalizedTime)
  65. {
  66. switch (stateU)
  67. {
  68. case "Idle" when _character.WeaponType == WeaponType.Firearm1H:
  69. stateU = "IdleFirearm1H";
  70. break;
  71. case "Idle" when _character.WeaponType == WeaponType.Firearm2H:
  72. stateU = "IdleFirearm2H";
  73. break;
  74. case "Ready" when _character.WeaponType == WeaponType.Firearm1H:
  75. stateU = "ReadyFirearm1H";
  76. break;
  77. case "Ready" when _character.WeaponType == WeaponType.Firearm2H:
  78. stateU = "ReadyFirearm2H";
  79. break;
  80. case "Ready":
  81. stateU = "Ready1H";
  82. break;
  83. }
  84. if (stateC != null)
  85. {
  86. _character.Animator.Play(stateC, 2, normalizedTime);
  87. }
  88. else
  89. {
  90. _character.Animator.Play(stateL, 0, normalizedTime);
  91. _character.Animator.Play(stateU, 1, normalizedTime);
  92. }
  93. yield return null;
  94. while (_character.Animator.GetCurrentAnimatorClipInfo(stateC == null ? 0 : 2).Length == 0)
  95. {
  96. yield return null;
  97. }
  98. if (_character.Animator.IsInTransition(1))
  99. {
  100. Debug.Log("IsInTransition");
  101. }
  102. }
  103. private Texture2D CaptureFrame(int width, int height)
  104. {
  105. var cam = GetComponent<Camera>();
  106. var renderTexture = new RenderTexture(width, height, 24);
  107. var texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false);
  108. cam.targetTexture = renderTexture;
  109. cam.Render();
  110. RenderTexture.active = renderTexture;
  111. texture2D.ReadPixels(new Rect(0, 0, width, height), 0, 0);
  112. cam.targetTexture = null;
  113. RenderTexture.active = null;
  114. Destroy(renderTexture);
  115. return texture2D;
  116. }
  117. private static Texture2D CreateSheet(List<List<Texture2D>> clips, int width, int height)
  118. {
  119. var texture = new Texture2D(clips[0].Count * width, clips.Count * height);
  120. for (var i = 0; i < clips.Count; i++)
  121. {
  122. for (var j = 0; j < clips[i].Count; j++)
  123. {
  124. texture.SetPixels(j * width, (clips.Count - 1 - i) * height, width, height, clips[i][j].GetPixels());
  125. }
  126. }
  127. texture.Apply();
  128. return texture;
  129. }
  130. }
  131. public class CaptureOption
  132. {
  133. public string StateL;
  134. public string StateU;
  135. public string StateC;
  136. public CaptureOption(string stateL)
  137. {
  138. StateL = stateL;
  139. StateU = stateL;
  140. }
  141. public CaptureOption(string stateL, string stateU, string stateC = null)
  142. {
  143. StateL = stateL;
  144. StateU = stateU;
  145. StateC = stateC;
  146. }
  147. }
  148. }